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. |
