/* * 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. */ int ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211_psq *psq = &ni->ni_psq; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_psq_head *qhead; int qlen, age; IEEE80211_PSQ_LOCK(psq); if (psq->psq_len >= psq->psq_maxlen) { psq->psq_drops++; IEEE80211_PSQ_UNLOCK(psq); IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "pwr save q overflow, drops %d (size %d)", psq->psq_drops, psq->psq_len); #ifdef IEEE80211_DEBUG if (ieee80211_msg_dumppkts(vap)) ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t), m->m_len, -1, -1); #endif psq_mfree(m); return ENOSPC; } /* * Tag the frame with it's expiry time and insert it in * the appropriate 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. */ /* TU -> secs. XXX handle overflow? */ age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; /* * Encapsulated frames go on the high priority queue, * other stuff goes on the low priority queue. We use * this to order frames returned out of the driver * ahead of frames we collect in ieee80211_start. */ if (m->m_flags & M_ENCAP) qhead = &psq->psq_head[0]; else qhead = &psq->psq_head[1]; if (qhead->tail == NULL) { struct mbuf *mh; qhead->head = m; /* * Take care to adjust age when inserting the first * frame of a queue and the other queue already has * frames. We need to preserve the age difference * relationship so ieee80211_node_psq_age works. */ if (qhead == &psq->psq_head[1]) { mh = psq->psq_head[0].head; if (mh != NULL) age-= M_AGE_GET(mh); } else { mh = psq->psq_head[1].head; if (mh != NULL) { int nage = M_AGE_GET(mh) - age; /* XXX is clamping to zero good 'nuf? */ M_AGE_SET(mh, nage < 0 ? 0 : nage); } } } else { qhead->tail->m_nextpkt = m; age -= M_AGE_GET(qhead->head); } KASSERT(age >= 0, ("age %d", age)); M_AGE_SET(m, age); m->m_nextpkt = NULL; qhead->tail = m; qhead->len++; qlen = ++(psq->psq_len); IEEE80211_PSQ_UNLOCK(psq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "save frame with age %d, %u now queued", age, qlen); if (qlen == 1 && vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 1); return 0; }
/* * 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); }