diff options
author | Christian Lamparter <chunkeey@web.de> | 2009-07-07 13:08:07 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-24 15:05:06 -0400 |
commit | e0f114e82e3781087a0ad0e92c94ff0d55012c1a (patch) | |
tree | d4c7cfa485360952b3da9fee7acc07efcfdcc7d8 | |
parent | 0a2b8bb24d4eb67788edd71d1ef8aa86c2e17e0f (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>
-rw-r--r-- | drivers/net/wireless/p54/fwio.c | 7 | ||||
-rw-r--r-- | drivers/net/wireless/p54/lmac.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/p54/main.c | 77 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/p54/txrx.c | 42 |
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, | |||
548 | int p54_download_eeprom(struct p54_common *priv, void *buf, | 548 | int p54_download_eeprom(struct p54_common *priv, void *buf, |
549 | u16 offset, u16 len); | 549 | u16 offset, u16 len); |
550 | 550 | ||
551 | /* utility */ | ||
552 | u8 *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 | ||
68 | static int p54_beacon_format_ie_tim(struct sk_buff *skb) | 68 | u8 *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); | 90 | static 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 | */ | ||
297 | static 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 | |||
291 | static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) | 330 | static 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, |