/* * Clear any frames queued on a node's power save queue. * The number of frames that were present is returned. */ int ath_node_pwrsaveq_drain(ath_node_t node) { struct ath_node *an = ATH_NODE(node); int qlen; struct ath_node_pwrsaveq *dataq,*mgtq; ath_wbuf_t athwbuf; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); qlen = ATH_NODE_PWRSAVEQ_QLEN(dataq); qlen += ATH_NODE_PWRSAVEQ_QLEN(mgtq); /* * free all the frames. */ ATH_NODE_PWRSAVEQ_LOCK(dataq); ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf); while(athwbuf) { ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf); } ATH_NODE_PWRSAVEQ_UNLOCK(dataq); ATH_NODE_PWRSAVEQ_LOCK(mgtq); ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf); while(athwbuf) { ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf); } ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); return qlen; }
/* * 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 ath_node_pwrsaveq_age(ath_node_t node) { struct ath_node *an = ATH_NODE(node); struct ath_softc *sc = an->an_sc; int discard = 0; struct ath_node_pwrsaveq *dataq,*mgtq; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); if ((ATH_NODE_PWRSAVEQ_QLEN(dataq) != 0) || (ATH_NODE_PWRSAVEQ_QLEN(mgtq) != 0)) { ath_wbuf_t athwbuf; for (;;) { ATH_NODE_PWRSAVEQ_LOCK(dataq); ATH_NODE_PWRSAVEQ_POLL(dataq, athwbuf); if((athwbuf == NULL) || (wbuf_get_age(athwbuf->wbuf) >= ATH_NODE_INACT_WAIT)) { if (athwbuf != NULL) wbuf_set_age(athwbuf->wbuf, wbuf_get_age(athwbuf->wbuf) - ATH_NODE_INACT_WAIT); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); break; } DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "discard data frame, age %u \n", wbuf_get_age(athwbuf->wbuf)); ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); discard++; } for (;;) { ATH_NODE_PWRSAVEQ_LOCK(mgtq); ATH_NODE_PWRSAVEQ_POLL(mgtq, athwbuf); if((athwbuf == NULL) || (wbuf_get_age(athwbuf->wbuf) >= ATH_NODE_INACT_WAIT)) { if (athwbuf != NULL) wbuf_set_age(athwbuf->wbuf, wbuf_get_age(athwbuf->wbuf) - ATH_NODE_INACT_WAIT); ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); break; } DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "discard mgt frame, age %u \n", wbuf_get_age(athwbuf->wbuf)); ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf); ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); discard++; } DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "discard %u frames for age \n", discard); } return discard; }
void ath_node_pwrsaveq_get_info(ath_node_t node, void *info) { struct ath_node *an = ATH_NODE(node); struct ath_node_pwrsaveq *dataq,*mgtq; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); ATH_NODE_PWRSAVEQ_LOCK(dataq); ATH_NODE_PWRSAVEQ_LOCK(mgtq); ((ath_node_pwrsaveq_info *)info)->data_count = ATH_NODE_PWRSAVEQ_QLEN(dataq); ((ath_node_pwrsaveq_info *)info)->mgt_count = ATH_NODE_PWRSAVEQ_QLEN(mgtq); ((ath_node_pwrsaveq_info *)info)->data_len = ATH_NODE_PWRSAVEQ_BYTES(dataq); ((ath_node_pwrsaveq_info *)info)->mgt_len = ATH_NODE_PWRSAVEQ_BYTES(mgtq); ((ath_node_pwrsaveq_info *)info)->ps_frame_count = mgtq->nsq_num_ps_frames; ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); }
/* * 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 by UMAC caller. */ void ath_node_pwrsaveq_queue(ath_node_t node, ath_wbuf_t athwbuf, u_int8_t frame_type) { struct ath_node *an = ATH_NODE(node); struct ath_softc *sc = an->an_sc; int qlen, age; struct ath_node_pwrsaveq *dataq,*mgtq,*psq; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); ATH_NODE_PWRSAVEQ_LOCK(dataq); ATH_NODE_PWRSAVEQ_LOCK(mgtq); if (frame_type == IEEE80211_FC0_TYPE_MGT) { psq = mgtq; } else { psq = dataq; } if (ATH_NODE_PWRSAVEQ_FULL(psq)) { ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); DPRINTF(sc, ATH_DEBUG_ANY, "%s pwr save q: overflow, drops (size %d) \n", (psq == dataq) ? "data" : "mgt", ATH_NODE_PWRSAVEQ_MAX_LEN); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); return; } /* * special handling of frames with PS = 1. */ ath_node_pwrsaveq_handle_ps_frames(an, athwbuf, frame_type); /* By default use the 4 * beacon intval as the aging time */ age = (ATH_NODE_AGE_INTVAL << 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 (ATH_NODE_INACT_WAIT is 150 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 * ATH_NODE_INACT_WAIT. Otherwise, we will discard all frames in ps queue even though * it is just queued. */ if (age < ATH_NODE_INACT_WAIT) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s Note: increased age from %d to %d secs.\n", __func__, age, ATH_NODE_INACT_WAIT); age = ATH_NODE_INACT_WAIT; } ATH_NODE_PWRSAVEQ_ENQUEUE(psq, athwbuf, qlen, age); /* * calculate the combined queue length of management and data queues. */ qlen = ATH_NODE_PWRSAVEQ_QLEN(dataq); qlen += ATH_NODE_PWRSAVEQ_QLEN(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s pwr queue:save frame, %u now queued \n", (psq == dataq) ? "data" : "mgt" ,qlen); /* TIM bit will be set by UMAC caller */ }
/* * node saveq flush. */ void ath_node_pwrsaveq_flush(ath_node_t node) { struct ath_node *an = ATH_NODE(node); struct ath_softc *sc = an->an_sc; ath_wbuf_t athwbuf; wbuf_t wbuf; struct ath_node_pwrsaveq *dataq,*mgtq; int dqlen, mqlen; ieee80211_tx_control_t *txctl; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); /* XXX if no stations in ps mode, flush mc frames */ dqlen = ATH_NODE_PWRSAVEQ_QLEN(dataq); /* * Flush queued mgmt frames. */ ATH_NODE_PWRSAVEQ_LOCK(mgtq); if (ATH_NODE_PWRSAVEQ_QLEN(mgtq) != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "flush mgt ps queue, %u packets queued \n", ATH_NODE_PWRSAVEQ_QLEN(mgtq)); for (;;) { ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf); if (athwbuf == NULL) break; wbuf = athwbuf->wbuf; ASSERT(wbuf); mqlen = ATH_NODE_PWRSAVEQ_QLEN(mgtq); DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "sendmgmt ps queue, %u packets remaining \n", mqlen); /* * For a Station node (non bss node) If this is the last packet, turn off the TIM bit, * Set the PWR_SAV bit on every frame but the last one * to allow encap to test for * adding more MORE_DATA bit to wh. */ if (mqlen || dqlen) { wbuf_set_moredata(wbuf); if (wbuf_is_moredata(wbuf)) { struct ieee80211_frame *wh = (struct ieee80211_frame *)wbuf_header(wbuf); wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; } } wbuf_clear_legacy_ps(wbuf); txctl = &athwbuf->txctl; if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); } else { if (athwbuf) { OS_FREE_PS(athwbuf); } } } } mgtq->nsq_num_ps_frames = 0; ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); /* * Flush queued unicast frames. */ ATH_NODE_PWRSAVEQ_LOCK(dataq); if (ATH_NODE_PWRSAVEQ_QLEN(dataq) != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "flush data ps queue, %u packets queued \n", ATH_NODE_PWRSAVEQ_QLEN(dataq)); for (;;) { ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf); if (athwbuf == NULL) break; wbuf = athwbuf->wbuf; ASSERT(wbuf); dqlen = ATH_NODE_PWRSAVEQ_QLEN(dataq); DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "senddata ps queue, %u packets remaining \n", dqlen); /* * For a Station node (non bss node) If this is the last packet, turn off the TIM bit, * Set the PWR_SAV bit on every frame but the last one * to allow encap to test for * adding more MORE_DATA bit to wh. */ if (dqlen) { wbuf_set_moredata(wbuf); if (wbuf_is_moredata(wbuf)) { struct ieee80211_frame *wh = (struct ieee80211_frame *)wbuf_header(wbuf); wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; } } wbuf_clear_legacy_ps(wbuf); txctl = &athwbuf->txctl; if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); } else { if (athwbuf) { OS_FREE_PS(athwbuf); } } } } ATH_NODE_PWRSAVEQ_UNLOCK(dataq); /* TIM will be set by UMAC caller */ }
/* * send one frme out of power save queue . * called in reponse to PS poll. * returns 1 if succesfully sends a frame. 0 other wise. */ int ath_node_pwrsaveq_send(ath_node_t node, u_int8_t frame_type) { struct ath_node *an = ATH_NODE(node); struct ath_softc *sc = an->an_sc; ath_wbuf_t athwbuf; wbuf_t wbuf = NULL; int qlen; struct ath_node_pwrsaveq *dataq, *mgtq; ieee80211_tx_control_t *txctl; struct ieee80211_frame *wh; u_int8_t more_frag; dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); mgtq = ATH_NODE_PWRSAVEQ_MGMTQ(an); next_frag: ATH_NODE_PWRSAVEQ_LOCK(dataq); ATH_NODE_PWRSAVEQ_LOCK(mgtq); if (frame_type == IEEE80211_FC0_TYPE_MGT) { ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf); if (athwbuf) wbuf = athwbuf->wbuf; if (wbuf && wbuf_is_pwrsaveframe(wbuf)) { /* ps frame (null (or) pspoll frame) */ --mgtq->nsq_num_ps_frames; } } else { ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf); if (athwbuf) wbuf = athwbuf->wbuf; } if (athwbuf == NULL || wbuf == NULL) { ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); return 0; } qlen = ATH_NODE_PWRSAVEQ_QLEN(dataq); qlen += ATH_NODE_PWRSAVEQ_QLEN(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(mgtq); ATH_NODE_PWRSAVEQ_UNLOCK(dataq); wh = (struct ieee80211_frame *)wbuf_header(wbuf); more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; /* * If there are more packets, set the more packets bit * in the packet dispatched to the station; otherwise * turn off the TIM bit. */ if (qlen != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "send packet, %u still queued \n", qlen); /* * encap will set more data bit. */ wbuf_set_moredata(wbuf); if (wbuf_is_moredata(wbuf)) { wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; } } else { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s", "send packet, queue empty \n"); /* TIM bit will be set by UMAC caller */ } wbuf_clear_legacy_ps(wbuf); txctl = &athwbuf->txctl; if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) { DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__); ath_node_pwrsaveq_complete_athwbuf(an, athwbuf); /* process all fragment together */ if (more_frag) goto next_frag; return 0; } if (athwbuf) { OS_FREE_PS(athwbuf); } /* process all fragments together */ if (more_frag) goto next_frag; return 1; }