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: Amitkumar Karwar <akarwar@marvell.com> Signed-off-by: Kiran Divekar <dkiran@marvell.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
643f82e32f
commit
1311843c58
5 changed files with 142 additions and 33 deletions
|
@ -226,6 +226,18 @@ setuserscan
|
||||||
All entries in the scan table (not just the new scan data when keep=1)
|
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.
|
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
|
IWCONFIG COMMANDS
|
||||||
========================
|
========================
|
||||||
|
|
|
@ -181,7 +181,7 @@ static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
|
||||||
struct cmd_header *resp)
|
struct cmd_header *resp)
|
||||||
{
|
{
|
||||||
lbs_deb_enter(LBS_DEB_CMD);
|
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;
|
priv->is_host_sleep_configured = 0;
|
||||||
if (priv->psstate == PS_STATE_FULL_POWER) {
|
if (priv->psstate == PS_STATE_FULL_POWER) {
|
||||||
priv->is_host_sleep_activated = 0;
|
priv->is_host_sleep_activated = 0;
|
||||||
|
@ -361,6 +361,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
|
||||||
return ret;
|
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
|
* @brief Set an SNMP MIB value
|
||||||
*
|
*
|
||||||
|
|
|
@ -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_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 */
|
#endif /* _LBS_CMD_H */
|
||||||
|
|
|
@ -124,6 +124,70 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
|
||||||
return ret;
|
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
|
* 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
|
* 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), },
|
{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
|
||||||
{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
|
{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
|
||||||
lbs_sleepparams_write), },
|
lbs_sleepparams_write), },
|
||||||
|
{ "hostsleep", 0644, FOPS(lbs_host_sleep_read,
|
||||||
|
lbs_host_sleep_write), },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct lbs_debugfs_files debugfs_events_files[] = {
|
static const struct lbs_debugfs_files debugfs_events_files[] = {
|
||||||
|
|
|
@ -544,20 +544,8 @@ static int lbs_thread(void *data)
|
||||||
return 0;
|
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)
|
int lbs_suspend(struct lbs_private *priv)
|
||||||
{
|
{
|
||||||
struct cmd_header cmd;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
lbs_deb_enter(LBS_DEB_FW);
|
lbs_deb_enter(LBS_DEB_FW);
|
||||||
|
@ -571,25 +559,8 @@ int lbs_suspend(struct lbs_private *priv)
|
||||||
priv->deep_sleep_required = 1;
|
priv->deep_sleep_required = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&cmd, 0, sizeof(cmd));
|
ret = lbs_set_host_sleep(priv, 1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
netif_device_detach(priv->dev);
|
netif_device_detach(priv->dev);
|
||||||
if (priv->mesh_dev)
|
if (priv->mesh_dev)
|
||||||
netif_device_detach(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 lbs_resume(struct lbs_private *priv)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
uint32_t criteria = EHS_REMOVE_WAKEUP;
|
|
||||||
|
|
||||||
lbs_deb_enter(LBS_DEB_FW);
|
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);
|
netif_device_attach(priv->dev);
|
||||||
if (priv->mesh_dev)
|
if (priv->mesh_dev)
|
||||||
|
|
Loading…
Reference in a new issue