libertas: Added support for host sleep feature
authorAmitkumar Karwar <akarwar@marvell.com>
Thu, 8 Jul 2010 01:13:48 +0000 (06:43 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 12 Jul 2010 20:05:31 +0000 (16:05 -0400)
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>
drivers/net/wireless/libertas/README
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmd.h
drivers/net/wireless/libertas/debugfs.c
drivers/net/wireless/libertas/main.c

index 2726c044430fb74bedc3fbd87937dc441f277bb8..60fd1afe89ac2ff7eee0c0c7df1f9d15c13ecec3 100644 (file)
@@ -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
 ========================
index 6c8a9d952a01b6f5ac1a5c531dde1c64f2b162ee..749fbde4fd54fcb270097fc3c86686d1bc619c71 100644 (file)
@@ -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
  *
index cb4138a55fdf9057fe13a0b8e1ced435cf0f0572..386e565d99ad3eb7008d555fbd9eeb7733c33ceb 100644 (file)
@@ -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 */
index 17367463c8554451c757e78d92d692c2d67a0ec7..acaf811646244e39c36d4ca6f1bd13bbee0a30f4 100644 (file)
@@ -124,6 +124,70 @@ out_unlock:
        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[] = {
index b519fc70f04fe5c80a533c7e231c46236f69e105..2a0b590a93f1098e79e0c9ba7219807045dbe27a 100644 (file)
@@ -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)