Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

libertas: Added support for host sleep feature



Existing "ethtool -s ethX wol X" command configures hostsleep
parameters, but those are activated only during suspend/resume,
there is no way to configure host sleep without actual suspend.

This patch adds debugfs command to enable/disable host sleep based on
already configured host sleep parameters using wol command.

Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarKiran Divekar <dkiran@marvell.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 643f82e3
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -226,6 +226,18 @@ 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.

hostsleep
	This command is used to enable/disable host sleep.
	Note: Host sleep parameters should be configured using
	"ethtool -s ethX wol X" command before enabling host sleep.

	Path: /sys/kernel/debug/libertas_wireless/ethX/

	Usage:
		cat hostsleep: reads the current hostsleep state
		echo "1" > hostsleep : enable host sleep.
		echo "0" > hostsleep : disable host sleep

========================
IWCONFIG COMMANDS
========================
+60 −1
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
			struct cmd_header *resp)
{
	lbs_deb_enter(LBS_DEB_CMD);
	if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
	if (priv->is_host_sleep_activated) {
		priv->is_host_sleep_configured = 0;
		if (priv->psstate == PS_STATE_FULL_POWER) {
			priv->is_host_sleep_activated = 0;
@@ -361,6 +361,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
	return ret;
}

static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
		unsigned long dummy,
		struct cmd_header *cmd)
{
	lbs_deb_enter(LBS_DEB_FW);
	priv->is_host_sleep_activated = 1;
	wake_up_interruptible(&priv->host_sleep_q);
	lbs_deb_leave(LBS_DEB_FW);
	return 0;
}

int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
{
	struct cmd_header cmd;
	int ret = 0;
	uint32_t criteria = EHS_REMOVE_WAKEUP;

	lbs_deb_enter(LBS_DEB_CMD);

	if (host_sleep) {
		if (priv->is_host_sleep_activated != 1) {
			memset(&cmd, 0, sizeof(cmd));
			ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
					(struct wol_config *)NULL);
			if (ret) {
				lbs_pr_info("Host sleep configuration failed: "
						"%d\n", ret);
				return ret;
			}
			if (priv->psstate == PS_STATE_FULL_POWER) {
				ret = __lbs_cmd(priv,
						CMD_802_11_HOST_SLEEP_ACTIVATE,
						&cmd,
						sizeof(cmd),
						lbs_ret_host_sleep_activate, 0);
				if (ret)
					lbs_pr_info("HOST_SLEEP_ACTIVATE "
							"failed: %d\n", ret);
			}

			if (!wait_event_interruptible_timeout(
						priv->host_sleep_q,
						priv->is_host_sleep_activated,
						(10 * HZ))) {
				lbs_pr_err("host_sleep_q: timer expired\n");
				ret = -1;
			}
		} else {
			lbs_pr_err("host sleep: already enabled\n");
		}
	} else {
		if (priv->is_host_sleep_activated)
			ret = lbs_host_sleep_cfg(priv, criteria,
					(struct wol_config *)NULL);
	}

	return ret;
}

/**
 *  @brief Set an SNMP MIB value
 *
+2 −0
Original line number Diff line number Diff line
@@ -127,4 +127,6 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);

int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);

int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep);

#endif /* _LBS_CMD_H */
+66 −0
Original line number Diff line number Diff line
@@ -124,6 +124,70 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
	return ret;
}

static ssize_t lbs_host_sleep_write(struct file *file,
				const char __user *user_buf, size_t count,
				loff_t *ppos)
{
	struct lbs_private *priv = file->private_data;
	ssize_t buf_size, ret;
	int host_sleep;
	unsigned long addr = get_zeroed_page(GFP_KERNEL);
	char *buf = (char *)addr;
	if (!buf)
		return -ENOMEM;

	buf_size = min(count, len - 1);
	if (copy_from_user(buf, user_buf, buf_size)) {
		ret = -EFAULT;
		goto out_unlock;
	}
	ret = sscanf(buf, "%d", &host_sleep);
	if (ret != 1) {
		ret = -EINVAL;
		goto out_unlock;
	}

	if (host_sleep == 0)
		ret = lbs_set_host_sleep(priv, 0);
	else if (host_sleep == 1) {
		if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
			lbs_pr_info("wake parameters not configured");
			ret = -EINVAL;
			goto out_unlock;
		}
		ret = lbs_set_host_sleep(priv, 1);
	} else {
		lbs_pr_err("invalid option\n");
		ret = -EINVAL;
	}

	if (!ret)
		ret = count;

out_unlock:
	free_page(addr);
	return ret;
}

static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
				  size_t count, loff_t *ppos)
{
	struct lbs_private *priv = file->private_data;
	ssize_t ret;
	size_t pos = 0;
	unsigned long addr = get_zeroed_page(GFP_KERNEL);
	char *buf = (char *)addr;
	if (!buf)
		return -ENOMEM;

	pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);

	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);

	free_page(addr);
	return ret;
}

/*
 * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
 * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
@@ -675,6 +739,8 @@ static const struct lbs_debugfs_files debugfs_files[] = {
	{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
				lbs_sleepparams_write), },
	{ "hostsleep", 0644, FOPS(lbs_host_sleep_read,
				lbs_host_sleep_write), },
};

static const struct lbs_debugfs_files debugfs_events_files[] = {
+2 −32
Original line number Diff line number Diff line
@@ -544,20 +544,8 @@ static int lbs_thread(void *data)
	return 0;
}

static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
		unsigned long dummy,
		struct cmd_header *cmd)
{
	lbs_deb_enter(LBS_DEB_FW);
	priv->is_host_sleep_activated = 1;
	wake_up_interruptible(&priv->host_sleep_q);
	lbs_deb_leave(LBS_DEB_FW);
	return 0;
}

int lbs_suspend(struct lbs_private *priv)
{
	struct cmd_header cmd;
	int ret;

	lbs_deb_enter(LBS_DEB_FW);
@@ -571,25 +559,8 @@ int lbs_suspend(struct lbs_private *priv)
		priv->deep_sleep_required = 1;
	}

	memset(&cmd, 0, sizeof(cmd));
	ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
						(struct wol_config *)NULL);
	if (ret) {
		lbs_pr_info("Host sleep configuration failed: %d\n", ret);
		return ret;
	}
	if (priv->psstate == PS_STATE_FULL_POWER) {
		ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd,
				sizeof(cmd), lbs_ret_host_sleep_activate, 0);
		if (ret)
			lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret);
	}
	ret = lbs_set_host_sleep(priv, 1);

	if (!wait_event_interruptible_timeout(priv->host_sleep_q,
				priv->is_host_sleep_activated, (10 * HZ))) {
		lbs_pr_err("host_sleep_q: timer expired\n");
		ret = -1;
	}
	netif_device_detach(priv->dev);
	if (priv->mesh_dev)
		netif_device_detach(priv->mesh_dev);
@@ -602,11 +573,10 @@ EXPORT_SYMBOL_GPL(lbs_suspend);
int lbs_resume(struct lbs_private *priv)
{
	int ret;
	uint32_t criteria = EHS_REMOVE_WAKEUP;

	lbs_deb_enter(LBS_DEB_FW);

	ret = lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL);
	ret = lbs_set_host_sleep(priv, 0);

	netif_device_attach(priv->dev);
	if (priv->mesh_dev)