ieee80211_tdma.c revision 186904
1/*- 2 * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting 3 * Copyright (c) 2007-2009 Intel Corporation 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28#ifdef __FreeBSD__ 29__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_tdma.c 186904 2009-01-08 17:12:47Z sam $"); 30#endif 31 32/* 33 * IEEE 802.11 TDMA mode support. 34 */ 35#include "opt_inet.h" 36#include "opt_wlan.h" 37 38#ifdef IEEE80211_SUPPORT_TDMA 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/mbuf.h> 42#include <sys/malloc.h> 43#include <sys/kernel.h> 44 45#include <sys/socket.h> 46#include <sys/sockio.h> 47#include <sys/endian.h> 48#include <sys/errno.h> 49#include <sys/proc.h> 50#include <sys/sysctl.h> 51 52#include <net/if.h> 53#include <net/if_media.h> 54#include <net/if_llc.h> 55#include <net/ethernet.h> 56 57#include <net/bpf.h> 58 59#include <net80211/ieee80211_var.h> 60#include <net80211/ieee80211_tdma.h> 61#include <net80211/ieee80211_input.h> 62 63#include "opt_tdma.h" 64#ifndef TDMA_SLOTLEN_DEFAULT 65#define TDMA_SLOTLEN_DEFAULT 10*1000 /* 10ms */ 66#endif 67#ifndef TDMA_SLOTCNT_DEFAULT 68#define TDMA_SLOTCNT_DEFAULT 2 /* 2x (pt-to-pt) */ 69#endif 70#ifndef TDMA_BINTVAL_DEFAULT 71#define TDMA_BINTVAL_DEFAULT 5 /* 5x ~= 100TU beacon intvl */ 72#endif 73#ifndef TDMA_TXRATE_11B_DEFAULT 74#define TDMA_TXRATE_11B_DEFAULT 2*11 75#endif 76#ifndef TDMA_TXRATE_11G_DEFAULT 77#define TDMA_TXRATE_11G_DEFAULT 2*24 78#endif 79#ifndef TDMA_TXRATE_11A_DEFAULT 80#define TDMA_TXRATE_11A_DEFAULT 2*24 81#endif 82 83static void tdma_vdetach(struct ieee80211vap *vap); 84static int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int); 85static void tdma_beacon_miss(struct ieee80211vap *vap); 86static void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *, 87 int subtype, int rssi, int noise, uint32_t rstamp); 88static int tdma_update(struct ieee80211vap *vap, 89 const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni, 90 int pickslot); 91static int tdma_process_params(struct ieee80211_node *ni, 92 const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh); 93 94static void 95setackpolicy(struct ieee80211com *ic, int noack) 96{ 97 struct ieee80211_wme_state *wme = &ic->ic_wme; 98 int ac; 99 100 for (ac = 0; ac < WME_NUM_AC; ac++) { 101 wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 102 wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 103 } 104} 105 106void 107ieee80211_tdma_vattach(struct ieee80211vap *vap) 108{ 109 struct ieee80211_tdma_state *ts; 110 111 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 112 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 113 114 ts = (struct ieee80211_tdma_state *) malloc( 115 sizeof(struct ieee80211_tdma_state), M_80211_VAP, M_NOWAIT | M_ZERO); 116 if (ts == NULL) { 117 printf("%s: cannot allocate TDMA state block\n", __func__); 118 /* NB: fall back to adhdemo mode */ 119 vap->iv_caps &= ~IEEE80211_C_TDMA; 120 return; 121 } 122 /* NB: default configuration is passive so no beacons */ 123 ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT; 124 ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT; 125 ts->tdma_bintval = TDMA_BINTVAL_DEFAULT; 126 ts->tdma_slot = 1; /* passive operation */ 127 128 /* setup default fixed rates */ 129 vap->iv_txparms[IEEE80211_MODE_11B].ucastrate = TDMA_TXRATE_11B_DEFAULT; 130 vap->iv_txparms[IEEE80211_MODE_11B].mcastrate = TDMA_TXRATE_11B_DEFAULT; 131 vap->iv_txparms[IEEE80211_MODE_11G].ucastrate = TDMA_TXRATE_11G_DEFAULT; 132 vap->iv_txparms[IEEE80211_MODE_11G].mcastrate = TDMA_TXRATE_11G_DEFAULT; 133 vap->iv_txparms[IEEE80211_MODE_11A].ucastrate = TDMA_TXRATE_11A_DEFAULT; 134 vap->iv_txparms[IEEE80211_MODE_11A].mcastrate = TDMA_TXRATE_11A_DEFAULT; 135 136 setackpolicy(vap->iv_ic, 1); /* disable ACK's */ 137 138 ts->tdma_opdetach = vap->iv_opdetach; 139 vap->iv_opdetach = tdma_vdetach; 140 ts->tdma_newstate = vap->iv_newstate; 141 vap->iv_newstate = tdma_newstate; 142 vap->iv_bmiss = tdma_beacon_miss; 143 ts->tdma_recv_mgmt = vap->iv_recv_mgmt; 144 vap->iv_recv_mgmt = tdma_recv_mgmt; 145 146 vap->iv_tdma = ts; 147} 148 149static void 150tdma_vdetach(struct ieee80211vap *vap) 151{ 152 struct ieee80211_tdma_state *ts = vap->iv_tdma; 153 154 ts->tdma_opdetach(vap); 155 free(vap->iv_tdma, M_80211_VAP); 156 157 setackpolicy(vap->iv_ic, 0); /* enable ACK's */ 158} 159 160/* 161 * TDMA state machine handler. 162 */ 163static int 164tdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 165{ 166 struct ieee80211_tdma_state *ts = vap->iv_tdma; 167 enum ieee80211_state ostate; 168 int status; 169 170 IEEE80211_LOCK_ASSERT(vap->iv_ic); 171 172 ostate = vap->iv_state; 173 IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 174 __func__, ieee80211_state_name[ostate], 175 ieee80211_state_name[nstate], arg); 176 177 if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 178 callout_stop(&vap->iv_swbmiss); 179 if (nstate == IEEE80211_S_SCAN && 180 (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) && 181 ts->tdma_slot != 0) { 182 /* 183 * Override adhoc behaviour when operating as a slave; 184 * we need to scan even if the channel is locked. 185 */ 186 vap->iv_state = nstate; /* state transition */ 187 ieee80211_cancel_scan(vap); /* background scan */ 188 if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 189 ieee80211_check_scan(vap, 190 vap->iv_scanreq_flags, 191 vap->iv_scanreq_duration, 192 vap->iv_scanreq_mindwell, 193 vap->iv_scanreq_maxdwell, 194 vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 195 vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 196 } else 197 ieee80211_check_scan_current(vap); 198 status = 0; 199 } else { 200 status = ts->tdma_newstate(vap, nstate, arg); 201 } 202 if (status == 0 && 203 nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN && 204 (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) && 205 ts->tdma_slot != 0) { 206 /* 207 * Start s/w beacon miss timer for slave devices w/o 208 * hardware support. The 2x is a fudge for our doing 209 * this in software. 210 */ 211 vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 212 2 * vap->iv_bmissthreshold * ts->tdma_bintval * 213 ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024)); 214 vap->iv_swbmiss_count = 0; 215 callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 216 ieee80211_swbmiss, vap); 217 } 218 return status; 219} 220 221static void 222tdma_beacon_miss(struct ieee80211vap *vap) 223{ 224 struct ieee80211_tdma_state *ts = vap->iv_tdma; 225 226 KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 227 KASSERT(vap->iv_state == IEEE80211_S_RUN, 228 ("wrong state %d", vap->iv_state)); 229 230 IEEE80211_DPRINTF(vap, 231 IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG, 232 "beacon miss, mode %u state %s\n", 233 vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 234 235 if (ts->tdma_peer != NULL) { /* XXX? can this be null? */ 236 ieee80211_notify_node_leave(vap->iv_bss); 237 ts->tdma_peer = NULL; 238 /* 239 * Treat beacon miss like an associate failure wrt the 240 * scan policy; this forces the entry in the scan cache 241 * to be ignored after several tries. 242 */ 243 ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, 244 IEEE80211_STATUS_TIMEOUT); 245 } 246#if 0 247 ts->tdma_inuse = 0; /* clear slot usage */ 248#endif 249 ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 250} 251 252static void 253tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 254 int subtype, int rssi, int noise, uint32_t rstamp) 255{ 256 struct ieee80211com *ic = ni->ni_ic; 257 struct ieee80211vap *vap = ni->ni_vap; 258 struct ieee80211_tdma_state *ts = vap->iv_tdma; 259 260 if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && 261 (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 262 struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); 263 struct ieee80211_scanparams scan; 264 265 if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 266 return; 267 if (scan.tdma == NULL) { 268 /* 269 * TDMA stations must beacon a TDMA ie; ignore 270 * any other station. 271 * XXX detect overlapping bss and change channel 272 */ 273 IEEE80211_DISCARD(vap, 274 IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 275 wh, ieee80211_mgt_subtype_name[subtype >> 276 IEEE80211_FC0_SUBTYPE_SHIFT], 277 "%s", "no TDMA ie"); 278 vap->iv_stats.is_rx_mgtdiscard++; 279 return; 280 } 281 if (ni == vap->iv_bss && 282 !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 283 /* 284 * Fake up a node for this newly 285 * discovered member of the IBSS. 286 */ 287 ni = ieee80211_add_neighbor(vap, wh, &scan); 288 if (ni == NULL) { 289 /* NB: stat kept for alloc failure */ 290 return; 291 } 292 } 293 /* 294 * Check for state updates. 295 */ 296 if (IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { 297 /* 298 * Count frame now that we know it's to be processed. 299 */ 300 vap->iv_stats.is_rx_beacon++; 301 IEEE80211_NODE_STAT(ni, rx_beacons); 302 /* 303 * Record tsf of last beacon. NB: this must be 304 * done before calling tdma_process_params 305 * as deeper routines reference it. 306 */ 307 memcpy(&ni->ni_tstamp.data, scan.tstamp, 308 sizeof(ni->ni_tstamp.data)); 309 /* 310 * Count beacon frame for s/w bmiss handling. 311 */ 312 vap->iv_swbmiss_count++; 313 vap->iv_bmiss_count = 0; 314 /* 315 * Process tdma ie. The contents are used to sync 316 * the slot timing, reconfigure the bss, etc. 317 */ 318 (void) tdma_process_params(ni, scan.tdma, rstamp, wh); 319 return; 320 } 321 /* 322 * NB: defer remaining work to the adhoc code; this causes 323 * 2x parsing of the frame but should happen infrequently 324 */ 325 } 326 ts->tdma_recv_mgmt(ni, m0, subtype, rssi, noise, rstamp); 327} 328 329/* 330 * Update TDMA state on receipt of a beacon frame with 331 * a TDMA information element. The sender's identity 332 * is provided so we can track who our peer is. If pickslot 333 * is non-zero we scan the slot allocation state in the ie 334 * locate a free slot for our use. 335 */ 336static int 337tdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma, 338 struct ieee80211_node *ni, int pickslot) 339{ 340 struct ieee80211_tdma_state *ts = vap->iv_tdma; 341 int slotlen, slotcnt, slot, bintval; 342 343 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 344 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 345 346 slotlen = le16toh(tdma->tdma_slotlen); 347 slotcnt = tdma->tdma_slotcnt; 348 bintval = tdma->tdma_bintval; 349 350 /* XXX rate-limit printf's */ 351 if (!(2 <= slotcnt && slotcnt <= IEEE80211_TDMA_MAXSLOTS)) { 352 printf("%s: bogus slot cnt %u\n", __func__, slotcnt); 353 return 0; 354 } 355 /* XXX magic constants */ 356 if (slotlen < 2 || slotlen > (0xfffff/100)) { 357 printf("%s: bogus slot len %u\n", __func__, slotlen); 358 return 0; 359 } 360 if (bintval < 1) { 361 printf("%s: bogus beacon interval %u\n", __func__, bintval); 362 return 0; 363 } 364 if (pickslot) { 365 /* 366 * Pick unoccupied slot. Note we never choose slot 0. 367 */ 368 for (slot = slotcnt-1; slot > 0; slot--) 369 if (isclr(tdma->tdma_inuse, slot)) 370 break; 371 if (slot <= 0) { 372 printf("%s: no free slot, slotcnt %u inuse: 0x%x\n", 373 __func__, slotcnt, tdma->tdma_inuse[0]); 374 /* XXX need to do something better */ 375 return 0; 376 } 377 } else 378 slot = ts->tdma_slot; 379 380 if (slotcnt != ts->tdma_slotcnt || 381 100*slotlen != ts->tdma_slotlen || 382 bintval != ts->tdma_bintval || 383 slot != ts->tdma_slot || 384 ts->tdma_peer != ni) { 385 /* 386 * New/changed parameters; update runtime state. 387 */ 388 /* XXX overwrites user parameters */ 389 ts->tdma_slotcnt = slotcnt; 390 ts->tdma_slotlen = 100*slotlen; 391 ts->tdma_slot = slot; 392 ts->tdma_bintval = bintval; 393 /* mark beacon to be updated before next xmit */ 394 ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 395 396 IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 397 "%s: slot %u slotcnt %u slotlen %u us bintval %u\n", 398 __func__, slot, slotcnt, 100*slotlen, tdma->tdma_bintval); 399 } 400 /* 401 * Notify driver. Note we can be called before 402 * entering RUN state if we scanned and are 403 * joining an existing bss. In that case do not 404 * call the driver because not all necessary state 405 * has been setup. The next beacon will dtrt. 406 */ 407 if (vap->iv_state == IEEE80211_S_RUN) 408 vap->iv_ic->ic_tdma_update(ni, tdma); 409 /* 410 * Dispatch join event on first beacon from new master. 411 */ 412 if (ts->tdma_peer != ni) { 413 if (ts->tdma_peer != NULL) 414 ieee80211_notify_node_leave(vap->iv_bss); 415 ieee80211_notify_node_join(ni, 1); 416 /* NB: no reference, we just use the address */ 417 ts->tdma_peer = ni; 418 } 419 return 1; 420} 421 422/* 423 * Process received TDMA parameters. 424 */ 425static int 426tdma_process_params(struct ieee80211_node *ni, 427 const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh) 428{ 429 struct ieee80211vap *vap = ni->ni_vap; 430 struct ieee80211_tdma_state *ts = vap->iv_tdma; 431 const struct ieee80211_tdma_param *tdma = 432 (const struct ieee80211_tdma_param *) ie; 433 u_int len = ie[1]; 434 435 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 436 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 437 438 if (len < sizeof(*tdma) - 2) { 439 IEEE80211_DISCARD_IE(vap, 440 IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 441 wh, "tdma", "too short, len %u", len); 442 return IEEE80211_REASON_IE_INVALID; 443 } 444 if (tdma->tdma_version != TDMA_VERSION) { 445 IEEE80211_DISCARD_IE(vap, 446 IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 447 wh, "tdma", "bad version %u", tdma->tdma_version); 448 return IEEE80211_REASON_IE_INVALID; 449 } 450 /* 451 * Can reach here while scanning, update 452 * operational state only in RUN state. 453 */ 454 if (vap->iv_state == IEEE80211_S_RUN) { 455 if (tdma->tdma_slot != ts->tdma_slot && 456 isclr(ts->tdma_inuse, tdma->tdma_slot)) { 457 IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni, 458 "discovered in slot %u", tdma->tdma_slot); 459 setbit(ts->tdma_inuse, tdma->tdma_slot); 460 /* XXX dispatch event only when operating as master */ 461 if (ts->tdma_slot == 0) 462 ieee80211_notify_node_join(ni, 1); 463 } 464 setbit(ts->tdma_active, tdma->tdma_slot); 465 if (tdma->tdma_slot == ts->tdma_slot-1) { 466 /* 467 * Slave tsf synchronization to station 468 * just before us in the schedule. The driver 469 * is responsible for copying the timestamp 470 * of the received beacon into our beacon 471 * frame so the sender can calculate round 472 * trip time. We cannot do that here because 473 * we don't know how to update our beacon frame. 474 */ 475 (void) tdma_update(vap, tdma, ni, 0); 476 /* XXX reschedule swbmiss timer on parameter change */ 477 } else if (tdma->tdma_slot == ts->tdma_slot+1) { 478 uint64_t tstamp; 479 int32_t rtt; 480 /* 481 * Use returned timstamp to calculate the 482 * roundtrip time. 483 */ 484 memcpy(&tstamp, tdma->tdma_tstamp, 8); 485 /* XXX use only 15 bits of rstamp */ 486 rtt = rstamp - (le64toh(tstamp) & 0x7fff); 487 if (rtt < 0) 488 rtt += 0x7fff; 489 /* XXX hack to quiet normal use */ 490 IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X, 491 "tdma rtt %5u [rstamp %5u tstamp %llu]\n", 492 rtt, rstamp, 493 (unsigned long long) le64toh(tstamp)); 494 } else if (tdma->tdma_slot == ts->tdma_slot && 495 le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) { 496 /* 497 * Station using the same slot as us and has 498 * been around longer than us; we must move. 499 * Note this can happen if stations do not 500 * see each other while scanning. 501 */ 502 IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 503 "slot %u collision rxtsf %llu tsf %llu\n", 504 tdma->tdma_slot, 505 (unsigned long long) le64toh(ni->ni_tstamp.tsf), 506 vap->iv_bss->ni_tstamp.tsf); 507 setbit(ts->tdma_inuse, tdma->tdma_slot); 508 509 (void) tdma_update(vap, tdma, ni, 1); 510 } 511 } 512 return 0; 513} 514 515int 516ieee80211_tdma_getslot(struct ieee80211vap *vap) 517{ 518 struct ieee80211_tdma_state *ts = vap->iv_tdma; 519 520 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 521 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 522 return ts->tdma_slot; 523} 524 525/* 526 * Parse a TDMA ie on station join and use it to setup node state. 527 */ 528void 529ieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie) 530{ 531 struct ieee80211vap *vap = ni->ni_vap; 532 533 if (vap->iv_caps & IEEE80211_C_TDMA) { 534 const struct ieee80211_tdma_param *tdma = 535 (const struct ieee80211_tdma_param *)ie; 536 struct ieee80211_tdma_state *ts = vap->iv_tdma; 537 /* 538 * Adopt TDMA configuration when joining an 539 * existing network. 540 */ 541 setbit(ts->tdma_inuse, tdma->tdma_slot); 542 (void) tdma_update(vap, tdma, ni, 1); 543 /* 544 * Propagate capabilities based on the local 545 * configuration and the remote station's advertised 546 * capabilities. In particular this permits us to 547 * enable use of QoS to disable ACK's. 548 */ 549 if ((vap->iv_flags & IEEE80211_F_WME) && 550 ni->ni_ies.wme_ie != NULL) 551 ni->ni_flags |= IEEE80211_NODE_QOS; 552 } 553} 554 555#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f 556/* 557 * Add a TDMA parameters element to a frame. 558 */ 559uint8_t * 560ieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap) 561{ 562#define ADDSHORT(frm, v) do { \ 563 frm[0] = (v) & 0xff; \ 564 frm[1] = (v) >> 8; \ 565 frm += 2; \ 566} while (0) 567 static const struct ieee80211_tdma_param param = { 568 .tdma_id = IEEE80211_ELEMID_VENDOR, 569 .tdma_len = sizeof(struct ieee80211_tdma_param) - 2, 570 .tdma_oui = { TDMA_OUI_BYTES }, 571 .tdma_type = TDMA_OUI_TYPE, 572 .tdma_subtype = TDMA_SUBTYPE_PARAM, 573 .tdma_version = TDMA_VERSION, 574 }; 575 const struct ieee80211_tdma_state *tdma = vap->iv_tdma; 576 uint16_t slotlen; 577 578 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 579 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 580 581 memcpy(frm, ¶m, sizeof(param)); 582 frm += __offsetof(struct ieee80211_tdma_param, tdma_slot); 583 *frm++ = tdma->tdma_slot; 584 *frm++ = tdma->tdma_slotcnt; 585 /* NB: convert units to fit in 16-bits */ 586 slotlen = tdma->tdma_slotlen / 100; /* 100us units */ 587 ADDSHORT(frm, slotlen); 588 *frm++ = tdma->tdma_bintval; 589 *frm++ = tdma->tdma_inuse[0]; 590 frm += 10; /* pad+timestamp */ 591 return frm; 592#undef ADDSHORT 593} 594#undef TDMA_OUI_BYTES 595 596/* 597 * Update TDMA state at TBTT. 598 */ 599void 600ieee80211_tdma_update_beacon(struct ieee80211vap *vap, 601 struct ieee80211_beacon_offsets *bo) 602{ 603 struct ieee80211_tdma_state *ts = vap->iv_tdma; 604 605 KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 606 ("not a tdma vap, caps 0x%x", vap->iv_caps)); 607 608 if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) { 609 (void) ieee80211_add_tdma(bo->bo_tdma, vap); 610 clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 611 } 612 if (ts->tdma_slot != 0) /* only on master */ 613 return; 614 if (ts->tdma_count <= 0) { 615 /* 616 * Time to update the mask of active/inuse stations. 617 * We track stations that we've received a beacon 618 * frame from and update this mask periodically. 619 * This allows us to miss a few beacons before marking 620 * a slot free for re-use. 621 */ 622 ts->tdma_inuse[0] = ts->tdma_active[0]; 623 ts->tdma_active[0] = 0x01; 624 /* update next time 'round */ 625 /* XXX use notify framework */ 626 setbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 627 /* NB: use s/w beacon miss threshold; may be too high */ 628 ts->tdma_count = vap->iv_bmissthreshold-1; 629 } else 630 ts->tdma_count--; 631} 632 633int 634ieee80211_tdma_ioctl_get80211(struct ieee80211vap *vap, 635 struct ieee80211req *ireq) 636{ 637 struct ieee80211_tdma_state *ts = vap->iv_tdma; 638 639 if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 640 return EOPNOTSUPP; 641 642 switch (ireq->i_type) { 643 case IEEE80211_IOC_TDMA_SLOT: 644 ireq->i_val = ts->tdma_slot; 645 break; 646 case IEEE80211_IOC_TDMA_SLOTCNT: 647 ireq->i_val = ts->tdma_slotcnt; 648 break; 649 case IEEE80211_IOC_TDMA_SLOTLEN: 650 ireq->i_val = ts->tdma_slotlen; 651 break; 652 case IEEE80211_IOC_TDMA_BINTERVAL: 653 ireq->i_val = ts->tdma_bintval; 654 break; 655 default: 656 return EINVAL; 657 } 658 return 0; 659} 660 661int 662ieee80211_tdma_ioctl_set80211(struct ieee80211vap *vap, 663 struct ieee80211req *ireq) 664{ 665 struct ieee80211_tdma_state *ts = vap->iv_tdma; 666 667 if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 668 return EOPNOTSUPP; 669 670 switch (ireq->i_type) { 671 case IEEE80211_IOC_TDMA_SLOT: 672 if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt)) 673 return EINVAL; 674 if (ireq->i_val != ts->tdma_slot) { 675 ts->tdma_slot = ireq->i_val; 676 return ERESTART; 677 } 678 break; 679 case IEEE80211_IOC_TDMA_SLOTCNT: 680 if (!(2 <= ireq->i_val && 681 ireq->i_val <= IEEE80211_TDMA_MAXSLOTS)) 682 return EINVAL; 683 if (ireq->i_val != ts->tdma_slotcnt) { 684 ts->tdma_slotcnt = ireq->i_val; 685 return ERESTART; 686 } 687 break; 688 case IEEE80211_IOC_TDMA_SLOTLEN: 689 /* 690 * XXX 691 * 150 insures at least 1/8 TU 692 * 0xfffff is the max duration for bursting 693 * (implict by way of 16-bit data type for i_val) 694 */ 695 if (ireq->i_val < 150) 696 return EINVAL; 697 if (ireq->i_val != ts->tdma_slotlen) { 698 ts->tdma_slotlen = ireq->i_val; 699 return ERESTART; 700 } 701 break; 702 case IEEE80211_IOC_TDMA_BINTERVAL: 703 if (ireq->i_val < 1) 704 return EINVAL; 705 if (ireq->i_val != ts->tdma_bintval) { 706 ts->tdma_bintval = ireq->i_val; 707 return ERESTART; 708 } 709 break; 710 default: 711 return EINVAL; 712 } 713 return 0; 714} 715#endif /* IEEE80211_SUPPORT_TDMA */ 716