diff options
author | Jouni Malinen <jouni.malinen@atheros.com> | 2009-05-14 14:28:48 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-20 14:46:24 -0400 |
commit | cc65965cbb24d2ca2bb70f26cac9d7243349e7e3 (patch) | |
tree | b46570ebe5a6f73b25dec14a8af06f2180a9859e | |
parent | 9d64a3cfaf3edb548b68ef4eedbadbb875eaa10e (diff) |
ath9k: Fix PS mode operation to receive buffered broadcast/multicast frames
The previous implementation was moving back to NETWORK SLEEP state
immediately after receiving a Beacon frame. This means that we are
unlikely to receive all the buffered broadcast/multicast frames that
would be sent after DTIM Beacon frames. Fix this by parsing the Beacon
frame and remaining awake, if needed, to receive the buffered
broadcast/multicast frames. The last buffered frame will trigger the
move back into NETWORK SLEEP state.
If the last broadcast/multicast frame is not received properly (or if
the AP fails to send it), the next Beacon frame will work as a backup
trigger for returning into NETWORK SLEEP.
A new debug type, PS (debug=0x800 module parameter), is added to make
it easier to debug potential power save issues in the
future. Currently, this is only used for the Beacon frame and buffered
broadcast/multicast receiving.
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/recv.c | 119 |
3 files changed, 114 insertions, 7 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 10ffc9442859..34256621d417 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | |||
@@ -515,6 +515,7 @@ struct ath_rfkill { | |||
515 | #define SC_OP_LED_ON BIT(13) | 515 | #define SC_OP_LED_ON BIT(13) |
516 | #define SC_OP_SCANNING BIT(14) | 516 | #define SC_OP_SCANNING BIT(14) |
517 | #define SC_OP_TSF_RESET BIT(15) | 517 | #define SC_OP_TSF_RESET BIT(15) |
518 | #define SC_OP_WAIT_FOR_CAB BIT(16) | ||
518 | 519 | ||
519 | struct ath_bus_ops { | 520 | struct ath_bus_ops { |
520 | void (*read_cachesize)(struct ath_softc *sc, int *csz); | 521 | void (*read_cachesize)(struct ath_softc *sc, int *csz); |
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 23298b90b52b..db845cf960c9 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h | |||
@@ -29,6 +29,7 @@ enum ATH_DEBUG { | |||
29 | ATH_DBG_BEACON = 0x00000100, | 29 | ATH_DBG_BEACON = 0x00000100, |
30 | ATH_DBG_CONFIG = 0x00000200, | 30 | ATH_DBG_CONFIG = 0x00000200, |
31 | ATH_DBG_FATAL = 0x00000400, | 31 | ATH_DBG_FATAL = 0x00000400, |
32 | ATH_DBG_PS = 0x00000800, | ||
32 | ATH_DBG_ANY = 0xffffffff | 33 | ATH_DBG_ANY = 0xffffffff |
33 | }; | 34 | }; |
34 | 35 | ||
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 58bb26c72771..72e9283bcf7b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c | |||
@@ -473,6 +473,112 @@ void ath_flushrecv(struct ath_softc *sc) | |||
473 | spin_unlock_bh(&sc->rx.rxflushlock); | 473 | spin_unlock_bh(&sc->rx.rxflushlock); |
474 | } | 474 | } |
475 | 475 | ||
476 | static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) | ||
477 | { | ||
478 | /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ | ||
479 | struct ieee80211_mgmt *mgmt; | ||
480 | u8 *pos, *end, id, elen; | ||
481 | struct ieee80211_tim_ie *tim; | ||
482 | |||
483 | mgmt = (struct ieee80211_mgmt *)skb->data; | ||
484 | pos = mgmt->u.beacon.variable; | ||
485 | end = skb->data + skb->len; | ||
486 | |||
487 | while (pos + 2 < end) { | ||
488 | id = *pos++; | ||
489 | elen = *pos++; | ||
490 | if (pos + elen > end) | ||
491 | break; | ||
492 | |||
493 | if (id == WLAN_EID_TIM) { | ||
494 | if (elen < sizeof(*tim)) | ||
495 | break; | ||
496 | tim = (struct ieee80211_tim_ie *) pos; | ||
497 | if (tim->dtim_count != 0) | ||
498 | break; | ||
499 | return tim->bitmap_ctrl & 0x01; | ||
500 | } | ||
501 | |||
502 | pos += elen; | ||
503 | } | ||
504 | |||
505 | return false; | ||
506 | } | ||
507 | |||
508 | static void ath_rx_ps_back_to_sleep(struct ath_softc *sc) | ||
509 | { | ||
510 | sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); | ||
511 | if (sc->hw->conf.flags & IEEE80211_CONF_PS) | ||
512 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); | ||
513 | } | ||
514 | |||
515 | static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) | ||
516 | { | ||
517 | struct ieee80211_mgmt *mgmt; | ||
518 | |||
519 | if (skb->len < 24 + 8 + 2 + 2) | ||
520 | return; | ||
521 | |||
522 | mgmt = (struct ieee80211_mgmt *)skb->data; | ||
523 | if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0) | ||
524 | return; /* not from our current AP */ | ||
525 | |||
526 | if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) { | ||
527 | /* We are not in PS mode anymore; remain awake */ | ||
528 | DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain " | ||
529 | "awake\n"); | ||
530 | sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); | ||
531 | return; | ||
532 | } | ||
533 | |||
534 | if (ath_beacon_dtim_pending_cab(skb)) { | ||
535 | /* | ||
536 | * Remain awake waiting for buffered broadcast/multicast | ||
537 | * frames. | ||
538 | */ | ||
539 | DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating " | ||
540 | "buffered broadcast/multicast frame(s)\n"); | ||
541 | sc->sc_flags |= SC_OP_WAIT_FOR_CAB; | ||
542 | return; | ||
543 | } | ||
544 | |||
545 | if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) { | ||
546 | /* | ||
547 | * This can happen if a broadcast frame is dropped or the AP | ||
548 | * fails to send a frame indicating that all CAB frames have | ||
549 | * been delivered. | ||
550 | */ | ||
551 | DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); | ||
552 | } | ||
553 | |||
554 | /* No more broadcast/multicast frames to be received at this point. */ | ||
555 | ath_rx_ps_back_to_sleep(sc); | ||
556 | } | ||
557 | |||
558 | static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) | ||
559 | { | ||
560 | struct ieee80211_hdr *hdr; | ||
561 | |||
562 | hdr = (struct ieee80211_hdr *)skb->data; | ||
563 | |||
564 | /* Process Beacon and CAB receive in PS state */ | ||
565 | if (ieee80211_is_beacon(hdr->frame_control)) | ||
566 | ath_rx_ps_beacon(sc, skb); | ||
567 | else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) && | ||
568 | (ieee80211_is_data(hdr->frame_control) || | ||
569 | ieee80211_is_action(hdr->frame_control)) && | ||
570 | is_multicast_ether_addr(hdr->addr1) && | ||
571 | !ieee80211_has_moredata(hdr->frame_control)) { | ||
572 | DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to " | ||
573 | "sleep\n"); | ||
574 | /* | ||
575 | * No more broadcast/multicast frames to be received at this | ||
576 | * point. | ||
577 | */ | ||
578 | ath_rx_ps_back_to_sleep(sc); | ||
579 | } | ||
580 | } | ||
581 | |||
476 | static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, | 582 | static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, |
477 | struct ieee80211_rx_status *rx_status) | 583 | struct ieee80211_rx_status *rx_status) |
478 | { | 584 | { |
@@ -667,8 +773,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
667 | rx_status.flag &= ~RX_FLAG_DECRYPTED; | 773 | rx_status.flag &= ~RX_FLAG_DECRYPTED; |
668 | } | 774 | } |
669 | 775 | ||
670 | ath_rx_send_to_mac80211(sc, skb, &rx_status); | ||
671 | |||
672 | /* We will now give hardware our shiny new allocated skb */ | 776 | /* We will now give hardware our shiny new allocated skb */ |
673 | bf->bf_mpdu = requeue_skb; | 777 | bf->bf_mpdu = requeue_skb; |
674 | bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data, | 778 | bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data, |
@@ -680,6 +784,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
680 | bf->bf_mpdu = NULL; | 784 | bf->bf_mpdu = NULL; |
681 | DPRINTF(sc, ATH_DBG_FATAL, | 785 | DPRINTF(sc, ATH_DBG_FATAL, |
682 | "dma_mapping_error() on RX\n"); | 786 | "dma_mapping_error() on RX\n"); |
787 | ath_rx_send_to_mac80211(sc, skb, &rx_status); | ||
683 | break; | 788 | break; |
684 | } | 789 | } |
685 | bf->bf_dmacontext = bf->bf_buf_addr; | 790 | bf->bf_dmacontext = bf->bf_buf_addr; |
@@ -695,11 +800,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
695 | sc->rx.rxotherant = 0; | 800 | sc->rx.rxotherant = 0; |
696 | } | 801 | } |
697 | 802 | ||
698 | if (ieee80211_is_beacon(fc) && | 803 | if (unlikely(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) |
699 | (sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) { | 804 | ath_rx_ps(sc, skb); |
700 | sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; | 805 | |
701 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); | 806 | ath_rx_send_to_mac80211(sc, skb, &rx_status); |
702 | } | 807 | |
703 | requeue: | 808 | requeue: |
704 | list_move_tail(&bf->list, &sc->rx.rxbuf); | 809 | list_move_tail(&bf->list, &sc->rx.rxbuf); |
705 | ath_rx_buf_link(sc, bf); | 810 | ath_rx_buf_link(sc, bf); |