/* * This function determines whether the received frame is a valid UAPSD trigger. * Called from interrupt context or DPC context depending on parameter isr_context. */ bool ath_net80211_check_uapsdtrigger(ieee80211_handle_t ieee, struct ieee80211_qosframe *qwh, u_int16_t keyix, bool isr_context) { struct ieee80211com *ic = NET80211_HANDLE(ieee); struct ath_softc_net80211 *scn = ATH_SOFTC_NET80211(ic); struct ieee80211_node *ni; int tid, ac; u_int16_t frame_seq; int queue_depth; bool isapsd = false; /* * Locate the node for sender */ IEEE80211_KEYMAP_LOCK(scn); ni = (keyix != HAL_RXKEYIX_INVALID) ? scn->sc_keyixmap[keyix] : NULL; if (ni == NULL) { IEEE80211_KEYMAP_UNLOCK(scn); /* * No key index or no entry, do a lookup */ if (isr_context) { ni = ieee80211_find_rxnode_nolock(ic, (struct ieee80211_frame_min *)qwh); } else { ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)qwh); } if (ni == NULL) { return isapsd; } } else { ieee80211_ref_node(ni); IEEE80211_KEYMAP_UNLOCK(scn); } if (!(ni->ni_flags & IEEE80211_NODE_UAPSD)) goto end; if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) goto end; /* * Must deal with change of state here, since otherwise there would * be a race (on two quick frames from STA) between this code and the * tasklet where we would: * - miss a trigger on entry to PS if we're already trigger hunting * - generate spurious SP on exit (due to frame following exit frame) */ if ((((qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == IEEE80211_FC1_PWR_MGT) ^ ((ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) == IEEE80211_NODE_UAPSD_TRIG))) { ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP; if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) { WME_UAPSD_NODE_TRIGSEQINIT(ni); ni->ni_stats.ns_uapsd_triggerenabled++; ni->ni_flags |= IEEE80211_NODE_UAPSD_TRIG; } else { /* * Node transitioned from UAPSD -> Active state. Flush out UAPSD frames */ ni->ni_stats.ns_uapsd_active++; ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG; scn->sc_ops->process_uapsd_trigger(scn->sc_dev, ATH_NODE_NET80211(ni)->an_sta, WME_UAPSD_NODE_MAXQDEPTH, 0, 1, WME_UAPSD_NODE_MAXQDEPTH); } goto end; } /* * Check for a valid trigger frame i.e. QoS Data or QoS NULL */ if ( ((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ) || !(qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) ) { goto end; } if (((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) && (((qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_QOS) || ((qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_QOS_NULL))) { tid = qwh->i_qos[0] & IEEE80211_QOS_TID; ac = TID_TO_WME_AC(tid); isapsd = true; if (WME_UAPSD_AC_CAN_TRIGGER(ac, ni)) { /* * Detect duplicate triggers and drop if so. */ frame_seq = le16toh(*(u_int16_t *)qwh->i_seq); if ((qwh->i_fc[1] & IEEE80211_FC1_RETRY) && frame_seq == ni->ni_uapsd_trigseq[ac]) { ni->ni_stats.ns_uapsd_duptriggers++; goto end; } /* * SP in progress for this node, discard trigger. */ if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) { ni->ni_stats.ns_uapsd_ignoretriggers++; goto end; } /* start the SP */ ni->ni_stats.ns_uapsd_triggers++; ni->ni_flags |= IEEE80211_NODE_UAPSD_SP; ni->ni_uapsd_trigseq[ac] = frame_seq; queue_depth = scn->sc_ops->process_uapsd_trigger(scn->sc_dev, ATH_NODE_NET80211(ni)->an_sta, ni->ni_uapsd_maxsp, ac, 0, WME_UAPSD_NODE_MAXQDEPTH); if (!queue_depth && (ni->ni_vap->iv_set_tim != NULL) && IEEE80211_NODE_UAPSD_USETIM(ni)) { ni->ni_vap->iv_set_tim(ni, 0); } } } end: ieee80211_free_node(ni); return isapsd; }
*/ tsfadjust = ni->ni_intval * (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ DPRINTF(sc, ATH_DEBUG_BEACON, "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", __func__, sc->sc_stagbeacons ? "stagger" : "burst", avp->av_bslot, ni->ni_intval, (long long unsigned) le64toh(tsfadjust)); wh = mtod(m, struct ieee80211_frame *); memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); } bf->bf_m = m; bf->bf_node = ieee80211_ref_node(ni); return 0; } /* * Setup the beacon frame for transmit. */ static void ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) { #define USE_SHPREAMBLE(_ic) \ (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ == IEEE80211_F_SHPREAMBLE) struct ieee80211_node *ni = bf->bf_node; struct ieee80211com *ic = ni->ni_ic;
static int hwmp_send_action(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], uint8_t *ie, size_t len) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "HWMP action"); vap->iv_stats.is_tx_badstate++; return EIO; /* XXX */ } KASSERT(ni != NULL, ("null node")); /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ #ifdef IEEE80211_DEBUG_REFCNT IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); #endif ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(struct ieee80211_action) + len ); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } *frm++ = IEEE80211_ACTION_CAT_MESHPATH; *frm++ = IEEE80211_ACTION_MESHPATH_SEL; switch (*ie) { case IEEE80211_ELEMID_MESHPREQ: frm = hwmp_add_meshpreq(frm, (struct ieee80211_meshpreq_ie *)ie); break; case IEEE80211_ELEMID_MESHPREP: frm = hwmp_add_meshprep(frm, (struct ieee80211_meshprep_ie *)ie); break; case IEEE80211_ELEMID_MESHPERR: frm = hwmp_add_meshperr(frm, (struct ieee80211_meshperr_ie *)ie); break; case IEEE80211_ELEMID_MESHRANN: frm = hwmp_add_meshrann(frm, (struct ieee80211_meshrann_ie *)ie); break; } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, IEEE80211_NONQOS_TID, sa, da, sa); m->m_flags |= M_ENCAP; /* mark encapsulated */ IEEE80211_NODE_STAT(ni, tx_mgmt); memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; params.ibp_rate0 = ni->ni_txparms->mgmtrate; if (IEEE80211_IS_MULTICAST(da)) params.ibp_try0 = 1; else params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; return ic->ic_raw_xmit(ni, m, ¶ms); }
/* * This function is called for each frame received on the high priority queue. * If the hardware has classified this frame as a UAPSD trigger, we locate the node * and deliver data. * For non-trigger frames, we check for PM transition. * Called from interrupt context. */ void ath_net80211_uapsd_deliverdata(ieee80211_handle_t ieee, struct ieee80211_qosframe *qwh, u_int16_t keyix, u_int8_t is_trig, bool isr_context) { struct ieee80211com *ic = NET80211_HANDLE(ieee); struct ath_softc_net80211 *scn = ATH_SOFTC_NET80211(ic); struct ieee80211_node *ni; int tid, ac; u_int16_t frame_seq; int queue_depth; bool sent_eosp = false; UNREFERENCED_PARAMETER(isr_context); /* * Locate the node for sender */ IEEE80211_KEYMAP_LOCK(scn); ni = (keyix != HAL_RXKEYIX_INVALID) ? scn->sc_keyixmap[keyix] : NULL; IEEE80211_KEYMAP_UNLOCK(scn); if (ni == NULL) { /* * No key index or no entry, do a lookup */ ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)qwh); if (ni == NULL) { return; } } else { ieee80211_ref_node(ni); } if (!(ni->ni_flags & IEEE80211_NODE_UAPSD) || (ni->ni_flags & IEEE80211_NODE_ATH_PAUSED)) goto end; /* We cannot have a PM state change if this is a trigger frame */ if (!is_trig) { /* * Must deal with change of state here, since otherwise there would * be a race (on two quick frames from STA) between this code and the * tasklet where we would: * - miss a trigger on entry to PS if we're already trigger hunting * - generate spurious SP on exit (due to frame following exit frame) */ if ((((qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == IEEE80211_FC1_PWR_MGT) ^ ((ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) == IEEE80211_NODE_UAPSD_TRIG))) { ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP; if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) { WME_UAPSD_NODE_TRIGSEQINIT(ni); ni->ni_stats.ns_uapsd_triggerenabled++; ni->ni_flags |= IEEE80211_NODE_UAPSD_TRIG; } else { /* * Node transitioned from UAPSD -> Active state. Flush out UAPSD frames */ ni->ni_stats.ns_uapsd_active++; ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG; scn->sc_ops->process_uapsd_trigger(scn->sc_dev, ATH_NODE_NET80211(ni)->an_sta, WME_UAPSD_NODE_MAXQDEPTH, 0, 1, &sent_eosp, WME_UAPSD_NODE_MAXQDEPTH); } goto end; } } else { /* Is UAPSD trigger */ tid = qwh->i_qos[0] & IEEE80211_QOS_TID; ac = TID_TO_WME_AC(tid); if (WME_UAPSD_AC_CAN_TRIGGER(ac, ni)) { /* * Detect duplicate triggers and drop if so. */ frame_seq = le16toh(*(u_int16_t *)qwh->i_seq); if ((qwh->i_fc[1] & IEEE80211_FC1_RETRY) && frame_seq == ni->ni_uapsd_trigseq[ac]) { ni->ni_stats.ns_uapsd_duptriggers++; DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : Drop duplicate trigger\n", __func__); goto end; } if (IEEE80211_IS_TDLS_NODE(ni)) { /* * TDLS defines any QoS frame with EOSP * set to be a non-trigger frame. Therefore, * ignore trigger. */ if(qwh->i_qos[0] & IEEE80211_QOS_EOSP) { ni->ni_stats.ns_uapsd_ignoretriggers++; DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : TDLS QOS frame with EOSP; ignore trigger\n", __func__); goto end; } } /* * SP in progress for this node, discard trigger. */ if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) { ni->ni_stats.ns_uapsd_ignoretriggers++; DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : SP in-progress; ignore trigger\n", __func__); goto end; } ni->ni_stats.ns_uapsd_triggers++; DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : Start SP\n", __func__); queue_depth = scn->sc_ops->process_uapsd_trigger(scn->sc_dev, ATH_NODE_NET80211(ni)->an_sta, ni->ni_uapsd_maxsp, ac, 0, &sent_eosp, WME_UAPSD_NODE_MAXQDEPTH); if (queue_depth == -1) goto end; /* start the SP */ ni->ni_flags |= IEEE80211_NODE_UAPSD_SP; ni->ni_uapsd_trigseq[ac] = frame_seq; if (IEEE80211_IS_TDLS_NODE(ni)) { if (sent_eosp) { ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP; DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : TDLS; End SP\n", __func__); } } else { if (!queue_depth && (ni->ni_vap->iv_set_tim != NULL) && IEEE80211_NODE_UAPSD_USETIM(ni)) { ni->ni_vap->iv_set_tim(ni, 0, isr_context); } } } } end: ieee80211_free_node(ni); return; }
/* * Allocate a beacon frame and fillin the appropriate bits. */ struct sk_buff * ieee80211_beacon_alloc(struct ieee80211_node *ni, struct ieee80211_beacon_offsets *bo) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; struct sk_buff *skb; int pktlen; u_int8_t *frm; struct ieee80211_rateset *rs; /* * beacon frame format * [8] time stamp * [2] beacon interval * [2] capability information * [tlv] ssid * [tlv] supported rates * [7] FH/DS parameter set * [tlv] IBSS/TIM parameter set * [tlv] country code * [3] power constraint * [5] channel switch announcement * [3] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] WME parameters * [tlv] WPA/RSN parameters * [tlv] Atheros Advanced Capabilities * [tlv] AtherosXR parameters * XXX Vendor-specific OIDs (e.g. Atheros) * NB: we allocate the max space required for the TIM bitmap. */ rs = &ni->ni_rates; pktlen = 8 /* time stamp */ + sizeof(u_int16_t) /* beacon interval */ + sizeof(u_int16_t) /* capability information */ + 2 + ni->ni_esslen /* ssid */ + 2 + IEEE80211_RATE_SIZE /* supported rates */ + 7 /* FH/DS parameters max(7,3) */ + 2 + 4 + vap->iv_tim_len /* IBSS/TIM parameter set*/ + ic->ic_country_ie.country_len + 2 /* country code */ + 3 /* power constraint */ + 5 /* channel switch announcement */ + 3 /* ERP */ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) /* Ext. Supp. Rates */ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ sizeof(struct ieee80211_wme_param) : 0) + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 2 * sizeof(struct ieee80211_ie_wpa) : 0) + sizeof(struct ieee80211_ie_athAdvCap) #ifdef ATH_SUPERG_XR + (ic->ic_ath_cap & IEEE80211_ATHC_XR ? /* XR */ sizeof(struct ieee80211_xr_param) : 0) #endif ; skb = ieee80211_getmgtframe(&frm, pktlen); if (skb == NULL) { IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "%s: cannot get buf; size %u", __func__, pktlen); vap->iv_stats.is_tx_nobuf++; return NULL; } SKB_NI(skb) = ieee80211_ref_node(ni); frm = ieee80211_beacon_init(ni, bo, frm); skb_trim(skb, frm - skb->data); wh = (struct ieee80211_frame *) skb_push(skb, sizeof(struct ieee80211_frame)); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; wh->i_dur = 0; IEEE80211_ADDR_COPY(wh->i_addr1, ic->ic_dev->broadcast); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bssid); IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: beacon bssid:" MAC_FMT "\n", __func__, MAC_ADDR(wh->i_addr3)); *(u_int16_t *)wh->i_seq = 0; return skb; }