ieee80211_power.c revision 178354
1/*- 2 * Copyright (c) 2002-2008 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 178354 2008-04-20 20:35:46Z sam $"); 28 29/* 30 * IEEE 802.11 power save support. 31 */ 32#include "opt_wlan.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37 38#include <sys/socket.h> 39 40#include <net/if.h> 41#include <net/if_media.h> 42#include <net/ethernet.h> 43 44#include <net80211/ieee80211_var.h> 45 46#include <net/bpf.h> 47 48static void ieee80211_update_ps(struct ieee80211vap *, int); 49static int ieee80211_set_tim(struct ieee80211_node *, int); 50 51MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state"); 52 53void 54ieee80211_power_attach(struct ieee80211com *ic) 55{ 56} 57 58void 59ieee80211_power_detach(struct ieee80211com *ic) 60{ 61} 62 63void 64ieee80211_power_vattach(struct ieee80211vap *vap) 65{ 66 if (vap->iv_opmode == IEEE80211_M_HOSTAP || 67 vap->iv_opmode == IEEE80211_M_IBSS) { 68 /* NB: driver should override */ 69 vap->iv_update_ps = ieee80211_update_ps; 70 vap->iv_set_tim = ieee80211_set_tim; 71 } 72} 73 74void 75ieee80211_power_latevattach(struct ieee80211vap *vap) 76{ 77 /* 78 * Allocate these only if needed. Beware that we 79 * know adhoc mode doesn't support ATIM yet... 80 */ 81 if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 82 vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t); 83 MALLOC(vap->iv_tim_bitmap, uint8_t *, vap->iv_tim_len, 84 M_80211_POWER, M_NOWAIT | M_ZERO); 85 if (vap->iv_tim_bitmap == NULL) { 86 printf("%s: no memory for TIM bitmap!\n", __func__); 87 /* XXX good enough to keep from crashing? */ 88 vap->iv_tim_len = 0; 89 } 90 } 91} 92 93void 94ieee80211_power_vdetach(struct ieee80211vap *vap) 95{ 96 if (vap->iv_tim_bitmap != NULL) { 97 FREE(vap->iv_tim_bitmap, M_80211_POWER); 98 vap->iv_tim_bitmap = NULL; 99 } 100} 101 102/* 103 * Clear any frames queued on a node's power save queue. 104 * The number of frames that were present is returned. 105 */ 106int 107ieee80211_node_saveq_drain(struct ieee80211_node *ni) 108{ 109 int qlen; 110 111 IEEE80211_NODE_SAVEQ_LOCK(ni); 112 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 113 _IF_DRAIN(&ni->ni_savedq); 114 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 115 116 return qlen; 117} 118 119/* 120 * Age frames on the power save queue. The aging interval is 121 * 4 times the listen interval specified by the station. This 122 * number is factored into the age calculations when the frame 123 * is placed on the queue. We store ages as time differences 124 * so we can check and/or adjust only the head of the list. 125 * If a frame's age exceeds the threshold then discard it. 126 * The number of frames discarded is returned so the caller 127 * can check if it needs to adjust the tim. 128 */ 129int 130ieee80211_node_saveq_age(struct ieee80211_node *ni) 131{ 132 int discard = 0; 133 134 if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { 135#ifdef IEEE80211_DEBUG 136 struct ieee80211vap *vap = ni->ni_vap; 137#endif 138 struct mbuf *m; 139 140 IEEE80211_NODE_SAVEQ_LOCK(ni); 141 while (IF_POLL(&ni->ni_savedq, m) != NULL && 142 M_AGE_GET(m) < IEEE80211_INACT_WAIT) { 143 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 144 "discard frame, age %u", M_AGE_GET(m)); 145 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 146 m_freem(m); 147 discard++; 148 } 149 if (m != NULL) 150 M_AGE_SUB(m, IEEE80211_INACT_WAIT); 151 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 152 153 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 154 "discard %u frames for age", discard); 155 IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); 156 } 157 return discard; 158} 159 160/* 161 * Handle a change in the PS station occupancy. 162 */ 163static void 164ieee80211_update_ps(struct ieee80211vap *vap, int nsta) 165{ 166 167 KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP || 168 vap->iv_opmode == IEEE80211_M_IBSS, 169 ("operating mode %u", vap->iv_opmode)); 170} 171 172/* 173 * Indicate whether there are frames queued for a station in power-save mode. 174 */ 175static int 176ieee80211_set_tim(struct ieee80211_node *ni, int set) 177{ 178 struct ieee80211vap *vap = ni->ni_vap; 179 struct ieee80211com *ic = ni->ni_ic; 180 uint16_t aid; 181 int changed; 182 183 KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP || 184 vap->iv_opmode == IEEE80211_M_IBSS, 185 ("operating mode %u", vap->iv_opmode)); 186 187 aid = IEEE80211_AID(ni->ni_associd); 188 KASSERT(aid < vap->iv_max_aid, 189 ("bogus aid %u, max %u", aid, vap->iv_max_aid)); 190 191 IEEE80211_LOCK(ic); 192 changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0)); 193 if (changed) { 194 if (set) { 195 setbit(vap->iv_tim_bitmap, aid); 196 vap->iv_ps_pending++; 197 } else { 198 clrbit(vap->iv_tim_bitmap, aid); 199 vap->iv_ps_pending--; 200 } 201 /* NB: we know vap is in RUN state so no need to check */ 202 vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM); 203 } 204 IEEE80211_UNLOCK(ic); 205 206 return changed; 207} 208 209/* 210 * Save an outbound packet for a node in power-save sleep state. 211 * The new packet is placed on the node's saved queue, and the TIM 212 * is changed, if necessary. 213 */ 214void 215ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) 216{ 217 struct ieee80211vap *vap = ni->ni_vap; 218 struct ieee80211com *ic = ni->ni_ic; 219 int qlen, age; 220 221 IEEE80211_NODE_SAVEQ_LOCK(ni); 222 if (_IF_QFULL(&ni->ni_savedq)) { 223 _IF_DROP(&ni->ni_savedq); 224 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 225 IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, 226 "pwr save q overflow, drops %d (size %d)", 227 ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); 228#ifdef IEEE80211_DEBUG 229 if (ieee80211_msg_dumppkts(vap)) 230 ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t), 231 m->m_len, -1, -1); 232#endif 233 m_freem(m); 234 return; 235 } 236 /* 237 * Tag the frame with it's expiry time and insert 238 * it in the queue. The aging interval is 4 times 239 * the listen interval specified by the station. 240 * Frames that sit around too long are reclaimed 241 * using this information. 242 */ 243 /* TU -> secs. XXX handle overflow? */ 244 age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; 245 _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); 246 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 247 248 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 249 "save frame with age %d, %u now queued", age, qlen); 250 251 if (qlen == 1 && vap->iv_set_tim != NULL) 252 vap->iv_set_tim(ni, 1); 253} 254 255/* 256 * Unload the frames from the ps q but don't send them 257 * to the driver yet. We do this in two stages to minimize 258 * locking but also because there's no easy way to preserve 259 * ordering given the existing ifnet access mechanisms. 260 * XXX could be optimized 261 */ 262static void 263pwrsave_flushq(struct ieee80211_node *ni) 264{ 265 struct mbuf *m, *mhead, *mtail; 266 int mcount; 267 268 IEEE80211_NODE_SAVEQ_LOCK(ni); 269 mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); 270 mhead = mtail = NULL; 271 for (;;) { 272 _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); 273 if (m == NULL) 274 break; 275 if (mhead == NULL) { 276 mhead = m; 277 m->m_nextpkt = NULL; 278 } else 279 mtail->m_nextpkt = m; 280 mtail = m; 281 } 282 IEEE80211_NODE_SAVEQ_UNLOCK(ni); 283 if (mhead != NULL) { 284 /* XXX need different driver interface */ 285 /* XXX bypasses q max and OACTIVE */ 286 struct ifnet *ifp = ni->ni_vap->iv_ifp; 287 IF_PREPEND_LIST(&ifp->if_snd, mhead, mtail, mcount); 288 if_start(ifp); 289 } 290} 291 292/* 293 * Handle station power-save state change. 294 */ 295void 296ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) 297{ 298 struct ieee80211vap *vap = ni->ni_vap; 299 int update; 300 301 update = 0; 302 if (enable) { 303 if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { 304 vap->iv_ps_sta++; 305 update = 1; 306 } 307 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 308 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 309 "power save mode on, %u sta's in ps mode", vap->iv_ps_sta); 310 311 if (update) 312 vap->iv_update_ps(vap, vap->iv_ps_sta); 313 } else { 314 if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { 315 vap->iv_ps_sta--; 316 update = 1; 317 } 318 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 319 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 320 "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); 321 322 /* NB: order here is intentional so TIM is clear before flush */ 323 if (vap->iv_set_tim != NULL) 324 vap->iv_set_tim(ni, 0); 325 if (update) { 326 /* NB if no sta's in ps, driver should flush mc q */ 327 vap->iv_update_ps(vap, vap->iv_ps_sta); 328 } 329 pwrsave_flushq(ni); 330 } 331} 332 333/* 334 * Handle power-save state change in station mode. 335 */ 336void 337ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable) 338{ 339 struct ieee80211_node *ni = vap->iv_bss; 340 int qlen; 341 342 if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) 343 return; 344 345 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 346 "sta power save mode %s", enable ? "on" : "off"); 347 if (!enable) { 348 ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 349 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 350 /* 351 * Flush any queued frames; we can do this immediately 352 * because we know they'll be queued behind the null 353 * data frame we send the ap. 354 * XXX can we use a data frame to take us out of ps? 355 */ 356 qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); 357 if (qlen != 0) { 358 IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 359 "flush ps queue, %u packets queued", qlen); 360 pwrsave_flushq(ni); 361 } 362 } else { 363 ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 364 ieee80211_send_nulldata(ieee80211_ref_node(ni)); 365 } 366} 367