u_int32_t ath_net80211_htc_node_getrate(const struct ieee80211_node *ni, u_int8_t type) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc_net80211 *scn = ATH_SOFTC_NET80211(ic); u_int8_t *vtar = NULL; u_int32_t size = 0; u_int8_t nodeindex = 0; nodeindex = ath_find_tgt_node_index((struct ieee80211_node *)ni); vtar = &nodeindex; size = sizeof(nodeindex); switch (type) { case IEEE80211_RATE_TX: ATH_NODE_NET80211(ni)->an_avgtxrate = scn->sc_ops->ath_wmi_node_getrate(scn->sc_dev, vtar, size); return (ATH_NODE_NET80211(ni)->an_avgtxrate); case IEEE80211_LASTRATE_TX: return (ATH_NODE_NET80211(ni)->an_lasttxrate) ; case IEEE80211_RATECODE_TX: return (ATH_NODE_NET80211(ni)->an_txratecode); case IEEE80211_RATE_RX: return ATH_RATE_OUT(ATH_NODE_NET80211(ni)->an_avgrxrate); default: return 0; } }
static void ath_net80211_drain_uapsd(void *arg, struct ieee80211_node *ni) { struct ath_softc_net80211 *scn = (struct ath_softc_net80211 *)arg; ath_node_t node = ATH_NODE_NET80211(ni)->an_sta; if (ni->ni_flags & IEEE80211_NODE_UAPSD_CREDIT_UPDATE) { scn->sc_ops->ath_htc_uapsd_credit_update(scn->sc_dev, node); } }
void ath_net80211_uapsd_process_uapsd_trigger(ieee80211_handle_t ieee, struct ieee80211_node *ni, bool enforce_max_sp, bool *sent_eosp) { struct ieee80211com *ic = NET80211_HANDLE(ieee); struct ath_softc_net80211 *scn = ATH_SOFTC_NET80211(ic); if (enforce_max_sp) { scn->sc_ops->process_uapsd_trigger(scn->sc_dev, ATH_NODE_NET80211(ni)->an_sta, ni->ni_uapsd_maxsp, 0, 0, sent_eosp, WME_UAPSD_NODE_MAXQDEPTH); } else { 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); } return; }
/* * This routine picks an AMSDU buffer, calls the platform specific 802.11 layer for * WLAN encapsulation and then dispatches it to hardware for transmit. */ void ath_amsdu_stageq_flush(struct ath_softc_net80211 *scn, struct ath_amsdu_tx *amsdutx) { struct ieee80211_node *ni; struct ieee80211com *ic; wbuf_t wbuf; ATH_AMSDU_TXQ_LOCK(scn); wbuf = amsdutx->amsdu_tx_buf; if (!wbuf) { ATH_AMSDU_TXQ_UNLOCK(scn); return; } amsdutx->amsdu_tx_buf = NULL; ATH_AMSDU_TXQ_UNLOCK(scn); ni = wbuf_get_node(wbuf); ic = ni->ni_ic; /* * Encapsulate the packet for transmission */ wbuf = ieee80211_encap(ni, wbuf); if (wbuf == NULL) { printk("%s[%d] : ERROR: ieee80211_encap ret NULL\n", __func__, __LINE__); return; } /* There is only one wbuf to send */ if (wbuf != NULL) { int error = 0; ATH_DEFINE_TXCTL(txctl, wbuf); HTC_WBUF_TX_DELCARE /* prepare this frame */ if (ath_tx_prepare(scn, wbuf, 0, txctl) != 0) { goto bad; } HTC_WBUF_TX_DATA_PREPARE(ic, scn); if (error == 0) { /* send this frame to hardware */ txctl->an = (ATH_NODE_NET80211(ni))->an_sta; if (scn->sc_ops->tx(scn->sc_dev, wbuf, txctl) != 0) { goto bad; } else { HTC_WBUF_TX_DATA_COMPLETE_STATUS(ic); } } }
/* * 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; }
/* * 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; }