diff options
author | Amitkumar Karwar <akarwar@marvell.com> | 2010-07-07 21:13:48 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-07-12 16:05:31 -0400 |
commit | 1311843c58ca606bab8bfe4cf6c0fe50deb9986d (patch) | |
tree | f716fbf88c163e33ef622ecb5e9c1d37df006bc7 | |
parent | 643f82e32f14faf0d0944c804203a6681b6b0a1e (diff) |
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>
-rw-r--r-- | drivers/net/wireless/libertas/README | 12 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 61 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/debugfs.c | 66 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 34 |
5 files changed, 142 insertions, 33 deletions
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README index 2726c044430f..60fd1afe89ac 100644 --- a/drivers/net/wireless/libertas/README +++ b/drivers/net/wireless/libertas/README | |||
@@ -226,6 +226,18 @@ setuserscan | |||
226 | All entries in the scan table (not just the new scan data when keep=1) | 226 | All entries in the scan table (not just the new scan data when keep=1) |
227 | will be displayed upon completion by use of the getscantable ioctl. | 227 | will be displayed upon completion by use of the getscantable ioctl. |
228 | 228 | ||
229 | hostsleep | ||
230 | This command is used to enable/disable host sleep. | ||
231 | Note: Host sleep parameters should be configured using | ||
232 | "ethtool -s ethX wol X" command before enabling host sleep. | ||
233 | |||
234 | Path: /sys/kernel/debug/libertas_wireless/ethX/ | ||
235 | |||
236 | Usage: | ||
237 | cat hostsleep: reads the current hostsleep state | ||
238 | echo "1" > hostsleep : enable host sleep. | ||
239 | echo "0" > hostsleep : disable host sleep | ||
240 | |||
229 | ======================== | 241 | ======================== |
230 | IWCONFIG COMMANDS | 242 | IWCONFIG COMMANDS |
231 | ======================== | 243 | ======================== |
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 6c8a9d952a01..749fbde4fd54 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c | |||
@@ -181,7 +181,7 @@ static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, | |||
181 | struct cmd_header *resp) | 181 | struct cmd_header *resp) |
182 | { | 182 | { |
183 | lbs_deb_enter(LBS_DEB_CMD); | 183 | lbs_deb_enter(LBS_DEB_CMD); |
184 | if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { | 184 | if (priv->is_host_sleep_activated) { |
185 | priv->is_host_sleep_configured = 0; | 185 | priv->is_host_sleep_configured = 0; |
186 | if (priv->psstate == PS_STATE_FULL_POWER) { | 186 | if (priv->psstate == PS_STATE_FULL_POWER) { |
187 | priv->is_host_sleep_activated = 0; | 187 | priv->is_host_sleep_activated = 0; |
@@ -361,6 +361,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) | |||
361 | return ret; | 361 | return ret; |
362 | } | 362 | } |
363 | 363 | ||
364 | static int lbs_ret_host_sleep_activate(struct lbs_private *priv, | ||
365 | unsigned long dummy, | ||
366 | struct cmd_header *cmd) | ||
367 | { | ||
368 | lbs_deb_enter(LBS_DEB_FW); | ||
369 | priv->is_host_sleep_activated = 1; | ||
370 | wake_up_interruptible(&priv->host_sleep_q); | ||
371 | lbs_deb_leave(LBS_DEB_FW); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) | ||
376 | { | ||
377 | struct cmd_header cmd; | ||
378 | int ret = 0; | ||
379 | uint32_t criteria = EHS_REMOVE_WAKEUP; | ||
380 | |||
381 | lbs_deb_enter(LBS_DEB_CMD); | ||
382 | |||
383 | if (host_sleep) { | ||
384 | if (priv->is_host_sleep_activated != 1) { | ||
385 | memset(&cmd, 0, sizeof(cmd)); | ||
386 | ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, | ||
387 | (struct wol_config *)NULL); | ||
388 | if (ret) { | ||
389 | lbs_pr_info("Host sleep configuration failed: " | ||
390 | "%d\n", ret); | ||
391 | return ret; | ||
392 | } | ||
393 | if (priv->psstate == PS_STATE_FULL_POWER) { | ||
394 | ret = __lbs_cmd(priv, | ||
395 | CMD_802_11_HOST_SLEEP_ACTIVATE, | ||
396 | &cmd, | ||
397 | sizeof(cmd), | ||
398 | lbs_ret_host_sleep_activate, 0); | ||
399 | if (ret) | ||
400 | lbs_pr_info("HOST_SLEEP_ACTIVATE " | ||
401 | "failed: %d\n", ret); | ||
402 | } | ||
403 | |||
404 | if (!wait_event_interruptible_timeout( | ||
405 | priv->host_sleep_q, | ||
406 | priv->is_host_sleep_activated, | ||
407 | (10 * HZ))) { | ||
408 | lbs_pr_err("host_sleep_q: timer expired\n"); | ||
409 | ret = -1; | ||
410 | } | ||
411 | } else { | ||
412 | lbs_pr_err("host sleep: already enabled\n"); | ||
413 | } | ||
414 | } else { | ||
415 | if (priv->is_host_sleep_activated) | ||
416 | ret = lbs_host_sleep_cfg(priv, criteria, | ||
417 | (struct wol_config *)NULL); | ||
418 | } | ||
419 | |||
420 | return ret; | ||
421 | } | ||
422 | |||
364 | /** | 423 | /** |
365 | * @brief Set an SNMP MIB value | 424 | * @brief Set an SNMP MIB value |
366 | * | 425 | * |
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index cb4138a55fdf..386e565d99ad 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h | |||
@@ -127,4 +127,6 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); | |||
127 | 127 | ||
128 | int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); | 128 | int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); |
129 | 129 | ||
130 | int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); | ||
131 | |||
130 | #endif /* _LBS_CMD_H */ | 132 | #endif /* _LBS_CMD_H */ |
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 17367463c855..acaf81164624 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c | |||
@@ -124,6 +124,70 @@ out_unlock: | |||
124 | return ret; | 124 | return ret; |
125 | } | 125 | } |
126 | 126 | ||
127 | static ssize_t lbs_host_sleep_write(struct file *file, | ||
128 | const char __user *user_buf, size_t count, | ||
129 | loff_t *ppos) | ||
130 | { | ||
131 | struct lbs_private *priv = file->private_data; | ||
132 | ssize_t buf_size, ret; | ||
133 | int host_sleep; | ||
134 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
135 | char *buf = (char *)addr; | ||
136 | if (!buf) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | buf_size = min(count, len - 1); | ||
140 | if (copy_from_user(buf, user_buf, buf_size)) { | ||
141 | ret = -EFAULT; | ||
142 | goto out_unlock; | ||
143 | } | ||
144 | ret = sscanf(buf, "%d", &host_sleep); | ||
145 | if (ret != 1) { | ||
146 | ret = -EINVAL; | ||
147 | goto out_unlock; | ||
148 | } | ||
149 | |||
150 | if (host_sleep == 0) | ||
151 | ret = lbs_set_host_sleep(priv, 0); | ||
152 | else if (host_sleep == 1) { | ||
153 | if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { | ||
154 | lbs_pr_info("wake parameters not configured"); | ||
155 | ret = -EINVAL; | ||
156 | goto out_unlock; | ||
157 | } | ||
158 | ret = lbs_set_host_sleep(priv, 1); | ||
159 | } else { | ||
160 | lbs_pr_err("invalid option\n"); | ||
161 | ret = -EINVAL; | ||
162 | } | ||
163 | |||
164 | if (!ret) | ||
165 | ret = count; | ||
166 | |||
167 | out_unlock: | ||
168 | free_page(addr); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, | ||
173 | size_t count, loff_t *ppos) | ||
174 | { | ||
175 | struct lbs_private *priv = file->private_data; | ||
176 | ssize_t ret; | ||
177 | size_t pos = 0; | ||
178 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
179 | char *buf = (char *)addr; | ||
180 | if (!buf) | ||
181 | return -ENOMEM; | ||
182 | |||
183 | pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); | ||
184 | |||
185 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
186 | |||
187 | free_page(addr); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
127 | /* | 191 | /* |
128 | * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might | 192 | * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might |
129 | * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the | 193 | * 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[] = { | |||
675 | { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, | 739 | { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, |
676 | { "sleepparams", 0644, FOPS(lbs_sleepparams_read, | 740 | { "sleepparams", 0644, FOPS(lbs_sleepparams_read, |
677 | lbs_sleepparams_write), }, | 741 | lbs_sleepparams_write), }, |
742 | { "hostsleep", 0644, FOPS(lbs_host_sleep_read, | ||
743 | lbs_host_sleep_write), }, | ||
678 | }; | 744 | }; |
679 | 745 | ||
680 | static const struct lbs_debugfs_files debugfs_events_files[] = { | 746 | static const struct lbs_debugfs_files debugfs_events_files[] = { |
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index b519fc70f04f..2a0b590a93f1 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -544,20 +544,8 @@ static int lbs_thread(void *data) | |||
544 | return 0; | 544 | return 0; |
545 | } | 545 | } |
546 | 546 | ||
547 | static int lbs_ret_host_sleep_activate(struct lbs_private *priv, | ||
548 | unsigned long dummy, | ||
549 | struct cmd_header *cmd) | ||
550 | { | ||
551 | lbs_deb_enter(LBS_DEB_FW); | ||
552 | priv->is_host_sleep_activated = 1; | ||
553 | wake_up_interruptible(&priv->host_sleep_q); | ||
554 | lbs_deb_leave(LBS_DEB_FW); | ||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | int lbs_suspend(struct lbs_private *priv) | 547 | int lbs_suspend(struct lbs_private *priv) |
559 | { | 548 | { |
560 | struct cmd_header cmd; | ||
561 | int ret; | 549 | int ret; |
562 | 550 | ||
563 | lbs_deb_enter(LBS_DEB_FW); | 551 | lbs_deb_enter(LBS_DEB_FW); |
@@ -571,25 +559,8 @@ int lbs_suspend(struct lbs_private *priv) | |||
571 | priv->deep_sleep_required = 1; | 559 | priv->deep_sleep_required = 1; |
572 | } | 560 | } |
573 | 561 | ||
574 | memset(&cmd, 0, sizeof(cmd)); | 562 | ret = lbs_set_host_sleep(priv, 1); |
575 | ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, | ||
576 | (struct wol_config *)NULL); | ||
577 | if (ret) { | ||
578 | lbs_pr_info("Host sleep configuration failed: %d\n", ret); | ||
579 | return ret; | ||
580 | } | ||
581 | if (priv->psstate == PS_STATE_FULL_POWER) { | ||
582 | ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd, | ||
583 | sizeof(cmd), lbs_ret_host_sleep_activate, 0); | ||
584 | if (ret) | ||
585 | lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret); | ||
586 | } | ||
587 | 563 | ||
588 | if (!wait_event_interruptible_timeout(priv->host_sleep_q, | ||
589 | priv->is_host_sleep_activated, (10 * HZ))) { | ||
590 | lbs_pr_err("host_sleep_q: timer expired\n"); | ||
591 | ret = -1; | ||
592 | } | ||
593 | netif_device_detach(priv->dev); | 564 | netif_device_detach(priv->dev); |
594 | if (priv->mesh_dev) | 565 | if (priv->mesh_dev) |
595 | netif_device_detach(priv->mesh_dev); | 566 | netif_device_detach(priv->mesh_dev); |
@@ -602,11 +573,10 @@ EXPORT_SYMBOL_GPL(lbs_suspend); | |||
602 | int lbs_resume(struct lbs_private *priv) | 573 | int lbs_resume(struct lbs_private *priv) |
603 | { | 574 | { |
604 | int ret; | 575 | int ret; |
605 | uint32_t criteria = EHS_REMOVE_WAKEUP; | ||
606 | 576 | ||
607 | lbs_deb_enter(LBS_DEB_FW); | 577 | lbs_deb_enter(LBS_DEB_FW); |
608 | 578 | ||
609 | ret = lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL); | 579 | ret = lbs_set_host_sleep(priv, 0); |
610 | 580 | ||
611 | netif_device_attach(priv->dev); | 581 | netif_device_attach(priv->dev); |
612 | if (priv->mesh_dev) | 582 | if (priv->mesh_dev) |