aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/p54
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@web.de>2009-07-07 13:08:07 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-07-24 15:05:06 -0400
commite0f114e82e3781087a0ad0e92c94ff0d55012c1a (patch)
treed4c7cfa485360952b3da9fee7acc07efcfdcc7d8 /drivers/net/wireless/p54
parent0a2b8bb24d4eb67788edd71d1ef8aa86c2e17e0f (diff)
p54: re-enable power save feature
This patch re-enables p54's power save features and adds a workaround which temporarily alters the device's power state in order to allow ps-polls to be sent and buffered data to be retrieved during psm. (Incorporates patch originally posted as "p54: fix beacon template dtim IE corruption". -- JWL) Signed-off-by: Christian Lamparter <chunkeey@web.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/p54')
-rw-r--r--drivers/net/wireless/p54/fwio.c7
-rw-r--r--drivers/net/wireless/p54/lmac.h3
-rw-r--r--drivers/net/wireless/p54/main.c77
-rw-r--r--drivers/net/wireless/p54/p54.h1
-rw-r--r--drivers/net/wireless/p54/txrx.c42
5 files changed, 98 insertions, 32 deletions
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c
index dc4f3f5ee0c8..349375f4a14b 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/p54/fwio.c
@@ -585,7 +585,8 @@ int p54_set_ps(struct p54_common *priv)
585 unsigned int i; 585 unsigned int i;
586 u16 mode; 586 u16 mode;
587 587
588 if (priv->hw->conf.flags & IEEE80211_CONF_PS) 588 if (priv->hw->conf.flags & IEEE80211_CONF_PS &&
589 !priv->powersave_override)
589 mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | 590 mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |
590 P54_PSM_CHECKSUM | P54_PSM_MCBC; 591 P54_PSM_CHECKSUM | P54_PSM_MCBC;
591 else 592 else
@@ -607,8 +608,8 @@ int p54_set_ps(struct p54_common *priv)
607 608
608 psm->beacon_rssi_skip_max = 200; 609 psm->beacon_rssi_skip_max = 200;
609 psm->rssi_delta_threshold = 0; 610 psm->rssi_delta_threshold = 0;
610 psm->nr = 10; 611 psm->nr = 1;
611 psm->exclude[0] = 0; 612 psm->exclude[0] = WLAN_EID_TIM;
612 613
613 p54_tx(priv, skb); 614 p54_tx(priv, skb);
614 return 0; 615 return 0;
diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h
index 0496cff26b35..af35cfcd4fe3 100644
--- a/drivers/net/wireless/p54/lmac.h
+++ b/drivers/net/wireless/p54/lmac.h
@@ -548,4 +548,7 @@ int p54_upload_key(struct p54_common *priv, u8 algo, int slot,
548int p54_download_eeprom(struct p54_common *priv, void *buf, 548int p54_download_eeprom(struct p54_common *priv, void *buf,
549 u16 offset, u16 len); 549 u16 offset, u16 len);
550 550
551/* utility */
552u8 *p54_find_ie(struct sk_buff *skb, u8 ie);
553
551#endif /* LMAC_H */ 554#endif /* LMAC_H */
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index f9b4f6a238ea..c9a054548d95 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -65,51 +65,64 @@ static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
65 return p54_update_beacon_tim(priv, sta->aid, set); 65 return p54_update_beacon_tim(priv, sta->aid, set);
66} 66}
67 67
68static int p54_beacon_format_ie_tim(struct sk_buff *skb) 68u8 *p54_find_ie(struct sk_buff *skb, u8 ie)
69{ 69{
70 /*
71 * the good excuse for this mess is ... the firmware.
72 * The dummy TIM MUST be at the end of the beacon frame,
73 * because it'll be overwritten!
74 */
75
76 struct ieee80211_mgmt *mgmt = (void *)skb->data; 70 struct ieee80211_mgmt *mgmt = (void *)skb->data;
77 u8 *pos, *end; 71 u8 *pos, *end;
78 72
79 if (skb->len <= sizeof(mgmt)) 73 if (skb->len <= sizeof(mgmt))
80 return -EINVAL; 74 return NULL;
81 75
82 pos = (u8 *)mgmt->u.beacon.variable; 76 pos = (u8 *)mgmt->u.beacon.variable;
83 end = skb->data + skb->len; 77 end = skb->data + skb->len;
84 while (pos < end) { 78 while (pos < end) {
85 if (pos + 2 + pos[1] > end) 79 if (pos + 2 + pos[1] > end)
86 return -EINVAL; 80 return NULL;
87 81
88 if (pos[0] == WLAN_EID_TIM) { 82 if (pos[0] == ie)
89 u8 dtim_len = pos[1]; 83 return pos;
90 u8 dtim_period = pos[3];
91 u8 *next = pos + 2 + dtim_len;
92 84
93 if (dtim_len < 3) 85 pos += 2 + pos[1];
94 return -EINVAL; 86 }
87 return NULL;
88}
95 89
96 memmove(pos, next, end - next); 90static int p54_beacon_format_ie_tim(struct sk_buff *skb)
91{
92 /*
93 * the good excuse for this mess is ... the firmware.
94 * The dummy TIM MUST be at the end of the beacon frame,
95 * because it'll be overwritten!
96 */
97 u8 *tim;
98 u8 dtim_len;
99 u8 dtim_period;
100 u8 *next;
97 101
98 if (dtim_len > 3) 102 tim = p54_find_ie(skb, WLAN_EID_TIM);
99 skb_trim(skb, skb->len - (dtim_len - 3)); 103 if (!tim)
104 return 0;
100 105
101 pos = end - (dtim_len + 2); 106 dtim_len = tim[1];
107 dtim_period = tim[3];
108 next = tim + 2 + dtim_len;
109
110 if (dtim_len < 3)
111 return -EINVAL;
112
113 memmove(tim, next, skb_tail_pointer(skb) - next);
114 tim = skb_tail_pointer(skb) - (dtim_len + 2);
115
116 /* add the dummy at the end */
117 tim[0] = WLAN_EID_TIM;
118 tim[1] = 3;
119 tim[2] = 0;
120 tim[3] = dtim_period;
121 tim[4] = 0;
122
123 if (dtim_len > 3)
124 skb_trim(skb, skb->len - (dtim_len - 3));
102 125
103 /* add the dummy at the end */
104 pos[0] = WLAN_EID_TIM;
105 pos[1] = 3;
106 pos[2] = 0;
107 pos[3] = dtim_period;
108 pos[4] = 0;
109 return 0;
110 }
111 pos += 2 + pos[1];
112 }
113 return 0; 126 return 0;
114} 127}
115 128
@@ -384,6 +397,9 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev,
384 priv->wakeup_timer = info->beacon_int * 397 priv->wakeup_timer = info->beacon_int *
385 info->dtim_period * 5; 398 info->dtim_period * 5;
386 p54_setup_mac(priv); 399 p54_setup_mac(priv);
400 } else {
401 priv->wakeup_timer = 500;
402 priv->aid = 0;
387 } 403 }
388 } 404 }
389 405
@@ -517,6 +533,9 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
517 skb_queue_head_init(&priv->tx_pending); 533 skb_queue_head_init(&priv->tx_pending);
518 dev->flags = IEEE80211_HW_RX_INCLUDES_FCS | 534 dev->flags = IEEE80211_HW_RX_INCLUDES_FCS |
519 IEEE80211_HW_SIGNAL_DBM | 535 IEEE80211_HW_SIGNAL_DBM |
536 IEEE80211_HW_SUPPORTS_PS |
537 IEEE80211_HW_PS_NULLFUNC_STACK |
538 IEEE80211_HW_BEACON_FILTER |
520 IEEE80211_HW_NOISE_DBM; 539 IEEE80211_HW_NOISE_DBM;
521 540
522 dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 541 dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index 19d085c73d7d..6772ed505d4d 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -208,6 +208,7 @@ struct p54_common {
208 u32 tsf_low32, tsf_high32; 208 u32 tsf_low32, tsf_high32;
209 u32 basic_rate_mask; 209 u32 basic_rate_mask;
210 u16 aid; 210 u16 aid;
211 bool powersave_override;
211 __le32 beacon_req_id; 212 __le32 beacon_req_id;
212 213
213 /* cryptographic engine information */ 214 /* cryptographic engine information */
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index 6426d2cae6de..01eadb1683fa 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -288,6 +288,45 @@ static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
288 priv->rssical_db[band].add) / 4; 288 priv->rssical_db[band].add) / 4;
289} 289}
290 290
291/*
292 * Even if the firmware is capable of dealing with incoming traffic,
293 * while dozing, we have to prepared in case mac80211 uses PS-POLL
294 * to retrieve outstanding frames from our AP.
295 * (see comment in net/mac80211/mlme.c @ line 1993)
296 */
297static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
298{
299 struct ieee80211_hdr *hdr = (void *) skb->data;
300 struct ieee80211_tim_ie *tim_ie;
301 u8 *tim;
302 u8 tim_len;
303 bool new_psm;
304
305 /* only beacons have a TIM IE */
306 if (!ieee80211_is_beacon(hdr->frame_control))
307 return;
308
309 if (!priv->aid)
310 return;
311
312 /* only consider beacons from the associated BSSID */
313 if (compare_ether_addr(hdr->addr3, priv->bssid))
314 return;
315
316 tim = p54_find_ie(skb, WLAN_EID_TIM);
317 if (!tim)
318 return;
319
320 tim_len = tim[1];
321 tim_ie = (struct ieee80211_tim_ie *) &tim[2];
322
323 new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
324 if (new_psm != priv->powersave_override) {
325 priv->powersave_override = new_psm;
326 p54_set_ps(priv);
327 }
328}
329
291static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) 330static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
292{ 331{
293 struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; 332 struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
@@ -340,6 +379,9 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
340 379
341 skb_pull(skb, header_len); 380 skb_pull(skb, header_len);
342 skb_trim(skb, le16_to_cpu(hdr->len)); 381 skb_trim(skb, le16_to_cpu(hdr->len));
382 if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
383 p54_pspoll_workaround(priv, skb);
384
343 ieee80211_rx_irqsafe(priv->hw, skb); 385 ieee80211_rx_irqsafe(priv->hw, skb);
344 386
345 queue_delayed_work(priv->hw->workqueue, &priv->work, 387 queue_delayed_work(priv->hw->workqueue, &priv->work,