aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wl12xx
diff options
context:
space:
mode:
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>2010-07-08 10:50:00 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-07-08 16:35:51 -0400
commit90494a90bea010af47547880634e0f1c52824a7d (patch)
treee7464aa406385a0a9908324e038736aa9cac6a4d /drivers/net/wireless/wl12xx
parent849923f43ca681cc86a401178db31acb60e79f3b (diff)
wl1271: Work around AP's with broken ps-poll functionality
Some AP's (such as Zyxel Prestige 600) have totally broken ps-poll functionality. When powersave is enabled, these AP's will set the TIM bit for a STA in beacons, but when the STA responds with a ps-poll, the AP does not respond with data. The wl1271 firmware is able to send an indication to the host, when this problem occurs. This patch adds implementation, which temporarily disables power-save in response to this indication, allowing the AP to transmit whatever data it has buffered for the STA / whatever data is inbound at that time. This patch does not make these AP's work reliably in PSM, but improves the chances of inbound data getting through. The side effect of this patch is increased power consumption when using a faulty AP. Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h5
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.c3
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_conf.h7
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.c60
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c14
6 files changed, 88 insertions, 2 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 1b52ce6a84d7..cfdccdb86067 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -351,6 +351,7 @@ struct wl1271 {
351#define WL1271_FLAG_IRQ_RUNNING (10) 351#define WL1271_FLAG_IRQ_RUNNING (10)
352#define WL1271_FLAG_IDLE (11) 352#define WL1271_FLAG_IDLE (11)
353#define WL1271_FLAG_IDLE_REQUESTED (12) 353#define WL1271_FLAG_IDLE_REQUESTED (12)
354#define WL1271_FLAG_PSPOLL_FAILURE (13)
354 unsigned long flags; 355 unsigned long flags;
355 356
356 struct wl1271_partition_set part; 357 struct wl1271_partition_set part;
@@ -445,6 +446,10 @@ struct wl1271 {
445 446
446 struct completion *elp_compl; 447 struct completion *elp_compl;
447 struct delayed_work elp_work; 448 struct delayed_work elp_work;
449 struct delayed_work pspoll_work;
450
451 /* counter for ps-poll delivery failures */
452 int ps_poll_failures;
448 453
449 /* retry counter for PSM entries */ 454 /* retry counter for PSM entries */
450 u8 psm_entry_retry; 455 u8 psm_entry_retry;
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 1a36d8a2196e..f44ccaff4e4b 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -414,7 +414,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
414 PS_REPORT_EVENT_ID | 414 PS_REPORT_EVENT_ID |
415 JOIN_EVENT_COMPLETE_ID | 415 JOIN_EVENT_COMPLETE_ID |
416 DISCONNECT_EVENT_COMPLETE_ID | 416 DISCONNECT_EVENT_COMPLETE_ID |
417 RSSI_SNR_TRIGGER_0_EVENT_ID; 417 RSSI_SNR_TRIGGER_0_EVENT_ID |
418 PSPOLL_DELIVERY_FAILURE_EVENT_ID;
418 419
419 ret = wl1271_event_unmask(wl); 420 ret = wl1271_event_unmask(wl);
420 if (ret < 0) { 421 if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
index d046d044b5bd..84b0de7f4bc6 100644
--- a/drivers/net/wireless/wl12xx/wl1271_conf.h
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -874,6 +874,13 @@ struct conf_conn_settings {
874 u8 ps_poll_threshold; 874 u8 ps_poll_threshold;
875 875
876 /* 876 /*
877 * PS Poll failure recovery ACTIVE period length
878 *
879 * Range: u32 (ms)
880 */
881 u32 ps_poll_recovery_period;
882
883 /*
877 * Configuration of signal average weights. 884 * Configuration of signal average weights.
878 */ 885 */
879 struct conf_sig_weights sig_weights; 886 struct conf_sig_weights sig_weights;
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index ca52cdec7a8f..15f6b86f81a2 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -28,6 +28,63 @@
28#include "wl1271_ps.h" 28#include "wl1271_ps.h"
29#include "wl12xx_80211.h" 29#include "wl12xx_80211.h"
30 30
31void wl1271_pspoll_work(struct work_struct *work)
32{
33 struct delayed_work *dwork;
34 struct wl1271 *wl;
35
36 dwork = container_of(work, struct delayed_work, work);
37 wl = container_of(dwork, struct wl1271, pspoll_work);
38
39 wl1271_debug(DEBUG_EVENT, "pspoll work");
40
41 mutex_lock(&wl->mutex);
42
43 if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags))
44 goto out;
45
46 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
47 goto out;
48
49 /*
50 * if we end up here, then we were in powersave when the pspoll
51 * delivery failure occurred, and no-one changed state since, so
52 * we should go back to powersave.
53 */
54 wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, true);
55
56out:
57 mutex_unlock(&wl->mutex);
58};
59
60static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
61{
62 int delay = wl->conf.conn.ps_poll_recovery_period;
63 int ret;
64
65 wl->ps_poll_failures++;
66 if (wl->ps_poll_failures == 1)
67 wl1271_info("AP with dysfunctional ps-poll, "
68 "trying to work around it.");
69
70 /* force active mode receive data from the AP */
71 if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
72 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, true);
73 if (ret < 0)
74 return;
75 set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
76 ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work,
77 msecs_to_jiffies(delay));
78 }
79
80 /*
81 * If already in active mode, lets we should be getting data from
82 * the AP right away. If we enter PSM too fast after this, and data
83 * remains on the AP, we will get another event like this, and we'll
84 * go into active once more.
85 */
86}
87
31static int wl1271_event_scan_complete(struct wl1271 *wl, 88static int wl1271_event_scan_complete(struct wl1271 *wl,
32 struct event_mailbox *mbox) 89 struct event_mailbox *mbox)
33{ 90{
@@ -191,6 +248,9 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
191 return ret; 248 return ret;
192 } 249 }
193 250
251 if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
252 wl1271_event_pspoll_delivery_fail(wl);
253
194 if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { 254 if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
195 wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); 255 wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
196 if (wl->vif) 256 if (wl->vif)
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h
index 58371008f270..9fb5a940b1a5 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.h
+++ b/drivers/net/wireless/wl12xx/wl1271_event.h
@@ -121,5 +121,6 @@ struct event_mailbox {
121int wl1271_event_unmask(struct wl1271 *wl); 121int wl1271_event_unmask(struct wl1271 *wl);
122void wl1271_event_mbox_config(struct wl1271 *wl); 122void wl1271_event_mbox_config(struct wl1271 *wl);
123int wl1271_event_handle(struct wl1271 *wl, u8 mbox); 123int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
124void wl1271_pspoll_work(struct work_struct *work);
124 125
125#endif 126#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index a37244c88fee..8a4b17f74dbc 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -233,7 +233,8 @@ static struct conf_drv_settings default_conf = {
233 .beacon_rx_timeout = 10000, 233 .beacon_rx_timeout = 10000,
234 .broadcast_timeout = 20000, 234 .broadcast_timeout = 20000,
235 .rx_broadcast_in_ps = 1, 235 .rx_broadcast_in_ps = 1,
236 .ps_poll_threshold = 20, 236 .ps_poll_threshold = 10,
237 .ps_poll_recovery_period = 700,
237 .bet_enable = CONF_BET_MODE_ENABLE, 238 .bet_enable = CONF_BET_MODE_ENABLE,
238 .bet_max_consecutive = 10, 239 .bet_max_consecutive = 10,
239 .psm_entry_retries = 3, 240 .psm_entry_retries = 3,
@@ -955,6 +956,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
955 956
956 cancel_work_sync(&wl->irq_work); 957 cancel_work_sync(&wl->irq_work);
957 cancel_work_sync(&wl->tx_work); 958 cancel_work_sync(&wl->tx_work);
959 cancel_delayed_work_sync(&wl->pspoll_work);
958 960
959 mutex_lock(&wl->mutex); 961 mutex_lock(&wl->mutex);
960 962
@@ -1260,6 +1262,13 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1260 wl1271_warning("idle mode change failed %d", ret); 1262 wl1271_warning("idle mode change failed %d", ret);
1261 } 1263 }
1262 1264
1265 /*
1266 * if mac80211 changes the PSM mode, make sure the mode is not
1267 * incorrectly changed after the pspoll failure active window.
1268 */
1269 if (changed & IEEE80211_CONF_CHANGE_PS)
1270 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1271
1263 if (conf->flags & IEEE80211_CONF_PS && 1272 if (conf->flags & IEEE80211_CONF_PS &&
1264 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { 1273 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1265 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); 1274 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
@@ -1766,6 +1775,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1766 wl->aid = bss_conf->aid; 1775 wl->aid = bss_conf->aid;
1767 set_assoc = true; 1776 set_assoc = true;
1768 1777
1778 wl->ps_poll_failures = 0;
1779
1769 /* 1780 /*
1770 * use basic rates from AP, and determine lowest rate 1781 * use basic rates from AP, and determine lowest rate
1771 * to use with control frames. 1782 * to use with control frames.
@@ -2390,6 +2401,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
2390 skb_queue_head_init(&wl->tx_queue); 2401 skb_queue_head_init(&wl->tx_queue);
2391 2402
2392 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); 2403 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
2404 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
2393 wl->channel = WL1271_DEFAULT_CHANNEL; 2405 wl->channel = WL1271_DEFAULT_CHANNEL;
2394 wl->beacon_int = WL1271_DEFAULT_BEACON_INT; 2406 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
2395 wl->default_key = 0; 2407 wl->default_key = 0;