diff options
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2500usb.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800usb.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00config.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 88 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt73usb.c | 1 |
6 files changed, 139 insertions, 0 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index b21f81231a09..15237c275486 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c | |||
@@ -1797,6 +1797,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
1797 | __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); | 1797 | __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); |
1798 | } | 1798 | } |
1799 | __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); | 1799 | __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); |
1800 | __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); | ||
1800 | 1801 | ||
1801 | /* | 1802 | /* |
1802 | * Set the rssi offset. | 1803 | * Set the rssi offset. |
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index b5d5f2203c52..0eb44cf2f44a 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c | |||
@@ -634,6 +634,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
634 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); | 634 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); |
635 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); | 635 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); |
636 | __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); | 636 | __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); |
637 | __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); | ||
637 | 638 | ||
638 | setup_timer(&rt2x00dev->txstatus_timer, | 639 | setup_timer(&rt2x00dev->txstatus_timer, |
639 | rt2800usb_tx_sta_fifo_timeout, | 640 | rt2800usb_tx_sta_fifo_timeout, |
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 86e141000156..73d3332be614 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -662,6 +662,7 @@ enum rt2x00_state_flags { | |||
662 | * Driver configuration | 662 | * Driver configuration |
663 | */ | 663 | */ |
664 | CONFIG_CHANNEL_HT40, | 664 | CONFIG_CHANNEL_HT40, |
665 | CONFIG_POWERSAVING, | ||
665 | }; | 666 | }; |
666 | 667 | ||
667 | /* | 668 | /* |
@@ -681,6 +682,7 @@ enum rt2x00_capability_flags { | |||
681 | REQUIRE_TASKLET_CONTEXT, | 682 | REQUIRE_TASKLET_CONTEXT, |
682 | REQUIRE_SW_SEQNO, | 683 | REQUIRE_SW_SEQNO, |
683 | REQUIRE_HT_TX_DESC, | 684 | REQUIRE_HT_TX_DESC, |
685 | REQUIRE_PS_AUTOWAKE, | ||
684 | 686 | ||
685 | /* | 687 | /* |
686 | * Capabilities | 688 | * Capabilities |
@@ -875,10 +877,20 @@ struct rt2x00_dev { | |||
875 | u8 calibration[2]; | 877 | u8 calibration[2]; |
876 | 878 | ||
877 | /* | 879 | /* |
880 | * Association id. | ||
881 | */ | ||
882 | u16 aid; | ||
883 | |||
884 | /* | ||
878 | * Beacon interval. | 885 | * Beacon interval. |
879 | */ | 886 | */ |
880 | u16 beacon_int; | 887 | u16 beacon_int; |
881 | 888 | ||
889 | /** | ||
890 | * Timestamp of last received beacon | ||
891 | */ | ||
892 | unsigned long last_beacon; | ||
893 | |||
882 | /* | 894 | /* |
883 | * Low level statistics which will have | 895 | * Low level statistics which will have |
884 | * to be kept up to date while device is running. | 896 | * to be kept up to date while device is running. |
@@ -907,6 +919,11 @@ struct rt2x00_dev { | |||
907 | struct work_struct txdone_work; | 919 | struct work_struct txdone_work; |
908 | 920 | ||
909 | /* | 921 | /* |
922 | * Powersaving work | ||
923 | */ | ||
924 | struct delayed_work autowakeup_work; | ||
925 | |||
926 | /* | ||
910 | * Data queue arrays for RX, TX, Beacon and ATIM. | 927 | * Data queue arrays for RX, TX, Beacon and ATIM. |
911 | */ | 928 | */ |
912 | unsigned int data_queues; | 929 | unsigned int data_queues; |
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 2a313b6d378d..edebbf04bc57 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c | |||
@@ -100,6 +100,10 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, | |||
100 | erp.basic_rates = bss_conf->basic_rates; | 100 | erp.basic_rates = bss_conf->basic_rates; |
101 | erp.beacon_int = bss_conf->beacon_int; | 101 | erp.beacon_int = bss_conf->beacon_int; |
102 | 102 | ||
103 | /* Update the AID, this is needed for dynamic PS support */ | ||
104 | rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; | ||
105 | rt2x00dev->last_beacon = bss_conf->timestamp; | ||
106 | |||
103 | /* Update global beacon interval time, this is needed for PS support */ | 107 | /* Update global beacon interval time, this is needed for PS support */ |
104 | rt2x00dev->beacon_int = bss_conf->beacon_int; | 108 | rt2x00dev->beacon_int = bss_conf->beacon_int; |
105 | 109 | ||
@@ -204,6 +208,9 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | |||
204 | { | 208 | { |
205 | struct rt2x00lib_conf libconf; | 209 | struct rt2x00lib_conf libconf; |
206 | u16 hw_value; | 210 | u16 hw_value; |
211 | u16 autowake_timeout; | ||
212 | u16 beacon_int; | ||
213 | u16 beacon_diff; | ||
207 | 214 | ||
208 | memset(&libconf, 0, sizeof(libconf)); | 215 | memset(&libconf, 0, sizeof(libconf)); |
209 | 216 | ||
@@ -227,6 +234,10 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | |||
227 | sizeof(libconf.channel)); | 234 | sizeof(libconf.channel)); |
228 | } | 235 | } |
229 | 236 | ||
237 | if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && | ||
238 | (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) | ||
239 | cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); | ||
240 | |||
230 | /* | 241 | /* |
231 | * Start configuration. | 242 | * Start configuration. |
232 | */ | 243 | */ |
@@ -239,6 +250,26 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | |||
239 | if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) | 250 | if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) |
240 | rt2x00link_reset_tuner(rt2x00dev, false); | 251 | rt2x00link_reset_tuner(rt2x00dev, false); |
241 | 252 | ||
253 | if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && | ||
254 | (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && | ||
255 | (conf->flags & IEEE80211_CONF_PS)) { | ||
256 | beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; | ||
257 | beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); | ||
258 | |||
259 | if (beacon_diff > beacon_int) | ||
260 | beacon_diff = 0; | ||
261 | |||
262 | autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff; | ||
263 | queue_delayed_work(rt2x00dev->workqueue, | ||
264 | &rt2x00dev->autowakeup_work, | ||
265 | autowake_timeout - 15); | ||
266 | } | ||
267 | |||
268 | if (conf->flags & IEEE80211_CONF_PS) | ||
269 | set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); | ||
270 | else | ||
271 | clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); | ||
272 | |||
242 | rt2x00dev->curr_band = conf->channel->band; | 273 | rt2x00dev->curr_band = conf->channel->band; |
243 | rt2x00dev->curr_freq = conf->channel->center_freq; | 274 | rt2x00dev->curr_freq = conf->channel->center_freq; |
244 | rt2x00dev->tx_power = conf->power_level; | 275 | rt2x00dev->tx_power = conf->power_level; |
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 7776d9f1f297..2eb5196977fd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c | |||
@@ -141,6 +141,16 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work) | |||
141 | rt2x00dev); | 141 | rt2x00dev); |
142 | } | 142 | } |
143 | 143 | ||
144 | static void rt2x00lib_autowakeup(struct work_struct *work) | ||
145 | { | ||
146 | struct rt2x00_dev *rt2x00dev = | ||
147 | container_of(work, struct rt2x00_dev, autowakeup_work.work); | ||
148 | |||
149 | if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) | ||
150 | ERROR(rt2x00dev, "Device failed to wakeup.\n"); | ||
151 | clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); | ||
152 | } | ||
153 | |||
144 | /* | 154 | /* |
145 | * Interrupt context handlers. | 155 | * Interrupt context handlers. |
146 | */ | 156 | */ |
@@ -416,6 +426,77 @@ void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) | |||
416 | } | 426 | } |
417 | EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); | 427 | EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); |
418 | 428 | ||
429 | static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) | ||
430 | { | ||
431 | struct ieee80211_mgmt *mgmt = (void *)data; | ||
432 | u8 *pos, *end; | ||
433 | |||
434 | pos = (u8 *)mgmt->u.beacon.variable; | ||
435 | end = data + len; | ||
436 | while (pos < end) { | ||
437 | if (pos + 2 + pos[1] > end) | ||
438 | return NULL; | ||
439 | |||
440 | if (pos[0] == ie) | ||
441 | return pos; | ||
442 | |||
443 | pos += 2 + pos[1]; | ||
444 | } | ||
445 | |||
446 | return NULL; | ||
447 | } | ||
448 | |||
449 | static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, | ||
450 | struct sk_buff *skb, | ||
451 | struct rxdone_entry_desc *rxdesc) | ||
452 | { | ||
453 | struct ieee80211_hdr *hdr = (void *) skb->data; | ||
454 | struct ieee80211_tim_ie *tim_ie; | ||
455 | u8 *tim; | ||
456 | u8 tim_len; | ||
457 | bool cam; | ||
458 | |||
459 | /* If this is not a beacon, or if mac80211 has no powersaving | ||
460 | * configured, or if the device is already in powersaving mode | ||
461 | * we can exit now. */ | ||
462 | if (likely(!ieee80211_is_beacon(hdr->frame_control) || | ||
463 | !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) | ||
464 | return; | ||
465 | |||
466 | /* min. beacon length + FCS_LEN */ | ||
467 | if (skb->len <= 40 + FCS_LEN) | ||
468 | return; | ||
469 | |||
470 | /* and only beacons from the associated BSSID, please */ | ||
471 | if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || | ||
472 | !rt2x00dev->aid) | ||
473 | return; | ||
474 | |||
475 | rt2x00dev->last_beacon = jiffies; | ||
476 | |||
477 | tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); | ||
478 | if (!tim) | ||
479 | return; | ||
480 | |||
481 | if (tim[1] < sizeof(*tim_ie)) | ||
482 | return; | ||
483 | |||
484 | tim_len = tim[1]; | ||
485 | tim_ie = (struct ieee80211_tim_ie *) &tim[2]; | ||
486 | |||
487 | /* Check whenever the PHY can be turned off again. */ | ||
488 | |||
489 | /* 1. What about buffered unicast traffic for our AID? */ | ||
490 | cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); | ||
491 | |||
492 | /* 2. Maybe the AP wants to send multicast/broadcast data? */ | ||
493 | cam |= (tim_ie->bitmap_ctrl & 0x01); | ||
494 | |||
495 | if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) | ||
496 | rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, | ||
497 | IEEE80211_CONF_CHANGE_PS); | ||
498 | } | ||
499 | |||
419 | static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, | 500 | static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, |
420 | struct rxdone_entry_desc *rxdesc) | 501 | struct rxdone_entry_desc *rxdesc) |
421 | { | 502 | { |
@@ -531,6 +612,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry) | |||
531 | rxdesc.flags |= RX_FLAG_HT; | 612 | rxdesc.flags |= RX_FLAG_HT; |
532 | 613 | ||
533 | /* | 614 | /* |
615 | * Check if this is a beacon, and more frames have been | ||
616 | * buffered while we were in powersaving mode. | ||
617 | */ | ||
618 | rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); | ||
619 | |||
620 | /* | ||
534 | * Update extra components | 621 | * Update extra components |
535 | */ | 622 | */ |
536 | rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); | 623 | rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); |
@@ -1017,6 +1104,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) | |||
1017 | } | 1104 | } |
1018 | 1105 | ||
1019 | INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); | 1106 | INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); |
1107 | INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); | ||
1020 | 1108 | ||
1021 | /* | 1109 | /* |
1022 | * Let the driver probe the device to detect the capabilities. | 1110 | * Let the driver probe the device to detect the capabilities. |
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index a6ce7d6cbdfa..ad20953cbf05 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c | |||
@@ -2209,6 +2209,7 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
2209 | if (!modparam_nohwcrypt) | 2209 | if (!modparam_nohwcrypt) |
2210 | __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); | 2210 | __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); |
2211 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); | 2211 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); |
2212 | __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); | ||
2212 | 2213 | ||
2213 | /* | 2214 | /* |
2214 | * Set the rssi offset. | 2215 | * Set the rssi offset. |