Commit 49125454 authored by Amitkumar Karwar's avatar Amitkumar Karwar Committed by John W. Linville
Browse files

libertas: Add auto deep sleep support for SD8385/SD8686/SD8688



Add timer based auto deep sleep feature in libertas driver which can be
configured using iwconfig command. This is tested on SD8688, SD8686 cards
with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
platforms. Tests have been done for USB/CS cards to make sure that the patch
won't break USB/CS code. We didn't test the if_spi driver.

Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
Acked-by: default avatarDan Williams <dcbw@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 125b181a
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
================================================================================
			README for USB8388
			README for Libertas

 (c) Copyright © 2003-2006, Marvell International Ltd.
 All Rights Reserved
@@ -226,4 +226,28 @@ setuserscan
    All entries in the scan table (not just the new scan data when keep=1)
    will be displayed upon completion by use of the getscantable ioctl.

========================
IWCONFIG COMMANDS
========================
power period

	This command is used to configure the station in deep sleep mode /
	auto deep sleep mode.

	The timer is implemented to monitor the activities (command, event,
	etc.). When an activity is detected station will exit from deep
	sleep mode automatically and restart the timer. At timer expiry
	(no activity for defined time period) the deep sleep mode is entered
	automatically.

	Note: this command is for SDIO interface only.

	Usage:
	To enable deep sleep mode do:
		iwconfig wlan0 power period 0
	To enable auto deep sleep mode with idle time period 5 seconds do:
		iwconfig wlan0 power period 5
	To disable deep sleep/auto deep sleep mode do:
		iwconfig wlan0 power period -1

==============================================================================
+69 −3
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@

static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);


/**
 *  @brief Simple callback that copies response back into command
 *
@@ -319,6 +318,60 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
	return 0;
}

static int lbs_wait_for_ds_awake(struct lbs_private *priv)
{
	int ret = 0;

	lbs_deb_enter(LBS_DEB_CMD);

	if (priv->is_deep_sleep) {
		if (!wait_event_interruptible_timeout(priv->ds_awake_q,
					!priv->is_deep_sleep, (10 * HZ))) {
			lbs_pr_err("ds_awake_q: timer expired\n");
			ret = -1;
		}
	}

	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
	return ret;
}

int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
{
	int ret =  0;

	lbs_deb_enter(LBS_DEB_CMD);

	if (deep_sleep) {
		if (priv->is_deep_sleep != 1) {
			lbs_deb_cmd("deep sleep: sleep\n");
			BUG_ON(!priv->enter_deep_sleep);
			ret = priv->enter_deep_sleep(priv);
			if (!ret) {
				netif_stop_queue(priv->dev);
				netif_carrier_off(priv->dev);
			}
		} else {
			lbs_pr_err("deep sleep: already enabled\n");
		}
	} else {
		if (priv->is_deep_sleep) {
			lbs_deb_cmd("deep sleep: wakeup\n");
			BUG_ON(!priv->exit_deep_sleep);
			ret = priv->exit_deep_sleep(priv);
			if (!ret) {
				ret = lbs_wait_for_ds_awake(priv);
				if (ret)
					lbs_pr_err("deep sleep: wakeup"
							"failed\n");
			}
		}
	}

	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
	return ret;
}

int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
			   struct assoc_request *assoc)
{
@@ -1242,8 +1295,17 @@ static void lbs_submit_command(struct lbs_private *priv,
		timeo = HZ/4;
	}

	if (command == CMD_802_11_DEEP_SLEEP) {
		if (priv->is_auto_deep_sleep_enabled) {
			priv->wakeup_dev_required = 1;
			priv->dnld_sent = 0;
		}
		priv->is_deep_sleep = 1;
		lbs_complete_command(priv, cmdnode, 0);
	} else {
		/* Setup the timer after transmit command */
		mod_timer(&priv->command_timer, jiffies + timeo);
	}

	lbs_deb_leave(LBS_DEB_HOST);
}
@@ -1505,6 +1567,10 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
	case CMD_802_11_BEACON_CTRL:
		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
		break;
	case CMD_802_11_DEEP_SLEEP:
		cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
		cmdptr->size = cpu_to_le16(S_DS_GEN);
		break;
	default:
		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
		ret = -1;
+12 −0
Original line number Diff line number Diff line
@@ -504,9 +504,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)

	case MACREG_INT_CODE_HOST_AWAKE:
		lbs_deb_cmd("EVENT: host awake\n");
		if (priv->reset_deep_sleep_wakeup)
			priv->reset_deep_sleep_wakeup(priv);
		priv->is_deep_sleep = 0;
		lbs_send_confirmwake(priv);
		break;

	case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
		if (priv->reset_deep_sleep_wakeup)
			priv->reset_deep_sleep_wakeup(priv);
		lbs_deb_cmd("EVENT: ds awake\n");
		priv->is_deep_sleep = 0;
		priv->wakeup_dev_required = 0;
		wake_up_interruptible(&priv->ds_awake_q);
		break;

	case MACREG_INT_CODE_PS_AWAKE:
		lbs_deb_cmd("EVENT: ps awake\n");
		/* handle unexpected PS AWAKE event */
+46 −0
Original line number Diff line number Diff line
@@ -117,6 +117,11 @@ static ssize_t lbs_sleepparams_write(struct file *file,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		ret = -EBUSY;
		goto out_unlock;
	}

	buf_size = min(count, len - 1);
	if (copy_from_user(buf, user_buf, buf_size)) {
		ret = -EFAULT;
@@ -157,6 +162,11 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		ret = -EBUSY;
		goto out_unlock;
	}

	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
	if (ret)
		goto out_unlock;
@@ -223,6 +233,9 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
	u8 freq;
	int events = 0;

	if (!lbs_is_cmd_allowed(priv))
		return -EBUSY;

	buf = (char *)get_zeroed_page(GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
@@ -275,6 +288,9 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
	char *buf;
	int ret;

	if (!lbs_is_cmd_allowed(priv))
		return -EBUSY;

	buf = (char *)get_zeroed_page(GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
@@ -444,6 +460,11 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		free_page(addr);
		return -EBUSY;
	}

	offval.offset = priv->mac_offset;
	offval.value = 0;

@@ -496,6 +517,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		res = -EBUSY;
		goto out_unlock;
	}

	buf_size = min(count, len - 1);
	if (copy_from_user(buf, userbuf, buf_size)) {
		res = -EFAULT;
@@ -532,6 +558,11 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		free_page(addr);
		return -EBUSY;
	}

	offval.offset = priv->bbp_offset;
	offval.value = 0;

@@ -585,6 +616,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		res = -EBUSY;
		goto out_unlock;
	}

	buf_size = min(count, len - 1);
	if (copy_from_user(buf, userbuf, buf_size)) {
		res = -EFAULT;
@@ -621,6 +657,11 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		free_page(addr);
		return -EBUSY;
	}

	offval.offset = priv->rf_offset;
	offval.value = 0;

@@ -674,6 +715,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
	if (!buf)
		return -ENOMEM;

	if (!lbs_is_cmd_allowed(priv)) {
		res = -EBUSY;
		goto out_unlock;
	}

	buf_size = min(count, len - 1);
	if (copy_from_user(buf, userbuf, buf_size)) {
		res = -EFAULT;
+4 −0
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ int lbs_execute_next_command(struct lbs_private *priv);
int lbs_process_event(struct lbs_private *priv, u32 event);
void lbs_queue_event(struct lbs_private *priv, u32 event);
void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
int lbs_is_cmd_allowed(struct lbs_private *priv);
int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
int lbs_exit_auto_deep_sleep(struct lbs_private *priv);

u32 lbs_fw_index_to_data_rate(u8 index);
u8 lbs_data_rate_to_fw_index(u32 rate);
Loading