/* * Startup beacon transmission for adhoc mode when they are sent entirely * by the hardware using the self-linked descriptor + veol trick. */ static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; struct ath_vap *avp; struct sk_buff *skb; avp = sc->sc_vaps[if_id]; ASSERT(avp); if (avp->av_bcbuf == NULL) { DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", __func__, avp, avp != NULL ? avp->av_bcbuf : NULL); return; } bf = avp->av_bcbuf; skb = (struct sk_buff *) bf->bf_mpdu; /* Construct tx descriptor. */ ath_beacon_setup(sc, avp, bf); /* NB: caller is known to have already stopped tx dma */ ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); ath9k_hw_txstart(ah, sc->sc_bhalq); DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__, sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc); }
void ath9k_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf = NULL; struct ieee80211_vif *vif; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int slot; if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { ath_dbg(common, RESET, "reset work is pending, skip beaconing now\n"); return; } /* * Check if the previous beacon has gone out. If * not don't try to post another, skip this period * and wait for the next. Missed beacons indicate * a problem and should not occur. If we miss too * many consecutive beacons reset the device. */ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; if (!ath9k_hw_check_alive(ah)) ieee80211_queue_work(sc->hw, &sc->hw_check_work); if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { ath_dbg(common, BSTUCK, "missed %u consecutive beacons\n", sc->beacon.bmisscnt); ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq); if (sc->beacon.bmisscnt > 3) ath9k_hw_bstuck_nfcal(ah); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); sc->beacon.bmisscnt = 0; ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK); } return; } slot = ath9k_beacon_choose_slot(sc); vif = sc->beacon.bslot[slot]; if (!vif || !vif->bss_conf.enable_beacon) return; bf = ath9k_beacon_generate(sc->hw, vif); WARN_ON(!bf); if (sc->beacon.bmisscnt != 0) { ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; } /* * Handle slot time change when a non-ERP station joins/leaves * an 11g network. The 802.11 layer notifies us via callback, * we mark updateslot, then wait one beacon before effecting * the change. This gives associated stations at least one * beacon interval to note the state change. * * NB: The slot time change state machine is clocked according * to whether we are bursting or staggering beacons. We * recognize the request to update and record the current * slot then don't transition until that slot is reached * again. If we miss a beacon for that slot then we'll be * slow to transition but we'll be sure at least one beacon * interval has passed. When bursting slot is always left * set to ATH_BCBUF so this check is a noop. */ if (sc->beacon.updateslot == UPDATE) { sc->beacon.updateslot = COMMIT; sc->beacon.slotupdate = slot; } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { ah->slottime = sc->beacon.slottime; ath9k_hw_init_global_settings(ah); sc->beacon.updateslot = OK; } if (bf) { ath9k_reset_beacon_status(sc); ath_dbg(common, BEACON, "Transmitting beacon for slot: %d\n", slot); /* NB: cabq traffic should already be queued and primed */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); if (!edma) ath9k_hw_txstart(ah, sc->beacon.beaconq); } }