/* * Age frames on the power save queue. The aging interval is * 4 times the listen interval specified by the station. This * number is factored into the age calculations when the frame * is placed on the queue. We store ages as time differences * so we can check and/or adjust only the head of the list. * If a frame's age exceeds the threshold then discard it. * The number of frames discarded is returned so the caller * can check if it needs to adjust the tim. */ int ieee80211_node_saveq_age(struct ieee80211_node *ni) { int discard = 0; struct node_powersave_queue *dataq,*mgtq; struct ieee80211vap *vap=ni->ni_vap; dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); /* XXX racey but good 'nuf? */ if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) != 0) || (IEEE80211_NODE_SAVEQ_QLEN(mgtq) != 0)) { wbuf_t wbuf; IEEE80211_NODE_SAVEQ_LOCK(dataq); while (IEEE80211_NODE_SAVEQ_POLL(dataq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); while (IEEE80211_NODE_SAVEQ_POLL(mgtq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard mgt frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard %u frames for age \n", discard); IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); } if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) == 0) && (IEEE80211_NODE_SAVEQ_QLEN(mgtq) == 0)) { if (vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 0); } return discard; }
static void ieee80211_node_saveq_handle_ps_frames(struct ieee80211_node *ni, wbuf_t wbuf, u_int8_t frame_type) { struct node_powersave_queue *mgtq; wbuf_t tmpwbuf; mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); if (wbuf_is_pwrsaveframe(wbuf)) { /* ps frame (null (or) pspoll frame) */ ++mgtq->nsq_num_ps_frames; } else { /* non ps frame*/ if (mgtq->nsq_num_ps_frames) { struct ieee80211_tx_status ts; struct node_powersave_queue *tmpq, temp_q; tmpq = &temp_q; IEEE80211_NODE_SAVEQ_INIT(tmpq); /* * go through the mgt queue and dump the frames with PS=1(pspoll,null). * accummulate the remaining frames into temp queue. */ for (;;) { IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, tmpwbuf); if (tmpwbuf == NULL) { break; } if (wbuf_is_pwrsaveframe(tmpwbuf)) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ANY, ni, "%s pwr save q: complete the PS frame with error \n", __func__); ts.ts_flags = IEEE80211_TX_ERROR; ieee80211_release_wbuf(ni,tmpwbuf, &ts); } else { IEEE80211_NODE_SAVEQ_ADD(tmpq,tmpwbuf); } } mgtq->nsq_num_ps_frames = 0; /* * move all the frames from temp queue to mgmt queue. */ for (;;) { IEEE80211_NODE_SAVEQ_DEQUEUE(tmpq, tmpwbuf); if (tmpwbuf == NULL) { break; } IEEE80211_NODE_SAVEQ_ADD(mgtq, tmpwbuf); } } } }
/* * Clear any frames queued on a node's power save queue. * The number of frames that were present is returned. */ int ieee80211_node_saveq_drain(struct ieee80211_node *ni) { int qlen; struct node_powersave_queue *dataq,*mgtq; wbuf_t wbuf; struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); qlen = IEEE80211_NODE_SAVEQ_QLEN(dataq); qlen += IEEE80211_NODE_SAVEQ_QLEN(mgtq); /* * free all the frames. */ IEEE80211_NODE_SAVEQ_LOCK(dataq); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); while(wbuf) { ieee80211_release_wbuf(ni,wbuf, &ts); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); } IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); while(wbuf) { ieee80211_release_wbuf(ni,wbuf, &ts); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); } IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); return qlen; }
/* * Save an outbound packet for a node in power-save sleep state. * The new packet is placed on the node's saved queue, and the TIM * is changed, if necessary. */ void ieee80211_node_saveq_queue(struct ieee80211_node *ni, wbuf_t wbuf, u_int8_t frame_type) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int qlen, age; struct node_powersave_queue *dataq,*mgtq,*psq; struct ieee80211_tx_status ts; #if LMAC_SUPPORT_POWERSAVE_QUEUE /* mark the frame and will give to ath layer */ wbuf_set_legacy_ps(wbuf); if (ic->ic_get_lmac_pwrsaveq_len(ic, ni, 0) == 0 && vap->iv_set_tim != NULL) { vap->iv_set_tim(ni, 1, false); } return; #endif dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); IEEE80211_NODE_SAVEQ_LOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); if (frame_type == IEEE80211_FC0_TYPE_MGT) { psq = mgtq; } else { psq = dataq; } if (IEEE80211_NODE_SAVEQ_FULL(psq)) { IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "%s pwr save q: overflow, drops (size %d) \n", (psq == dataq) ? "data" : "mgt", IEEE80211_PS_MAX_QUEUE); IEEE80211_NODE_STAT(ni,psq_drops); #ifdef IEEE80211_DEBUG if (ieee80211_msg_dumppkts(vap)) ieee80211_dump_pkt(ni->ni_ic, wtod(wbuf, caddr_t), wbuf_get_len(wbuf), -1, -1); #endif ts.ts_flags = IEEE80211_TX_ERROR; ieee80211_release_wbuf(ni,wbuf, &ts); return; } /* * special handling of frames with PS = 1. */ ieee80211_node_saveq_handle_ps_frames(ni,wbuf,frame_type); /* * Tag the frame with it's expiry time and insert * it in the queue. The aging interval is 4 times * the listen interval specified by the station. * Frames that sit around too long are reclaimed * using this information. */ age = ((ni->ni_intval * ic->ic_lintval) << 2) >> 10; /* TU -> secs */ /* * Note: our aging algorithm is not working well. In fact, due to the long interval * when the aging algorithm is called (IEEE80211_INACT_WAIT is 15 secs), we depend on * the associated station node to be disassociated to clear its stale frames. However, * as a temporary fix, I will make sure that the age is at least greater than * IEEE80211_INACT_WAIT. Otherwise, we will discard all frames in ps queue even though * it is just queued. */ if (age < IEEE80211_INACT_WAIT) { IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "%s Note: increased age from %d to %d secs.\n", __func__, age, IEEE80211_INACT_WAIT); age = IEEE80211_INACT_WAIT; } IEEE80211_NODE_SAVEQ_ENQUEUE(psq, wbuf, qlen, age); /* * calculate the combined queue length of management and data queues. */ qlen = IEEE80211_NODE_SAVEQ_QLEN(dataq); qlen += IEEE80211_NODE_SAVEQ_QLEN(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "%s pwr queue:save frame, %u now queued \n", (psq == dataq) ? "data" : "mgt" ,qlen); if (qlen == 1 && vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 1, false); }
/* * Age frames on the power save queue. The aging interval is * 4 times the listen interval specified by the station. This * number is factored into the age calculations when the frame * is placed on the queue. We store ages as time differences * so we can check and/or adjust only the head of the list. * If a frame's age exceeds the threshold then discard it. * The number of frames discarded is returned so the caller * can check if it needs to adjust the tim. */ int ieee80211_node_saveq_age(struct ieee80211_node *ni) { int discard = 0; struct node_powersave_queue *dataq,*mgtq; struct ieee80211vap *vap=ni->ni_vap; #if LMAC_SUPPORT_POWERSAVE_QUEUE struct ieee80211com *ic = ni->ni_ic; ic->ic_node_pwrsaveq_age(ic, ni); if (ic->ic_get_lmac_pwrsaveq_len(ic, ni, 0) == 0 && vap->iv_set_tim != NULL) { vap->iv_set_tim(ni, 0, false); } return 0; #endif dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); /* XXX racey but good 'nuf? */ if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) != 0) || (IEEE80211_NODE_SAVEQ_QLEN(mgtq) != 0)) { wbuf_t wbuf; IEEE80211_NODE_SAVEQ_LOCK(dataq); while (IEEE80211_NODE_SAVEQ_POLL(dataq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); while (IEEE80211_NODE_SAVEQ_POLL(mgtq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard mgt frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard %u frames for age \n", discard); IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); } if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) == 0) && (IEEE80211_NODE_SAVEQ_QLEN(mgtq) == 0)) { #ifdef ATH_SWRETRY /* also check whether there is frame in tid q */ if (vap->iv_set_tim != NULL && (ni->ni_ic)->ic_exist_pendingfrm_tidq(ni->ni_ic, ni) !=0) #else if (vap->iv_set_tim != NULL) #endif vap->iv_set_tim(ni, 0, false); } return discard; }
/* * Clear any frames queued on a node's power save queue. * The number of frames that were present is returned. */ int ieee80211_node_saveq_drain(struct ieee80211_node *ni) { int qlen; struct node_powersave_queue *dataq,*mgtq; wbuf_t wbuf; struct ieee80211_tx_status ts; //AUTELAN-Added-Begin:duanmingzhe for tid stuck int tidno = 0; u_int8_t subtype; struct ieee80211_frame *wh = NULL; struct ath_atx_tid *tid = NULL; struct ath_node *an = ((struct ath_node_net80211 *)(ni))->an_sta; struct ieee80211_frame_bar *bar; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = vap->iv_ic; osdev_t os_handle = ic->ic_osdev; struct ath_softc *sc = NULL; //AUTELAN-Added-End:duanmingzhe for tid stuck ts.ts_flags = IEEE80211_TX_ERROR; dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); qlen = IEEE80211_NODE_SAVEQ_QLEN(dataq); qlen += IEEE80211_NODE_SAVEQ_QLEN(mgtq); /* * free all the frames. */ IEEE80211_NODE_SAVEQ_LOCK(dataq); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); while(wbuf) { ieee80211_release_wbuf(ni,wbuf, &ts); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); } IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); while(wbuf) { //AUTELAN-Added-Begin:duanmingzhe for tid stuck /****************************************** Before release the wbuf, we need to check the wbuf is bar frame? If it is, call ATH_TX_RESUME_TID,because ******************************************/ wh = (struct ieee80211_frame *)wbuf_header(wbuf); subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if(subtype == IEEE80211_FC0_SUBTYPE_BAR) { bar = (struct ieee80211_frame_bar *) wbuf_header(wbuf); tidno = (le16toh(bar->i_ctl) & IEEE80211_BAR_CTL_TID_M) >> IEEE80211_BAR_CTL_TID_S; tid = &an->an_tx_tid[tidno]; sc = an->an_sc; if(tid!=NULL) { ATH_TX_RESUME_TID(sc, tid); } } //AUTELAN-Added-End:duanmingzhe for tid stuck ieee80211_release_wbuf(ni,wbuf, &ts); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); }