1/*- 2 * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h>
| 1/*- 2 * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h>
|
27__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_power.c 170530 2007-06-11 03:36:55Z sam $");
| 27__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_power.c 172211 2007-09-17 19:07:24Z sam $");
|
28 29/* 30 * IEEE 802.11 power save support. 31 */ 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35 36#include <sys/socket.h> 37 38#include <net/if.h> 39#include <net/if_media.h> 40#include <net/ethernet.h> 41 42#include <net80211/ieee80211_var.h> 43 44#include <net/bpf.h> 45 46static void ieee80211_set_tim(struct ieee80211_node *ni, int set); 47 48void 49ieee80211_power_attach(struct ieee80211com *ic) 50{ 51 if (ic->ic_opmode == IEEE80211_M_HOSTAP || 52 ic->ic_opmode == IEEE80211_M_IBSS) { 53 /* NB: driver should override */ 54 ic->ic_set_tim = ieee80211_set_tim; 55 } 56} 57 58void 59ieee80211_power_lateattach(struct ieee80211com *ic) 60{ 61 /* 62 * Allocate these only if needed. Beware that we 63 * know adhoc mode doesn't support ATIM yet... 64 */ 65 if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 66 ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t); 67 MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len, 68 M_DEVBUF, M_NOWAIT | M_ZERO); 69 if (ic->ic_tim_bitmap == NULL) { 70 printf("%s: no memory for TIM bitmap!\n", __func__); 71 /* XXX good enough to keep from crashing? */ 72 ic->ic_tim_len = 0; 73 } 74 } 75} 76 77void 78ieee80211_power_detach(struct ieee80211com *ic) 79{ 80 if (ic->ic_tim_bitmap != NULL) { 81 FREE(ic->ic_tim_bitmap, M_DEVBUF); 82 ic->ic_tim_bitmap = NULL; 83 } 84} 85 86/* 87 * Clear any frames queued on a node's power save queue. 88 * The number of frames that were present is returned. 89 */ 90int 91ieee80211_node_saveq_drain(struct ieee80211_node *ni) 92{ 93 int qlen; 94 95 IEEE80211_NODE_SAVEQ_LOCK(ni); 96 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 97 _IF_DRAIN(&ni->ni_savedq); 98 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 99 100 return qlen; 101} 102 103/* 104 * Age frames on the power save queue. The aging interval is 105 * 4 times the listen interval specified by the station. This 106 * number is factored into the age calculations when the frame 107 * is placed on the queue. We store ages as time differences 108 * so we can check and/or adjust only the head of the list. 109 * If a frame's age exceeds the threshold then discard it. 110 * The number of frames discarded is returned so the caller 111 * can check if it needs to adjust the tim. 112 */ 113int 114ieee80211_node_saveq_age(struct ieee80211_node *ni) 115{ 116 int discard = 0; 117 118 if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { 119 struct mbuf *m; 120 121 IEEE80211_NODE_SAVEQ_LOCK(ni); 122 while (IF_POLL(&ni->ni_savedq, m) != NULL && 123 M_AGE_GET(m) < IEEE80211_INACT_WAIT) { 124IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ 125 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 126 m_freem(m); 127 discard++; 128 } 129 if (m != NULL) 130 M_AGE_SUB(m, IEEE80211_INACT_WAIT); 131 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 132 133 IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni, 134 "discard %u frames for age", discard); 135 IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); 136 } 137 return discard; 138} 139 140/* 141 * Indicate whether there are frames queued for a station in power-save mode. 142 */ 143static void 144ieee80211_set_tim(struct ieee80211_node *ni, int set) 145{ 146 struct ieee80211com *ic = ni->ni_ic; 147 uint16_t aid; 148 149 KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || 150 ic->ic_opmode == IEEE80211_M_IBSS, 151 ("operating mode %u", ic->ic_opmode)); 152 153 aid = IEEE80211_AID(ni->ni_associd); 154 KASSERT(aid < ic->ic_max_aid, 155 ("bogus aid %u, max %u", aid, ic->ic_max_aid)); 156 157 IEEE80211_BEACON_LOCK(ic); 158 if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { 159 if (set) { 160 setbit(ic->ic_tim_bitmap, aid); 161 ic->ic_ps_pending++; 162 } else { 163 clrbit(ic->ic_tim_bitmap, aid); 164 ic->ic_ps_pending--; 165 }
| 28 29/* 30 * IEEE 802.11 power save support. 31 */ 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35 36#include <sys/socket.h> 37 38#include <net/if.h> 39#include <net/if_media.h> 40#include <net/ethernet.h> 41 42#include <net80211/ieee80211_var.h> 43 44#include <net/bpf.h> 45 46static void ieee80211_set_tim(struct ieee80211_node *ni, int set); 47 48void 49ieee80211_power_attach(struct ieee80211com *ic) 50{ 51 if (ic->ic_opmode == IEEE80211_M_HOSTAP || 52 ic->ic_opmode == IEEE80211_M_IBSS) { 53 /* NB: driver should override */ 54 ic->ic_set_tim = ieee80211_set_tim; 55 } 56} 57 58void 59ieee80211_power_lateattach(struct ieee80211com *ic) 60{ 61 /* 62 * Allocate these only if needed. Beware that we 63 * know adhoc mode doesn't support ATIM yet... 64 */ 65 if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 66 ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t); 67 MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len, 68 M_DEVBUF, M_NOWAIT | M_ZERO); 69 if (ic->ic_tim_bitmap == NULL) { 70 printf("%s: no memory for TIM bitmap!\n", __func__); 71 /* XXX good enough to keep from crashing? */ 72 ic->ic_tim_len = 0; 73 } 74 } 75} 76 77void 78ieee80211_power_detach(struct ieee80211com *ic) 79{ 80 if (ic->ic_tim_bitmap != NULL) { 81 FREE(ic->ic_tim_bitmap, M_DEVBUF); 82 ic->ic_tim_bitmap = NULL; 83 } 84} 85 86/* 87 * Clear any frames queued on a node's power save queue. 88 * The number of frames that were present is returned. 89 */ 90int 91ieee80211_node_saveq_drain(struct ieee80211_node *ni) 92{ 93 int qlen; 94 95 IEEE80211_NODE_SAVEQ_LOCK(ni); 96 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 97 _IF_DRAIN(&ni->ni_savedq); 98 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 99 100 return qlen; 101} 102 103/* 104 * Age frames on the power save queue. The aging interval is 105 * 4 times the listen interval specified by the station. This 106 * number is factored into the age calculations when the frame 107 * is placed on the queue. We store ages as time differences 108 * so we can check and/or adjust only the head of the list. 109 * If a frame's age exceeds the threshold then discard it. 110 * The number of frames discarded is returned so the caller 111 * can check if it needs to adjust the tim. 112 */ 113int 114ieee80211_node_saveq_age(struct ieee80211_node *ni) 115{ 116 int discard = 0; 117 118 if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { 119 struct mbuf *m; 120 121 IEEE80211_NODE_SAVEQ_LOCK(ni); 122 while (IF_POLL(&ni->ni_savedq, m) != NULL && 123 M_AGE_GET(m) < IEEE80211_INACT_WAIT) { 124IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ 125 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 126 m_freem(m); 127 discard++; 128 } 129 if (m != NULL) 130 M_AGE_SUB(m, IEEE80211_INACT_WAIT); 131 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 132 133 IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni, 134 "discard %u frames for age", discard); 135 IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); 136 } 137 return discard; 138} 139 140/* 141 * Indicate whether there are frames queued for a station in power-save mode. 142 */ 143static void 144ieee80211_set_tim(struct ieee80211_node *ni, int set) 145{ 146 struct ieee80211com *ic = ni->ni_ic; 147 uint16_t aid; 148 149 KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || 150 ic->ic_opmode == IEEE80211_M_IBSS, 151 ("operating mode %u", ic->ic_opmode)); 152 153 aid = IEEE80211_AID(ni->ni_associd); 154 KASSERT(aid < ic->ic_max_aid, 155 ("bogus aid %u, max %u", aid, ic->ic_max_aid)); 156 157 IEEE80211_BEACON_LOCK(ic); 158 if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { 159 if (set) { 160 setbit(ic->ic_tim_bitmap, aid); 161 ic->ic_ps_pending++; 162 } else { 163 clrbit(ic->ic_tim_bitmap, aid); 164 ic->ic_ps_pending--; 165 }
|
166 ic->ic_flags |= IEEE80211_F_TIMUPDATE;
| 166 /* NB: we know ic is in RUN state so no need to check */ 167 ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
|
167 } 168 IEEE80211_BEACON_UNLOCK(ic); 169} 170 171/* 172 * Save an outbound packet for a node in power-save sleep state. 173 * The new packet is placed on the node's saved queue, and the TIM 174 * is changed, if necessary. 175 */ 176void 177ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) 178{ 179 struct ieee80211com *ic = ni->ni_ic; 180 int qlen, age; 181 182 IEEE80211_NODE_SAVEQ_LOCK(ni); 183 if (_IF_QFULL(&ni->ni_savedq)) { 184 _IF_DROP(&ni->ni_savedq); 185 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 186 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 187 "[%s] pwr save q overflow, drops %d (size %d)\n", 188 ether_sprintf(ni->ni_macaddr), 189 ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); 190#ifdef IEEE80211_DEBUG 191 if (ieee80211_msg_dumppkts(ic)) 192 ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1); 193#endif 194 m_freem(m); 195 return; 196 } 197 /* 198 * Tag the frame with it's expiry time and insert 199 * it in the queue. The aging interval is 4 times 200 * the listen interval specified by the station. 201 * Frames that sit around too long are reclaimed 202 * using this information. 203 */ 204 /* TU -> secs. XXX handle overflow? */ 205 age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; 206 _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); 207 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 208 209 IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 210 "[%s] save frame with age %d, %u now queued\n", 211 ether_sprintf(ni->ni_macaddr), age, qlen); 212 213 if (qlen == 1 && ic->ic_set_tim != NULL) 214 ic->ic_set_tim(ni, 1); 215} 216 217/* 218 * Handle station power-save state change. 219 */ 220void 221ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) 222{ 223 struct ieee80211com *ic = ni->ni_ic; 224 struct mbuf *m, *mhead, *mtail; 225 int mcount; 226 227 if (enable) { 228 if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) 229 ic->ic_ps_sta++; 230 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 231 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 232 "power save mode on, %u sta's in ps mode", ic->ic_ps_sta); 233 return; 234 } 235 236 if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) 237 ic->ic_ps_sta--; 238 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 239 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 240 "power save mode off, %u sta's in ps mode", ic->ic_ps_sta); 241 /* XXX if no stations in ps mode, flush mc frames */ 242 243 /* 244 * Flush queued unicast frames. 245 */ 246 if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { 247 if (ic->ic_set_tim != NULL) 248 ic->ic_set_tim(ni, 0); /* just in case */ 249 return; 250 } 251 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 252 "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni)); 253 /* 254 * Unload the frames from the ps q but don't send them 255 * to the driver yet. We do this in two stages to minimize 256 * locking but also because there's no easy way to preserve 257 * ordering given the existing ifnet access mechanisms. 258 * XXX could be optimized 259 */ 260 IEEE80211_NODE_SAVEQ_LOCK(ni); 261 mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); 262 mhead = mtail = NULL; 263 for (;;) { 264 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 265 if (m == NULL) 266 break; 267 if (mhead == NULL) { 268 mhead = m; 269 m->m_nextpkt = NULL; 270 } else 271 mtail->m_nextpkt = m; 272 mtail = m; 273 } 274 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 275 if (mhead != NULL) { 276 /* XXX need different driver interface */ 277 /* XXX bypasses q max */ 278 IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); 279 } 280 if (ic->ic_set_tim != NULL) 281 ic->ic_set_tim(ni, 0); 282} 283 284/* 285 * Handle power-save state change in station mode. 286 */ 287void 288ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable) 289{ 290 struct ieee80211_node *ni = ic->ic_bss; 291 int qlen; 292 293 if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) 294 return; 295 296 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 297 "sta power save mode %s", enable ? "on" : "off"); 298 if (!enable) { 299 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 300 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 301 /* 302 * Flush any queued frames; we can do this immediately 303 * because we know they'll be queued behind the null 304 * data frame we send the ap. 305 * XXX can we use a data frame to take us out of ps? 306 */ 307 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 308 if (qlen != 0) { 309 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 310 "flush ps queue, %u packets queued", qlen); 311 for (;;) { 312 struct mbuf *m; 313 314 IEEE80211_NODE_SAVEQ_LOCK(ni); 315 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 316 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 317 if (m == NULL) 318 break; 319 /* XXX need different driver interface */ 320 /* XXX bypasses q max */ 321 IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 322 } 323 } 324 } else { 325 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 326 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 327 } 328}
| 168 } 169 IEEE80211_BEACON_UNLOCK(ic); 170} 171 172/* 173 * Save an outbound packet for a node in power-save sleep state. 174 * The new packet is placed on the node's saved queue, and the TIM 175 * is changed, if necessary. 176 */ 177void 178ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) 179{ 180 struct ieee80211com *ic = ni->ni_ic; 181 int qlen, age; 182 183 IEEE80211_NODE_SAVEQ_LOCK(ni); 184 if (_IF_QFULL(&ni->ni_savedq)) { 185 _IF_DROP(&ni->ni_savedq); 186 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 187 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 188 "[%s] pwr save q overflow, drops %d (size %d)\n", 189 ether_sprintf(ni->ni_macaddr), 190 ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); 191#ifdef IEEE80211_DEBUG 192 if (ieee80211_msg_dumppkts(ic)) 193 ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1); 194#endif 195 m_freem(m); 196 return; 197 } 198 /* 199 * Tag the frame with it's expiry time and insert 200 * it in the queue. The aging interval is 4 times 201 * the listen interval specified by the station. 202 * Frames that sit around too long are reclaimed 203 * using this information. 204 */ 205 /* TU -> secs. XXX handle overflow? */ 206 age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; 207 _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); 208 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 209 210 IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 211 "[%s] save frame with age %d, %u now queued\n", 212 ether_sprintf(ni->ni_macaddr), age, qlen); 213 214 if (qlen == 1 && ic->ic_set_tim != NULL) 215 ic->ic_set_tim(ni, 1); 216} 217 218/* 219 * Handle station power-save state change. 220 */ 221void 222ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) 223{ 224 struct ieee80211com *ic = ni->ni_ic; 225 struct mbuf *m, *mhead, *mtail; 226 int mcount; 227 228 if (enable) { 229 if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) 230 ic->ic_ps_sta++; 231 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 232 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 233 "power save mode on, %u sta's in ps mode", ic->ic_ps_sta); 234 return; 235 } 236 237 if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) 238 ic->ic_ps_sta--; 239 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 240 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 241 "power save mode off, %u sta's in ps mode", ic->ic_ps_sta); 242 /* XXX if no stations in ps mode, flush mc frames */ 243 244 /* 245 * Flush queued unicast frames. 246 */ 247 if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { 248 if (ic->ic_set_tim != NULL) 249 ic->ic_set_tim(ni, 0); /* just in case */ 250 return; 251 } 252 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 253 "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni)); 254 /* 255 * Unload the frames from the ps q but don't send them 256 * to the driver yet. We do this in two stages to minimize 257 * locking but also because there's no easy way to preserve 258 * ordering given the existing ifnet access mechanisms. 259 * XXX could be optimized 260 */ 261 IEEE80211_NODE_SAVEQ_LOCK(ni); 262 mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); 263 mhead = mtail = NULL; 264 for (;;) { 265 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 266 if (m == NULL) 267 break; 268 if (mhead == NULL) { 269 mhead = m; 270 m->m_nextpkt = NULL; 271 } else 272 mtail->m_nextpkt = m; 273 mtail = m; 274 } 275 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 276 if (mhead != NULL) { 277 /* XXX need different driver interface */ 278 /* XXX bypasses q max */ 279 IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); 280 } 281 if (ic->ic_set_tim != NULL) 282 ic->ic_set_tim(ni, 0); 283} 284 285/* 286 * Handle power-save state change in station mode. 287 */ 288void 289ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable) 290{ 291 struct ieee80211_node *ni = ic->ic_bss; 292 int qlen; 293 294 if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) 295 return; 296 297 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 298 "sta power save mode %s", enable ? "on" : "off"); 299 if (!enable) { 300 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 301 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 302 /* 303 * Flush any queued frames; we can do this immediately 304 * because we know they'll be queued behind the null 305 * data frame we send the ap. 306 * XXX can we use a data frame to take us out of ps? 307 */ 308 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 309 if (qlen != 0) { 310 IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, 311 "flush ps queue, %u packets queued", qlen); 312 for (;;) { 313 struct mbuf *m; 314 315 IEEE80211_NODE_SAVEQ_LOCK(ni); 316 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 317 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 318 if (m == NULL) 319 break; 320 /* XXX need different driver interface */ 321 /* XXX bypasses q max */ 322 IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 323 } 324 } 325 } else { 326 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 327 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 328 } 329}
|