ieee80211_tdma.c revision 190384
1186904Ssam/*- 2186904Ssam * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting 3186904Ssam * Copyright (c) 2007-2009 Intel Corporation 4186904Ssam * All rights reserved. 5186904Ssam * 6186904Ssam * Redistribution and use in source and binary forms, with or without 7186904Ssam * modification, are permitted provided that the following conditions 8186904Ssam * are met: 9186904Ssam * 1. Redistributions of source code must retain the above copyright 10186904Ssam * notice, this list of conditions and the following disclaimer. 11186904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12186904Ssam * notice, this list of conditions and the following disclaimer in the 13186904Ssam * documentation and/or other materials provided with the distribution. 14186904Ssam * 15186904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16186904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17186904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18186904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19186904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20186904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21186904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22186904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23186904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24186904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25186904Ssam */ 26186904Ssam 27186904Ssam#include <sys/cdefs.h> 28186904Ssam#ifdef __FreeBSD__ 29186904Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_tdma.c 190384 2009-03-24 17:57:48Z sam $"); 30186904Ssam#endif 31186904Ssam 32186904Ssam/* 33186904Ssam * IEEE 802.11 TDMA mode support. 34186904Ssam */ 35186904Ssam#include "opt_inet.h" 36186904Ssam#include "opt_wlan.h" 37186904Ssam 38186904Ssam#include <sys/param.h> 39186904Ssam#include <sys/systm.h> 40186904Ssam#include <sys/mbuf.h> 41186904Ssam#include <sys/malloc.h> 42186904Ssam#include <sys/kernel.h> 43186904Ssam 44186904Ssam#include <sys/socket.h> 45186904Ssam#include <sys/sockio.h> 46186904Ssam#include <sys/endian.h> 47186904Ssam#include <sys/errno.h> 48186904Ssam#include <sys/proc.h> 49186904Ssam#include <sys/sysctl.h> 50186904Ssam 51186904Ssam#include <net/if.h> 52186904Ssam#include <net/if_media.h> 53186904Ssam#include <net/if_llc.h> 54186904Ssam#include <net/ethernet.h> 55186904Ssam 56186904Ssam#include <net/bpf.h> 57186904Ssam 58186904Ssam#include <net80211/ieee80211_var.h> 59186904Ssam#include <net80211/ieee80211_tdma.h> 60186904Ssam#include <net80211/ieee80211_input.h> 61186904Ssam 62186904Ssam#include "opt_tdma.h" 63186904Ssam#ifndef TDMA_SLOTLEN_DEFAULT 64186904Ssam#define TDMA_SLOTLEN_DEFAULT 10*1000 /* 10ms */ 65186904Ssam#endif 66186904Ssam#ifndef TDMA_SLOTCNT_DEFAULT 67186904Ssam#define TDMA_SLOTCNT_DEFAULT 2 /* 2x (pt-to-pt) */ 68186904Ssam#endif 69186904Ssam#ifndef TDMA_BINTVAL_DEFAULT 70186904Ssam#define TDMA_BINTVAL_DEFAULT 5 /* 5x ~= 100TU beacon intvl */ 71186904Ssam#endif 72186904Ssam#ifndef TDMA_TXRATE_11B_DEFAULT 73186904Ssam#define TDMA_TXRATE_11B_DEFAULT 2*11 74186904Ssam#endif 75186904Ssam#ifndef TDMA_TXRATE_11G_DEFAULT 76186904Ssam#define TDMA_TXRATE_11G_DEFAULT 2*24 77186904Ssam#endif 78186904Ssam#ifndef TDMA_TXRATE_11A_DEFAULT 79186904Ssam#define TDMA_TXRATE_11A_DEFAULT 2*24 80186904Ssam#endif 81187899Ssam#ifndef TDMA_TXRATE_STURBO_A_DEFAULT 82187899Ssam#define TDMA_TXRATE_STURBO_A_DEFAULT 2*24 83187899Ssam#endif 84188782Ssam#ifndef TDMA_TXRATE_HALF_DEFAULT 85188782Ssam#define TDMA_TXRATE_HALF_DEFAULT 2*12 86188782Ssam#endif 87188782Ssam#ifndef TDMA_TXRATE_QUARTER_DEFAULT 88188782Ssam#define TDMA_TXRATE_QUARTER_DEFAULT 2*6 89188782Ssam#endif 90187899Ssam#ifndef TDMA_TXRATE_11NA_DEFAULT 91187899Ssam#define TDMA_TXRATE_11NA_DEFAULT (4 | IEEE80211_RATE_MCS) 92187899Ssam#endif 93187899Ssam#ifndef TDMA_TXRATE_11NG_DEFAULT 94187899Ssam#define TDMA_TXRATE_11NG_DEFAULT (4 | IEEE80211_RATE_MCS) 95187899Ssam#endif 96186904Ssam 97186904Ssamstatic void tdma_vdetach(struct ieee80211vap *vap); 98186904Ssamstatic int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int); 99186904Ssamstatic void tdma_beacon_miss(struct ieee80211vap *vap); 100186904Ssamstatic void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *, 101186904Ssam int subtype, int rssi, int noise, uint32_t rstamp); 102186904Ssamstatic int tdma_update(struct ieee80211vap *vap, 103186904Ssam const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni, 104186904Ssam int pickslot); 105186904Ssamstatic int tdma_process_params(struct ieee80211_node *ni, 106186904Ssam const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh); 107186904Ssam 108186904Ssamstatic void 109187899Ssamsettxparms(struct ieee80211vap *vap, enum ieee80211_phymode mode, int rate) 110187899Ssam{ 111187899Ssam vap->iv_txparms[mode].ucastrate = rate; 112187899Ssam vap->iv_txparms[mode].mcastrate = rate; 113187899Ssam} 114187899Ssam 115187899Ssamstatic void 116186904Ssamsetackpolicy(struct ieee80211com *ic, int noack) 117186904Ssam{ 118186904Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 119186904Ssam int ac; 120186904Ssam 121186904Ssam for (ac = 0; ac < WME_NUM_AC; ac++) { 122186904Ssam wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 123186904Ssam wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 124186904Ssam } 125186904Ssam} 126186904Ssam 127186904Ssamvoid 128186904Ssamieee80211_tdma_vattach(struct ieee80211vap *vap) 129186904Ssam{ 130186904Ssam struct ieee80211_tdma_state *ts; 131186904Ssam 132186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 133186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 134186904Ssam 135186904Ssam ts = (struct ieee80211_tdma_state *) malloc( 136186904Ssam sizeof(struct ieee80211_tdma_state), M_80211_VAP, M_NOWAIT | M_ZERO); 137186904Ssam if (ts == NULL) { 138186904Ssam printf("%s: cannot allocate TDMA state block\n", __func__); 139186904Ssam /* NB: fall back to adhdemo mode */ 140186904Ssam vap->iv_caps &= ~IEEE80211_C_TDMA; 141186904Ssam return; 142186904Ssam } 143186904Ssam /* NB: default configuration is passive so no beacons */ 144189980Ssam ts->tdma_version = TDMA_VERSION; 145186904Ssam ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT; 146186904Ssam ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT; 147186904Ssam ts->tdma_bintval = TDMA_BINTVAL_DEFAULT; 148186904Ssam ts->tdma_slot = 1; /* passive operation */ 149186904Ssam 150186904Ssam /* setup default fixed rates */ 151187899Ssam settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT); 152187899Ssam settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT); 153187899Ssam settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT); 154187899Ssam settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_STURBO_A_DEFAULT); 155187899Ssam settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT); 156187899Ssam settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT); 157188782Ssam settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT); 158188782Ssam settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT); 159186904Ssam 160186904Ssam setackpolicy(vap->iv_ic, 1); /* disable ACK's */ 161186904Ssam 162186904Ssam ts->tdma_opdetach = vap->iv_opdetach; 163186904Ssam vap->iv_opdetach = tdma_vdetach; 164186904Ssam ts->tdma_newstate = vap->iv_newstate; 165186904Ssam vap->iv_newstate = tdma_newstate; 166186904Ssam vap->iv_bmiss = tdma_beacon_miss; 167186904Ssam ts->tdma_recv_mgmt = vap->iv_recv_mgmt; 168186904Ssam vap->iv_recv_mgmt = tdma_recv_mgmt; 169186904Ssam 170186904Ssam vap->iv_tdma = ts; 171186904Ssam} 172186904Ssam 173186904Ssamstatic void 174186904Ssamtdma_vdetach(struct ieee80211vap *vap) 175186904Ssam{ 176186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 177186904Ssam 178186904Ssam ts->tdma_opdetach(vap); 179186904Ssam free(vap->iv_tdma, M_80211_VAP); 180186904Ssam 181186904Ssam setackpolicy(vap->iv_ic, 0); /* enable ACK's */ 182186904Ssam} 183186904Ssam 184188467Ssamstatic void 185188467Ssamsta_leave(void *arg, struct ieee80211_node *ni) 186188467Ssam{ 187188467Ssam struct ieee80211vap *vap = arg; 188188467Ssam 189188467Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 190188467Ssam ieee80211_node_leave(ni); 191188467Ssam} 192188467Ssam 193186904Ssam/* 194186904Ssam * TDMA state machine handler. 195186904Ssam */ 196186904Ssamstatic int 197186904Ssamtdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 198186904Ssam{ 199186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 200188467Ssam struct ieee80211com *ic = vap->iv_ic; 201186904Ssam enum ieee80211_state ostate; 202186904Ssam int status; 203186904Ssam 204188467Ssam IEEE80211_LOCK_ASSERT(ic); 205186904Ssam 206186904Ssam ostate = vap->iv_state; 207186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 208186904Ssam __func__, ieee80211_state_name[ostate], 209186904Ssam ieee80211_state_name[nstate], arg); 210186904Ssam 211186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 212186904Ssam callout_stop(&vap->iv_swbmiss); 213186904Ssam if (nstate == IEEE80211_S_SCAN && 214186904Ssam (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) && 215186904Ssam ts->tdma_slot != 0) { 216186904Ssam /* 217186904Ssam * Override adhoc behaviour when operating as a slave; 218186904Ssam * we need to scan even if the channel is locked. 219186904Ssam */ 220186904Ssam vap->iv_state = nstate; /* state transition */ 221186904Ssam ieee80211_cancel_scan(vap); /* background scan */ 222188467Ssam if (ostate == IEEE80211_S_RUN) { 223188467Ssam /* purge station table; entries are stale */ 224188467Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 225188467Ssam } 226186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 227186904Ssam ieee80211_check_scan(vap, 228186904Ssam vap->iv_scanreq_flags, 229186904Ssam vap->iv_scanreq_duration, 230186904Ssam vap->iv_scanreq_mindwell, 231186904Ssam vap->iv_scanreq_maxdwell, 232186904Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 233186904Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 234186904Ssam } else 235186904Ssam ieee80211_check_scan_current(vap); 236186904Ssam status = 0; 237186904Ssam } else { 238186904Ssam status = ts->tdma_newstate(vap, nstate, arg); 239186904Ssam } 240186904Ssam if (status == 0 && 241186904Ssam nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN && 242186904Ssam (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) && 243188925Ssam ts->tdma_slot != 0 && 244188925Ssam vap->iv_des_chan == IEEE80211_CHAN_ANYC) { 245186904Ssam /* 246186904Ssam * Start s/w beacon miss timer for slave devices w/o 247188925Ssam * hardware support. Note we do this only if we're 248188925Ssam * not locked to a channel (i.e. roam to follow the 249188925Ssam * master). The 2x is a fudge for our doing this in 250188925Ssam * software. 251186904Ssam */ 252186904Ssam vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 253186904Ssam 2 * vap->iv_bmissthreshold * ts->tdma_bintval * 254186904Ssam ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024)); 255186904Ssam vap->iv_swbmiss_count = 0; 256186904Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 257186904Ssam ieee80211_swbmiss, vap); 258186904Ssam } 259186904Ssam return status; 260186904Ssam} 261186904Ssam 262186904Ssamstatic void 263186904Ssamtdma_beacon_miss(struct ieee80211vap *vap) 264186904Ssam{ 265186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 266186904Ssam 267186904Ssam KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 268186904Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 269186904Ssam ("wrong state %d", vap->iv_state)); 270186904Ssam 271186904Ssam IEEE80211_DPRINTF(vap, 272186904Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG, 273186904Ssam "beacon miss, mode %u state %s\n", 274186904Ssam vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 275186904Ssam 276186904Ssam if (ts->tdma_peer != NULL) { /* XXX? can this be null? */ 277186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 278186904Ssam ts->tdma_peer = NULL; 279186904Ssam /* 280186904Ssam * Treat beacon miss like an associate failure wrt the 281186904Ssam * scan policy; this forces the entry in the scan cache 282186904Ssam * to be ignored after several tries. 283186904Ssam */ 284186904Ssam ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, 285186904Ssam IEEE80211_STATUS_TIMEOUT); 286186904Ssam } 287186904Ssam#if 0 288186904Ssam ts->tdma_inuse = 0; /* clear slot usage */ 289186904Ssam#endif 290186904Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 291186904Ssam} 292186904Ssam 293186904Ssamstatic void 294186904Ssamtdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 295186904Ssam int subtype, int rssi, int noise, uint32_t rstamp) 296186904Ssam{ 297186904Ssam struct ieee80211com *ic = ni->ni_ic; 298186904Ssam struct ieee80211vap *vap = ni->ni_vap; 299186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 300186904Ssam 301186904Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && 302186904Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 303186904Ssam struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); 304186904Ssam struct ieee80211_scanparams scan; 305186904Ssam 306186904Ssam if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 307186904Ssam return; 308186904Ssam if (scan.tdma == NULL) { 309186904Ssam /* 310186904Ssam * TDMA stations must beacon a TDMA ie; ignore 311186904Ssam * any other station. 312186904Ssam * XXX detect overlapping bss and change channel 313186904Ssam */ 314186904Ssam IEEE80211_DISCARD(vap, 315186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 316186904Ssam wh, ieee80211_mgt_subtype_name[subtype >> 317186904Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 318186904Ssam "%s", "no TDMA ie"); 319186904Ssam vap->iv_stats.is_rx_mgtdiscard++; 320186904Ssam return; 321186904Ssam } 322186904Ssam if (ni == vap->iv_bss && 323186904Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 324186904Ssam /* 325186904Ssam * Fake up a node for this newly 326186904Ssam * discovered member of the IBSS. 327186904Ssam */ 328186904Ssam ni = ieee80211_add_neighbor(vap, wh, &scan); 329186904Ssam if (ni == NULL) { 330186904Ssam /* NB: stat kept for alloc failure */ 331186904Ssam return; 332186904Ssam } 333186904Ssam } 334186904Ssam /* 335186904Ssam * Check for state updates. 336186904Ssam */ 337186904Ssam if (IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { 338186904Ssam /* 339186904Ssam * Count frame now that we know it's to be processed. 340186904Ssam */ 341186904Ssam vap->iv_stats.is_rx_beacon++; 342186904Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 343186904Ssam /* 344186904Ssam * Record tsf of last beacon. NB: this must be 345186904Ssam * done before calling tdma_process_params 346186904Ssam * as deeper routines reference it. 347186904Ssam */ 348186904Ssam memcpy(&ni->ni_tstamp.data, scan.tstamp, 349186904Ssam sizeof(ni->ni_tstamp.data)); 350186904Ssam /* 351186904Ssam * Count beacon frame for s/w bmiss handling. 352186904Ssam */ 353186904Ssam vap->iv_swbmiss_count++; 354186904Ssam /* 355186904Ssam * Process tdma ie. The contents are used to sync 356186904Ssam * the slot timing, reconfigure the bss, etc. 357186904Ssam */ 358186904Ssam (void) tdma_process_params(ni, scan.tdma, rstamp, wh); 359186904Ssam return; 360186904Ssam } 361186904Ssam /* 362186904Ssam * NB: defer remaining work to the adhoc code; this causes 363186904Ssam * 2x parsing of the frame but should happen infrequently 364186904Ssam */ 365186904Ssam } 366186904Ssam ts->tdma_recv_mgmt(ni, m0, subtype, rssi, noise, rstamp); 367186904Ssam} 368186904Ssam 369186904Ssam/* 370186904Ssam * Update TDMA state on receipt of a beacon frame with 371186904Ssam * a TDMA information element. The sender's identity 372186904Ssam * is provided so we can track who our peer is. If pickslot 373186904Ssam * is non-zero we scan the slot allocation state in the ie 374189980Ssam * to locate a free slot for our use. 375186904Ssam */ 376186904Ssamstatic int 377186904Ssamtdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma, 378186904Ssam struct ieee80211_node *ni, int pickslot) 379186904Ssam{ 380186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 381189980Ssam int slot, slotlen, update; 382186904Ssam 383186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 384186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 385186904Ssam 386189980Ssam update = 0; 387189980Ssam if (tdma->tdma_slotcnt != ts->tdma_slotcnt) { 388189980Ssam if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) { 389189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 390189981Ssam printf("%s: bad slot cnt %u\n", 391189981Ssam __func__, tdma->tdma_slotcnt); 392189980Ssam return 0; 393189980Ssam } 394189980Ssam update |= TDMA_UPDATE_SLOTCNT; 395189980Ssam } 396189980Ssam slotlen = le16toh(tdma->tdma_slotlen) * 100; 397189980Ssam if (slotlen != ts->tdma_slotlen) { 398189980Ssam if (!TDMA_SLOTLEN_VALID(slotlen)) { 399189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 400189981Ssam printf("%s: bad slot len %u\n", 401189981Ssam __func__, slotlen); 402189980Ssam return 0; 403189980Ssam } 404189980Ssam update |= TDMA_UPDATE_SLOTLEN; 405189980Ssam } 406189980Ssam if (tdma->tdma_bintval != ts->tdma_bintval) { 407189980Ssam if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) { 408189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 409189981Ssam printf("%s: bad beacon interval %u\n", 410189981Ssam __func__, tdma->tdma_bintval); 411189980Ssam return 0; 412189980Ssam } 413189980Ssam update |= TDMA_UPDATE_BINTVAL; 414189980Ssam } 415189980Ssam slot = ts->tdma_slot; 416186904Ssam if (pickslot) { 417186904Ssam /* 418186904Ssam * Pick unoccupied slot. Note we never choose slot 0. 419186904Ssam */ 420189980Ssam for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--) 421186904Ssam if (isclr(tdma->tdma_inuse, slot)) 422186904Ssam break; 423186904Ssam if (slot <= 0) { 424186904Ssam printf("%s: no free slot, slotcnt %u inuse: 0x%x\n", 425189980Ssam __func__, tdma->tdma_slotcnt, 426189980Ssam tdma->tdma_inuse[0]); 427186904Ssam /* XXX need to do something better */ 428186904Ssam return 0; 429186904Ssam } 430189980Ssam if (slot != ts->tdma_slot) 431189980Ssam update |= TDMA_UPDATE_SLOT; 432189980Ssam } 433189980Ssam if (ni != ts->tdma_peer) { 434189980Ssam /* update everything */ 435189980Ssam update = TDMA_UPDATE_SLOT 436189980Ssam | TDMA_UPDATE_SLOTCNT 437189980Ssam | TDMA_UPDATE_SLOTLEN 438189980Ssam | TDMA_UPDATE_BINTVAL; 439189980Ssam } 440186904Ssam 441189980Ssam if (update) { 442186904Ssam /* 443186904Ssam * New/changed parameters; update runtime state. 444186904Ssam */ 445186904Ssam /* XXX overwrites user parameters */ 446189980Ssam if (update & TDMA_UPDATE_SLOTCNT) 447189980Ssam ts->tdma_slotcnt = tdma->tdma_slotcnt; 448189980Ssam if (update & TDMA_UPDATE_SLOTLEN) 449189980Ssam ts->tdma_slotlen = slotlen; 450189980Ssam if (update & TDMA_UPDATE_SLOT) 451189980Ssam ts->tdma_slot = slot; 452189980Ssam if (update & TDMA_UPDATE_BINTVAL) 453189980Ssam ts->tdma_bintval = tdma->tdma_bintval; 454186904Ssam /* mark beacon to be updated before next xmit */ 455186904Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 456186904Ssam 457186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 458186904Ssam "%s: slot %u slotcnt %u slotlen %u us bintval %u\n", 459189980Ssam __func__, ts->tdma_slot, ts->tdma_slotcnt, 460189980Ssam 100*ts->tdma_slotlen, ts->tdma_bintval); 461186904Ssam } 462186904Ssam /* 463186904Ssam * Notify driver. Note we can be called before 464186904Ssam * entering RUN state if we scanned and are 465186904Ssam * joining an existing bss. In that case do not 466186904Ssam * call the driver because not all necessary state 467186904Ssam * has been setup. The next beacon will dtrt. 468186904Ssam */ 469186904Ssam if (vap->iv_state == IEEE80211_S_RUN) 470189980Ssam vap->iv_ic->ic_tdma_update(ni, tdma, update); 471186904Ssam /* 472186904Ssam * Dispatch join event on first beacon from new master. 473186904Ssam */ 474186904Ssam if (ts->tdma_peer != ni) { 475186904Ssam if (ts->tdma_peer != NULL) 476186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 477186904Ssam ieee80211_notify_node_join(ni, 1); 478186904Ssam /* NB: no reference, we just use the address */ 479186904Ssam ts->tdma_peer = ni; 480186904Ssam } 481186904Ssam return 1; 482186904Ssam} 483186904Ssam 484186904Ssam/* 485186904Ssam * Process received TDMA parameters. 486186904Ssam */ 487186904Ssamstatic int 488186904Ssamtdma_process_params(struct ieee80211_node *ni, 489186904Ssam const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh) 490186904Ssam{ 491186904Ssam struct ieee80211vap *vap = ni->ni_vap; 492186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 493186904Ssam const struct ieee80211_tdma_param *tdma = 494186904Ssam (const struct ieee80211_tdma_param *) ie; 495186904Ssam u_int len = ie[1]; 496186904Ssam 497186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 498186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 499186904Ssam 500186904Ssam if (len < sizeof(*tdma) - 2) { 501186904Ssam IEEE80211_DISCARD_IE(vap, 502186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 503186904Ssam wh, "tdma", "too short, len %u", len); 504186904Ssam return IEEE80211_REASON_IE_INVALID; 505186904Ssam } 506189980Ssam if (tdma->tdma_version != ts->tdma_version) { 507186904Ssam IEEE80211_DISCARD_IE(vap, 508186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 509189980Ssam wh, "tdma", "bad version %u (ours %u)", 510189980Ssam tdma->tdma_version, ts->tdma_version); 511186904Ssam return IEEE80211_REASON_IE_INVALID; 512186904Ssam } 513189980Ssam /* 514189980Ssam * NB: ideally we'd check against tdma_slotcnt, but that 515189980Ssam * would require extra effort so do this easy check that 516189980Ssam * covers the work below; more stringent checks are done 517189980Ssam * before we make more extensive use of the ie contents. 518189980Ssam */ 519189980Ssam if (tdma->tdma_slot >= TDMA_MAXSLOTS) { 520189980Ssam IEEE80211_DISCARD_IE(vap, 521189980Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 522189980Ssam wh, "tdma", "invalid slot %u", tdma->tdma_slot); 523189980Ssam return IEEE80211_REASON_IE_INVALID; 524189980Ssam } 525186904Ssam /* 526186904Ssam * Can reach here while scanning, update 527186904Ssam * operational state only in RUN state. 528186904Ssam */ 529186904Ssam if (vap->iv_state == IEEE80211_S_RUN) { 530186904Ssam if (tdma->tdma_slot != ts->tdma_slot && 531186904Ssam isclr(ts->tdma_inuse, tdma->tdma_slot)) { 532186904Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni, 533186904Ssam "discovered in slot %u", tdma->tdma_slot); 534186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 535186904Ssam /* XXX dispatch event only when operating as master */ 536186904Ssam if (ts->tdma_slot == 0) 537186904Ssam ieee80211_notify_node_join(ni, 1); 538186904Ssam } 539186904Ssam setbit(ts->tdma_active, tdma->tdma_slot); 540186904Ssam if (tdma->tdma_slot == ts->tdma_slot-1) { 541186904Ssam /* 542186904Ssam * Slave tsf synchronization to station 543186904Ssam * just before us in the schedule. The driver 544186904Ssam * is responsible for copying the timestamp 545186904Ssam * of the received beacon into our beacon 546186904Ssam * frame so the sender can calculate round 547186904Ssam * trip time. We cannot do that here because 548186904Ssam * we don't know how to update our beacon frame. 549186904Ssam */ 550186904Ssam (void) tdma_update(vap, tdma, ni, 0); 551186904Ssam /* XXX reschedule swbmiss timer on parameter change */ 552186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot+1) { 553186904Ssam uint64_t tstamp; 554186904Ssam int32_t rtt; 555186904Ssam /* 556186904Ssam * Use returned timstamp to calculate the 557186904Ssam * roundtrip time. 558186904Ssam */ 559186904Ssam memcpy(&tstamp, tdma->tdma_tstamp, 8); 560186904Ssam /* XXX use only 15 bits of rstamp */ 561186904Ssam rtt = rstamp - (le64toh(tstamp) & 0x7fff); 562186904Ssam if (rtt < 0) 563186904Ssam rtt += 0x7fff; 564186904Ssam /* XXX hack to quiet normal use */ 565186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X, 566186904Ssam "tdma rtt %5u [rstamp %5u tstamp %llu]\n", 567186904Ssam rtt, rstamp, 568186904Ssam (unsigned long long) le64toh(tstamp)); 569186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot && 570186904Ssam le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) { 571186904Ssam /* 572186904Ssam * Station using the same slot as us and has 573186904Ssam * been around longer than us; we must move. 574186904Ssam * Note this can happen if stations do not 575186904Ssam * see each other while scanning. 576186904Ssam */ 577186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 578186904Ssam "slot %u collision rxtsf %llu tsf %llu\n", 579186904Ssam tdma->tdma_slot, 580186904Ssam (unsigned long long) le64toh(ni->ni_tstamp.tsf), 581186904Ssam vap->iv_bss->ni_tstamp.tsf); 582186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 583186904Ssam 584186904Ssam (void) tdma_update(vap, tdma, ni, 1); 585186904Ssam } 586186904Ssam } 587186904Ssam return 0; 588186904Ssam} 589186904Ssam 590186904Ssamint 591186904Ssamieee80211_tdma_getslot(struct ieee80211vap *vap) 592186904Ssam{ 593186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 594186904Ssam 595186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 596186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 597186904Ssam return ts->tdma_slot; 598186904Ssam} 599186904Ssam 600186904Ssam/* 601186904Ssam * Parse a TDMA ie on station join and use it to setup node state. 602186904Ssam */ 603186904Ssamvoid 604186904Ssamieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie) 605186904Ssam{ 606186904Ssam struct ieee80211vap *vap = ni->ni_vap; 607186904Ssam 608186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 609186904Ssam const struct ieee80211_tdma_param *tdma = 610186904Ssam (const struct ieee80211_tdma_param *)ie; 611186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 612186904Ssam /* 613186904Ssam * Adopt TDMA configuration when joining an 614186904Ssam * existing network. 615186904Ssam */ 616186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 617186904Ssam (void) tdma_update(vap, tdma, ni, 1); 618186904Ssam /* 619186904Ssam * Propagate capabilities based on the local 620186904Ssam * configuration and the remote station's advertised 621186904Ssam * capabilities. In particular this permits us to 622186904Ssam * enable use of QoS to disable ACK's. 623186904Ssam */ 624186904Ssam if ((vap->iv_flags & IEEE80211_F_WME) && 625186904Ssam ni->ni_ies.wme_ie != NULL) 626186904Ssam ni->ni_flags |= IEEE80211_NODE_QOS; 627186904Ssam } 628186904Ssam} 629186904Ssam 630186904Ssam#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f 631186904Ssam/* 632186904Ssam * Add a TDMA parameters element to a frame. 633186904Ssam */ 634186904Ssamuint8_t * 635186904Ssamieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap) 636186904Ssam{ 637186904Ssam#define ADDSHORT(frm, v) do { \ 638186904Ssam frm[0] = (v) & 0xff; \ 639186904Ssam frm[1] = (v) >> 8; \ 640186904Ssam frm += 2; \ 641186904Ssam} while (0) 642186904Ssam static const struct ieee80211_tdma_param param = { 643186904Ssam .tdma_id = IEEE80211_ELEMID_VENDOR, 644186904Ssam .tdma_len = sizeof(struct ieee80211_tdma_param) - 2, 645186904Ssam .tdma_oui = { TDMA_OUI_BYTES }, 646186904Ssam .tdma_type = TDMA_OUI_TYPE, 647186904Ssam .tdma_subtype = TDMA_SUBTYPE_PARAM, 648186904Ssam .tdma_version = TDMA_VERSION, 649186904Ssam }; 650189980Ssam const struct ieee80211_tdma_state *ts = vap->iv_tdma; 651186904Ssam uint16_t slotlen; 652186904Ssam 653186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 654186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 655186904Ssam 656186904Ssam memcpy(frm, ¶m, sizeof(param)); 657186904Ssam frm += __offsetof(struct ieee80211_tdma_param, tdma_slot); 658189980Ssam *frm++ = ts->tdma_slot; 659189980Ssam *frm++ = ts->tdma_slotcnt; 660186904Ssam /* NB: convert units to fit in 16-bits */ 661189980Ssam slotlen = ts->tdma_slotlen / 100; /* 100us units */ 662186904Ssam ADDSHORT(frm, slotlen); 663189980Ssam *frm++ = ts->tdma_bintval; 664189980Ssam *frm++ = ts->tdma_inuse[0]; 665186904Ssam frm += 10; /* pad+timestamp */ 666186904Ssam return frm; 667186904Ssam#undef ADDSHORT 668186904Ssam} 669186904Ssam#undef TDMA_OUI_BYTES 670186904Ssam 671186904Ssam/* 672186904Ssam * Update TDMA state at TBTT. 673186904Ssam */ 674186904Ssamvoid 675186904Ssamieee80211_tdma_update_beacon(struct ieee80211vap *vap, 676186904Ssam struct ieee80211_beacon_offsets *bo) 677186904Ssam{ 678186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 679186904Ssam 680186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 681186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 682186904Ssam 683186904Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) { 684186904Ssam (void) ieee80211_add_tdma(bo->bo_tdma, vap); 685186904Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 686186904Ssam } 687186904Ssam if (ts->tdma_slot != 0) /* only on master */ 688186904Ssam return; 689186904Ssam if (ts->tdma_count <= 0) { 690186904Ssam /* 691186904Ssam * Time to update the mask of active/inuse stations. 692186904Ssam * We track stations that we've received a beacon 693186904Ssam * frame from and update this mask periodically. 694186904Ssam * This allows us to miss a few beacons before marking 695186904Ssam * a slot free for re-use. 696186904Ssam */ 697186904Ssam ts->tdma_inuse[0] = ts->tdma_active[0]; 698186904Ssam ts->tdma_active[0] = 0x01; 699186904Ssam /* update next time 'round */ 700186904Ssam /* XXX use notify framework */ 701186904Ssam setbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 702186904Ssam /* NB: use s/w beacon miss threshold; may be too high */ 703186904Ssam ts->tdma_count = vap->iv_bmissthreshold-1; 704186904Ssam } else 705186904Ssam ts->tdma_count--; 706186904Ssam} 707186904Ssam 708190384Ssamstatic int 709190384Ssamtdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 710186904Ssam{ 711186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 712186904Ssam 713186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 714186904Ssam return EOPNOTSUPP; 715186904Ssam 716186904Ssam switch (ireq->i_type) { 717186904Ssam case IEEE80211_IOC_TDMA_SLOT: 718186904Ssam ireq->i_val = ts->tdma_slot; 719186904Ssam break; 720186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 721186904Ssam ireq->i_val = ts->tdma_slotcnt; 722186904Ssam break; 723186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 724186904Ssam ireq->i_val = ts->tdma_slotlen; 725186904Ssam break; 726186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 727186904Ssam ireq->i_val = ts->tdma_bintval; 728186904Ssam break; 729186904Ssam default: 730190384Ssam return ENOSYS; 731186904Ssam } 732186904Ssam return 0; 733186904Ssam} 734190384SsamIEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211); 735186904Ssam 736190384Ssamstatic int 737190384Ssamtdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 738186904Ssam{ 739186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 740186904Ssam 741186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 742186904Ssam return EOPNOTSUPP; 743186904Ssam 744186904Ssam switch (ireq->i_type) { 745186904Ssam case IEEE80211_IOC_TDMA_SLOT: 746186904Ssam if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt)) 747186904Ssam return EINVAL; 748186904Ssam if (ireq->i_val != ts->tdma_slot) { 749186904Ssam ts->tdma_slot = ireq->i_val; 750186904Ssam return ERESTART; 751186904Ssam } 752186904Ssam break; 753186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 754189980Ssam if (!TDMA_SLOTCNT_VALID(ireq->i_val)) 755186904Ssam return EINVAL; 756186904Ssam if (ireq->i_val != ts->tdma_slotcnt) { 757186904Ssam ts->tdma_slotcnt = ireq->i_val; 758186904Ssam return ERESTART; 759186904Ssam } 760186904Ssam break; 761186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 762186904Ssam /* 763186904Ssam * XXX 764186904Ssam * 150 insures at least 1/8 TU 765186904Ssam * 0xfffff is the max duration for bursting 766186904Ssam * (implict by way of 16-bit data type for i_val) 767186904Ssam */ 768189980Ssam if (!TDMA_SLOTLEN_VALID(ireq->i_val)) 769186904Ssam return EINVAL; 770186904Ssam if (ireq->i_val != ts->tdma_slotlen) { 771186904Ssam ts->tdma_slotlen = ireq->i_val; 772186904Ssam return ERESTART; 773186904Ssam } 774186904Ssam break; 775186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 776189980Ssam if (!TDMA_BINTVAL_VALID(ireq->i_val)) 777186904Ssam return EINVAL; 778186904Ssam if (ireq->i_val != ts->tdma_bintval) { 779186904Ssam ts->tdma_bintval = ireq->i_val; 780186904Ssam return ERESTART; 781186904Ssam } 782186904Ssam break; 783186904Ssam default: 784190384Ssam return ENOSYS; 785186904Ssam } 786186904Ssam return 0; 787186904Ssam} 788190384SsamIEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211); 789