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$"); 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 46 static void ieee80211_set_tim(struct ieee80211_node *ni, int set); 47 48 void 49 ieee80211_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 58 void 59 ieee80211_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 77 void 78 ieee80211_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 */ 90 int 91 ieee80211_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 */ 113 int 114 ieee80211_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) { 124 IEEE80211_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 */ 143 static void 144 ieee80211_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; 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 */ 176 void 177 ieee80211_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 */ 220 void 221 ieee80211_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 */ 287 void 288 ieee80211_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 } 329