ieee80211_tdma.c revision 191017
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 191017 2009-04-13 20:50:51Z sam $"); 30186904Ssam#endif 31186904Ssam 32186904Ssam/* 33186904Ssam * IEEE 802.11 TDMA mode support. 34186904Ssam */ 35186904Ssam#include "opt_inet.h" 36190455Ssam#include "opt_tdma.h" 37186904Ssam#include "opt_wlan.h" 38186904Ssam 39186904Ssam#include <sys/param.h> 40186904Ssam#include <sys/systm.h> 41186904Ssam#include <sys/mbuf.h> 42186904Ssam#include <sys/malloc.h> 43186904Ssam#include <sys/kernel.h> 44186904Ssam 45186904Ssam#include <sys/socket.h> 46186904Ssam#include <sys/sockio.h> 47186904Ssam#include <sys/endian.h> 48186904Ssam#include <sys/errno.h> 49186904Ssam#include <sys/proc.h> 50186904Ssam#include <sys/sysctl.h> 51186904Ssam 52186904Ssam#include <net/if.h> 53186904Ssam#include <net/if_media.h> 54186904Ssam#include <net/if_llc.h> 55186904Ssam#include <net/ethernet.h> 56186904Ssam 57186904Ssam#include <net/bpf.h> 58186904Ssam 59186904Ssam#include <net80211/ieee80211_var.h> 60186904Ssam#include <net80211/ieee80211_tdma.h> 61186904Ssam#include <net80211/ieee80211_input.h> 62186904Ssam 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 97190455Ssam#define TDMA_VERSION_VALID(_version) \ 98190455Ssam (TDMA_VERSION_V2 <= (_version) && (_version) <= TDMA_VERSION) 99190455Ssam#define TDMA_SLOTCNT_VALID(_slotcnt) \ 100190455Ssam (2 <= (_slotcnt) && (_slotcnt) <= TDMA_MAXSLOTS) 101190455Ssam/* XXX magic constants */ 102190455Ssam#define TDMA_SLOTLEN_VALID(_slotlen) \ 103190455Ssam (2*100 <= (_slotlen) && (unsigned)(_slotlen) <= 0xfffff) 104190455Ssam/* XXX probably should set a max */ 105190455Ssam#define TDMA_BINTVAL_VALID(_bintval) (1 <= (_bintval)) 106190455Ssam 107186904Ssamstatic void tdma_vdetach(struct ieee80211vap *vap); 108186904Ssamstatic int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int); 109186904Ssamstatic void tdma_beacon_miss(struct ieee80211vap *vap); 110186904Ssamstatic void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *, 111186904Ssam int subtype, int rssi, int noise, uint32_t rstamp); 112186904Ssamstatic int tdma_update(struct ieee80211vap *vap, 113186904Ssam const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni, 114186904Ssam int pickslot); 115186904Ssamstatic int tdma_process_params(struct ieee80211_node *ni, 116186904Ssam const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh); 117186904Ssam 118186904Ssamstatic void 119187899Ssamsettxparms(struct ieee80211vap *vap, enum ieee80211_phymode mode, int rate) 120187899Ssam{ 121187899Ssam vap->iv_txparms[mode].ucastrate = rate; 122187899Ssam vap->iv_txparms[mode].mcastrate = rate; 123187899Ssam} 124187899Ssam 125187899Ssamstatic void 126186904Ssamsetackpolicy(struct ieee80211com *ic, int noack) 127186904Ssam{ 128186904Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 129186904Ssam int ac; 130186904Ssam 131186904Ssam for (ac = 0; ac < WME_NUM_AC; ac++) { 132186904Ssam wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 133186904Ssam wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 134186904Ssam } 135186904Ssam} 136186904Ssam 137186904Ssamvoid 138186904Ssamieee80211_tdma_vattach(struct ieee80211vap *vap) 139186904Ssam{ 140186904Ssam struct ieee80211_tdma_state *ts; 141186904Ssam 142186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 143186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 144186904Ssam 145186904Ssam ts = (struct ieee80211_tdma_state *) malloc( 146186904Ssam sizeof(struct ieee80211_tdma_state), M_80211_VAP, M_NOWAIT | M_ZERO); 147186904Ssam if (ts == NULL) { 148186904Ssam printf("%s: cannot allocate TDMA state block\n", __func__); 149186904Ssam /* NB: fall back to adhdemo mode */ 150186904Ssam vap->iv_caps &= ~IEEE80211_C_TDMA; 151186904Ssam return; 152186904Ssam } 153186904Ssam /* NB: default configuration is passive so no beacons */ 154189980Ssam ts->tdma_version = TDMA_VERSION; 155186904Ssam ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT; 156186904Ssam ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT; 157186904Ssam ts->tdma_bintval = TDMA_BINTVAL_DEFAULT; 158186904Ssam ts->tdma_slot = 1; /* passive operation */ 159186904Ssam 160186904Ssam /* setup default fixed rates */ 161187899Ssam settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT); 162187899Ssam settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT); 163187899Ssam settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT); 164187899Ssam settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_STURBO_A_DEFAULT); 165187899Ssam settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT); 166187899Ssam settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT); 167188782Ssam settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT); 168188782Ssam settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT); 169186904Ssam 170186904Ssam setackpolicy(vap->iv_ic, 1); /* disable ACK's */ 171186904Ssam 172186904Ssam ts->tdma_opdetach = vap->iv_opdetach; 173186904Ssam vap->iv_opdetach = tdma_vdetach; 174186904Ssam ts->tdma_newstate = vap->iv_newstate; 175186904Ssam vap->iv_newstate = tdma_newstate; 176186904Ssam vap->iv_bmiss = tdma_beacon_miss; 177186904Ssam ts->tdma_recv_mgmt = vap->iv_recv_mgmt; 178186904Ssam vap->iv_recv_mgmt = tdma_recv_mgmt; 179186904Ssam 180186904Ssam vap->iv_tdma = ts; 181186904Ssam} 182186904Ssam 183186904Ssamstatic void 184186904Ssamtdma_vdetach(struct ieee80211vap *vap) 185186904Ssam{ 186186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 187186904Ssam 188186904Ssam ts->tdma_opdetach(vap); 189186904Ssam free(vap->iv_tdma, M_80211_VAP); 190186904Ssam 191186904Ssam setackpolicy(vap->iv_ic, 0); /* enable ACK's */ 192186904Ssam} 193186904Ssam 194188467Ssamstatic void 195188467Ssamsta_leave(void *arg, struct ieee80211_node *ni) 196188467Ssam{ 197188467Ssam struct ieee80211vap *vap = arg; 198188467Ssam 199188467Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 200188467Ssam ieee80211_node_leave(ni); 201188467Ssam} 202188467Ssam 203186904Ssam/* 204186904Ssam * TDMA state machine handler. 205186904Ssam */ 206186904Ssamstatic int 207186904Ssamtdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 208186904Ssam{ 209186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 210188467Ssam struct ieee80211com *ic = vap->iv_ic; 211186904Ssam enum ieee80211_state ostate; 212186904Ssam int status; 213186904Ssam 214188467Ssam IEEE80211_LOCK_ASSERT(ic); 215186904Ssam 216186904Ssam ostate = vap->iv_state; 217186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 218186904Ssam __func__, ieee80211_state_name[ostate], 219186904Ssam ieee80211_state_name[nstate], arg); 220186904Ssam 221186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 222186904Ssam callout_stop(&vap->iv_swbmiss); 223186904Ssam if (nstate == IEEE80211_S_SCAN && 224186904Ssam (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) && 225186904Ssam ts->tdma_slot != 0) { 226186904Ssam /* 227186904Ssam * Override adhoc behaviour when operating as a slave; 228186904Ssam * we need to scan even if the channel is locked. 229186904Ssam */ 230186904Ssam vap->iv_state = nstate; /* state transition */ 231186904Ssam ieee80211_cancel_scan(vap); /* background scan */ 232188467Ssam if (ostate == IEEE80211_S_RUN) { 233188467Ssam /* purge station table; entries are stale */ 234188467Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 235188467Ssam } 236186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 237186904Ssam ieee80211_check_scan(vap, 238186904Ssam vap->iv_scanreq_flags, 239186904Ssam vap->iv_scanreq_duration, 240186904Ssam vap->iv_scanreq_mindwell, 241186904Ssam vap->iv_scanreq_maxdwell, 242186904Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 243186904Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 244186904Ssam } else 245186904Ssam ieee80211_check_scan_current(vap); 246186904Ssam status = 0; 247186904Ssam } else { 248186904Ssam status = ts->tdma_newstate(vap, nstate, arg); 249186904Ssam } 250186904Ssam if (status == 0 && 251186904Ssam nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN && 252186904Ssam (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) && 253188925Ssam ts->tdma_slot != 0 && 254188925Ssam vap->iv_des_chan == IEEE80211_CHAN_ANYC) { 255186904Ssam /* 256186904Ssam * Start s/w beacon miss timer for slave devices w/o 257188925Ssam * hardware support. Note we do this only if we're 258188925Ssam * not locked to a channel (i.e. roam to follow the 259188925Ssam * master). The 2x is a fudge for our doing this in 260188925Ssam * software. 261186904Ssam */ 262186904Ssam vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 263186904Ssam 2 * vap->iv_bmissthreshold * ts->tdma_bintval * 264186904Ssam ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024)); 265186904Ssam vap->iv_swbmiss_count = 0; 266186904Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 267186904Ssam ieee80211_swbmiss, vap); 268186904Ssam } 269186904Ssam return status; 270186904Ssam} 271186904Ssam 272186904Ssamstatic void 273186904Ssamtdma_beacon_miss(struct ieee80211vap *vap) 274186904Ssam{ 275186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 276186904Ssam 277186904Ssam KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 278186904Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 279186904Ssam ("wrong state %d", vap->iv_state)); 280186904Ssam 281186904Ssam IEEE80211_DPRINTF(vap, 282186904Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG, 283186904Ssam "beacon miss, mode %u state %s\n", 284186904Ssam vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 285186904Ssam 286186904Ssam if (ts->tdma_peer != NULL) { /* XXX? can this be null? */ 287186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 288186904Ssam ts->tdma_peer = NULL; 289186904Ssam /* 290186904Ssam * Treat beacon miss like an associate failure wrt the 291186904Ssam * scan policy; this forces the entry in the scan cache 292186904Ssam * to be ignored after several tries. 293186904Ssam */ 294186904Ssam ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, 295186904Ssam IEEE80211_STATUS_TIMEOUT); 296186904Ssam } 297186904Ssam#if 0 298186904Ssam ts->tdma_inuse = 0; /* clear slot usage */ 299186904Ssam#endif 300186904Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 301186904Ssam} 302186904Ssam 303186904Ssamstatic void 304186904Ssamtdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 305186904Ssam int subtype, int rssi, int noise, uint32_t rstamp) 306186904Ssam{ 307186904Ssam struct ieee80211com *ic = ni->ni_ic; 308186904Ssam struct ieee80211vap *vap = ni->ni_vap; 309186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 310186904Ssam 311186904Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && 312186904Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 313186904Ssam struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); 314186904Ssam struct ieee80211_scanparams scan; 315186904Ssam 316186904Ssam if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 317186904Ssam return; 318186904Ssam if (scan.tdma == NULL) { 319186904Ssam /* 320186904Ssam * TDMA stations must beacon a TDMA ie; ignore 321186904Ssam * any other station. 322186904Ssam * XXX detect overlapping bss and change channel 323186904Ssam */ 324186904Ssam IEEE80211_DISCARD(vap, 325186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 326186904Ssam wh, ieee80211_mgt_subtype_name[subtype >> 327186904Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 328186904Ssam "%s", "no TDMA ie"); 329186904Ssam vap->iv_stats.is_rx_mgtdiscard++; 330186904Ssam return; 331186904Ssam } 332186904Ssam if (ni == vap->iv_bss && 333186904Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 334186904Ssam /* 335186904Ssam * Fake up a node for this newly 336186904Ssam * discovered member of the IBSS. 337186904Ssam */ 338186904Ssam ni = ieee80211_add_neighbor(vap, wh, &scan); 339186904Ssam if (ni == NULL) { 340186904Ssam /* NB: stat kept for alloc failure */ 341186904Ssam return; 342186904Ssam } 343186904Ssam } 344186904Ssam /* 345186904Ssam * Check for state updates. 346186904Ssam */ 347191016Ssam if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) { 348186904Ssam /* 349186904Ssam * Count frame now that we know it's to be processed. 350186904Ssam */ 351186904Ssam vap->iv_stats.is_rx_beacon++; 352186904Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 353186904Ssam /* 354186904Ssam * Record tsf of last beacon. NB: this must be 355186904Ssam * done before calling tdma_process_params 356186904Ssam * as deeper routines reference it. 357186904Ssam */ 358186904Ssam memcpy(&ni->ni_tstamp.data, scan.tstamp, 359186904Ssam sizeof(ni->ni_tstamp.data)); 360186904Ssam /* 361186904Ssam * Count beacon frame for s/w bmiss handling. 362186904Ssam */ 363186904Ssam vap->iv_swbmiss_count++; 364186904Ssam /* 365186904Ssam * Process tdma ie. The contents are used to sync 366186904Ssam * the slot timing, reconfigure the bss, etc. 367186904Ssam */ 368186904Ssam (void) tdma_process_params(ni, scan.tdma, rstamp, wh); 369186904Ssam return; 370186904Ssam } 371186904Ssam /* 372186904Ssam * NB: defer remaining work to the adhoc code; this causes 373186904Ssam * 2x parsing of the frame but should happen infrequently 374186904Ssam */ 375186904Ssam } 376186904Ssam ts->tdma_recv_mgmt(ni, m0, subtype, rssi, noise, rstamp); 377186904Ssam} 378186904Ssam 379186904Ssam/* 380186904Ssam * Update TDMA state on receipt of a beacon frame with 381186904Ssam * a TDMA information element. The sender's identity 382186904Ssam * is provided so we can track who our peer is. If pickslot 383186904Ssam * is non-zero we scan the slot allocation state in the ie 384189980Ssam * to locate a free slot for our use. 385186904Ssam */ 386186904Ssamstatic int 387186904Ssamtdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma, 388186904Ssam struct ieee80211_node *ni, int pickslot) 389186904Ssam{ 390186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 391189980Ssam int slot, slotlen, update; 392186904Ssam 393186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 394186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 395186904Ssam 396189980Ssam update = 0; 397189980Ssam if (tdma->tdma_slotcnt != ts->tdma_slotcnt) { 398189980Ssam if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) { 399189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 400189981Ssam printf("%s: bad slot cnt %u\n", 401189981Ssam __func__, tdma->tdma_slotcnt); 402189980Ssam return 0; 403189980Ssam } 404189980Ssam update |= TDMA_UPDATE_SLOTCNT; 405189980Ssam } 406189980Ssam slotlen = le16toh(tdma->tdma_slotlen) * 100; 407189980Ssam if (slotlen != ts->tdma_slotlen) { 408189980Ssam if (!TDMA_SLOTLEN_VALID(slotlen)) { 409189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 410189981Ssam printf("%s: bad slot len %u\n", 411189981Ssam __func__, slotlen); 412189980Ssam return 0; 413189980Ssam } 414189980Ssam update |= TDMA_UPDATE_SLOTLEN; 415189980Ssam } 416189980Ssam if (tdma->tdma_bintval != ts->tdma_bintval) { 417189980Ssam if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) { 418189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 419189981Ssam printf("%s: bad beacon interval %u\n", 420189981Ssam __func__, tdma->tdma_bintval); 421189980Ssam return 0; 422189980Ssam } 423189980Ssam update |= TDMA_UPDATE_BINTVAL; 424189980Ssam } 425189980Ssam slot = ts->tdma_slot; 426186904Ssam if (pickslot) { 427186904Ssam /* 428186904Ssam * Pick unoccupied slot. Note we never choose slot 0. 429186904Ssam */ 430189980Ssam for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--) 431186904Ssam if (isclr(tdma->tdma_inuse, slot)) 432186904Ssam break; 433186904Ssam if (slot <= 0) { 434186904Ssam printf("%s: no free slot, slotcnt %u inuse: 0x%x\n", 435189980Ssam __func__, tdma->tdma_slotcnt, 436189980Ssam tdma->tdma_inuse[0]); 437186904Ssam /* XXX need to do something better */ 438186904Ssam return 0; 439186904Ssam } 440189980Ssam if (slot != ts->tdma_slot) 441189980Ssam update |= TDMA_UPDATE_SLOT; 442189980Ssam } 443189980Ssam if (ni != ts->tdma_peer) { 444189980Ssam /* update everything */ 445189980Ssam update = TDMA_UPDATE_SLOT 446189980Ssam | TDMA_UPDATE_SLOTCNT 447189980Ssam | TDMA_UPDATE_SLOTLEN 448189980Ssam | TDMA_UPDATE_BINTVAL; 449189980Ssam } 450186904Ssam 451189980Ssam if (update) { 452186904Ssam /* 453186904Ssam * New/changed parameters; update runtime state. 454186904Ssam */ 455186904Ssam /* XXX overwrites user parameters */ 456189980Ssam if (update & TDMA_UPDATE_SLOTCNT) 457189980Ssam ts->tdma_slotcnt = tdma->tdma_slotcnt; 458189980Ssam if (update & TDMA_UPDATE_SLOTLEN) 459189980Ssam ts->tdma_slotlen = slotlen; 460189980Ssam if (update & TDMA_UPDATE_SLOT) 461189980Ssam ts->tdma_slot = slot; 462189980Ssam if (update & TDMA_UPDATE_BINTVAL) 463189980Ssam ts->tdma_bintval = tdma->tdma_bintval; 464186904Ssam /* mark beacon to be updated before next xmit */ 465186904Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 466186904Ssam 467186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 468186904Ssam "%s: slot %u slotcnt %u slotlen %u us bintval %u\n", 469189980Ssam __func__, ts->tdma_slot, ts->tdma_slotcnt, 470191017Ssam ts->tdma_slotlen, ts->tdma_bintval); 471186904Ssam } 472186904Ssam /* 473186904Ssam * Notify driver. Note we can be called before 474186904Ssam * entering RUN state if we scanned and are 475186904Ssam * joining an existing bss. In that case do not 476186904Ssam * call the driver because not all necessary state 477186904Ssam * has been setup. The next beacon will dtrt. 478186904Ssam */ 479186904Ssam if (vap->iv_state == IEEE80211_S_RUN) 480189980Ssam vap->iv_ic->ic_tdma_update(ni, tdma, update); 481186904Ssam /* 482186904Ssam * Dispatch join event on first beacon from new master. 483186904Ssam */ 484186904Ssam if (ts->tdma_peer != ni) { 485186904Ssam if (ts->tdma_peer != NULL) 486186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 487186904Ssam ieee80211_notify_node_join(ni, 1); 488186904Ssam /* NB: no reference, we just use the address */ 489186904Ssam ts->tdma_peer = ni; 490186904Ssam } 491186904Ssam return 1; 492186904Ssam} 493186904Ssam 494186904Ssam/* 495186904Ssam * Process received TDMA parameters. 496186904Ssam */ 497186904Ssamstatic int 498186904Ssamtdma_process_params(struct ieee80211_node *ni, 499186904Ssam const u_int8_t *ie, u_int32_t rstamp, const struct ieee80211_frame *wh) 500186904Ssam{ 501186904Ssam struct ieee80211vap *vap = ni->ni_vap; 502186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 503186904Ssam const struct ieee80211_tdma_param *tdma = 504186904Ssam (const struct ieee80211_tdma_param *) ie; 505186904Ssam u_int len = ie[1]; 506186904Ssam 507186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 508186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 509186904Ssam 510186904Ssam if (len < sizeof(*tdma) - 2) { 511186904Ssam IEEE80211_DISCARD_IE(vap, 512186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 513186904Ssam wh, "tdma", "too short, len %u", len); 514186904Ssam return IEEE80211_REASON_IE_INVALID; 515186904Ssam } 516189980Ssam if (tdma->tdma_version != ts->tdma_version) { 517186904Ssam IEEE80211_DISCARD_IE(vap, 518186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 519189980Ssam wh, "tdma", "bad version %u (ours %u)", 520189980Ssam tdma->tdma_version, ts->tdma_version); 521186904Ssam return IEEE80211_REASON_IE_INVALID; 522186904Ssam } 523189980Ssam /* 524189980Ssam * NB: ideally we'd check against tdma_slotcnt, but that 525189980Ssam * would require extra effort so do this easy check that 526189980Ssam * covers the work below; more stringent checks are done 527189980Ssam * before we make more extensive use of the ie contents. 528189980Ssam */ 529189980Ssam if (tdma->tdma_slot >= TDMA_MAXSLOTS) { 530189980Ssam IEEE80211_DISCARD_IE(vap, 531189980Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 532189980Ssam wh, "tdma", "invalid slot %u", tdma->tdma_slot); 533189980Ssam return IEEE80211_REASON_IE_INVALID; 534189980Ssam } 535186904Ssam /* 536186904Ssam * Can reach here while scanning, update 537186904Ssam * operational state only in RUN state. 538186904Ssam */ 539186904Ssam if (vap->iv_state == IEEE80211_S_RUN) { 540186904Ssam if (tdma->tdma_slot != ts->tdma_slot && 541186904Ssam isclr(ts->tdma_inuse, tdma->tdma_slot)) { 542186904Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni, 543186904Ssam "discovered in slot %u", tdma->tdma_slot); 544186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 545186904Ssam /* XXX dispatch event only when operating as master */ 546186904Ssam if (ts->tdma_slot == 0) 547186904Ssam ieee80211_notify_node_join(ni, 1); 548186904Ssam } 549186904Ssam setbit(ts->tdma_active, tdma->tdma_slot); 550186904Ssam if (tdma->tdma_slot == ts->tdma_slot-1) { 551186904Ssam /* 552186904Ssam * Slave tsf synchronization to station 553186904Ssam * just before us in the schedule. The driver 554186904Ssam * is responsible for copying the timestamp 555186904Ssam * of the received beacon into our beacon 556186904Ssam * frame so the sender can calculate round 557186904Ssam * trip time. We cannot do that here because 558186904Ssam * we don't know how to update our beacon frame. 559186904Ssam */ 560186904Ssam (void) tdma_update(vap, tdma, ni, 0); 561186904Ssam /* XXX reschedule swbmiss timer on parameter change */ 562186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot+1) { 563186904Ssam uint64_t tstamp; 564186904Ssam int32_t rtt; 565186904Ssam /* 566186904Ssam * Use returned timstamp to calculate the 567186904Ssam * roundtrip time. 568186904Ssam */ 569186904Ssam memcpy(&tstamp, tdma->tdma_tstamp, 8); 570186904Ssam /* XXX use only 15 bits of rstamp */ 571186904Ssam rtt = rstamp - (le64toh(tstamp) & 0x7fff); 572186904Ssam if (rtt < 0) 573186904Ssam rtt += 0x7fff; 574186904Ssam /* XXX hack to quiet normal use */ 575186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X, 576186904Ssam "tdma rtt %5u [rstamp %5u tstamp %llu]\n", 577186904Ssam rtt, rstamp, 578186904Ssam (unsigned long long) le64toh(tstamp)); 579186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot && 580186904Ssam le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) { 581186904Ssam /* 582186904Ssam * Station using the same slot as us and has 583186904Ssam * been around longer than us; we must move. 584186904Ssam * Note this can happen if stations do not 585186904Ssam * see each other while scanning. 586186904Ssam */ 587186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 588186904Ssam "slot %u collision rxtsf %llu tsf %llu\n", 589186904Ssam tdma->tdma_slot, 590186904Ssam (unsigned long long) le64toh(ni->ni_tstamp.tsf), 591186904Ssam vap->iv_bss->ni_tstamp.tsf); 592186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 593186904Ssam 594186904Ssam (void) tdma_update(vap, tdma, ni, 1); 595186904Ssam } 596186904Ssam } 597186904Ssam return 0; 598186904Ssam} 599186904Ssam 600186904Ssamint 601186904Ssamieee80211_tdma_getslot(struct ieee80211vap *vap) 602186904Ssam{ 603186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 604186904Ssam 605186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 606186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 607186904Ssam return ts->tdma_slot; 608186904Ssam} 609186904Ssam 610186904Ssam/* 611186904Ssam * Parse a TDMA ie on station join and use it to setup node state. 612186904Ssam */ 613186904Ssamvoid 614186904Ssamieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie) 615186904Ssam{ 616186904Ssam struct ieee80211vap *vap = ni->ni_vap; 617186904Ssam 618186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 619186904Ssam const struct ieee80211_tdma_param *tdma = 620186904Ssam (const struct ieee80211_tdma_param *)ie; 621186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 622186904Ssam /* 623186904Ssam * Adopt TDMA configuration when joining an 624186904Ssam * existing network. 625186904Ssam */ 626186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 627186904Ssam (void) tdma_update(vap, tdma, ni, 1); 628186904Ssam /* 629186904Ssam * Propagate capabilities based on the local 630186904Ssam * configuration and the remote station's advertised 631186904Ssam * capabilities. In particular this permits us to 632186904Ssam * enable use of QoS to disable ACK's. 633186904Ssam */ 634186904Ssam if ((vap->iv_flags & IEEE80211_F_WME) && 635186904Ssam ni->ni_ies.wme_ie != NULL) 636186904Ssam ni->ni_flags |= IEEE80211_NODE_QOS; 637186904Ssam } 638186904Ssam} 639186904Ssam 640186904Ssam#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f 641186904Ssam/* 642186904Ssam * Add a TDMA parameters element to a frame. 643186904Ssam */ 644186904Ssamuint8_t * 645186904Ssamieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap) 646186904Ssam{ 647186904Ssam#define ADDSHORT(frm, v) do { \ 648186904Ssam frm[0] = (v) & 0xff; \ 649186904Ssam frm[1] = (v) >> 8; \ 650186904Ssam frm += 2; \ 651186904Ssam} while (0) 652186904Ssam static const struct ieee80211_tdma_param param = { 653186904Ssam .tdma_id = IEEE80211_ELEMID_VENDOR, 654186904Ssam .tdma_len = sizeof(struct ieee80211_tdma_param) - 2, 655186904Ssam .tdma_oui = { TDMA_OUI_BYTES }, 656186904Ssam .tdma_type = TDMA_OUI_TYPE, 657186904Ssam .tdma_subtype = TDMA_SUBTYPE_PARAM, 658186904Ssam .tdma_version = TDMA_VERSION, 659186904Ssam }; 660189980Ssam const struct ieee80211_tdma_state *ts = vap->iv_tdma; 661186904Ssam uint16_t slotlen; 662186904Ssam 663186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 664186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 665186904Ssam 666186904Ssam memcpy(frm, ¶m, sizeof(param)); 667186904Ssam frm += __offsetof(struct ieee80211_tdma_param, tdma_slot); 668189980Ssam *frm++ = ts->tdma_slot; 669189980Ssam *frm++ = ts->tdma_slotcnt; 670186904Ssam /* NB: convert units to fit in 16-bits */ 671189980Ssam slotlen = ts->tdma_slotlen / 100; /* 100us units */ 672186904Ssam ADDSHORT(frm, slotlen); 673189980Ssam *frm++ = ts->tdma_bintval; 674189980Ssam *frm++ = ts->tdma_inuse[0]; 675186904Ssam frm += 10; /* pad+timestamp */ 676186904Ssam return frm; 677186904Ssam#undef ADDSHORT 678186904Ssam} 679186904Ssam#undef TDMA_OUI_BYTES 680186904Ssam 681186904Ssam/* 682186904Ssam * Update TDMA state at TBTT. 683186904Ssam */ 684186904Ssamvoid 685186904Ssamieee80211_tdma_update_beacon(struct ieee80211vap *vap, 686186904Ssam struct ieee80211_beacon_offsets *bo) 687186904Ssam{ 688186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 689186904Ssam 690186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 691186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 692186904Ssam 693186904Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) { 694186904Ssam (void) ieee80211_add_tdma(bo->bo_tdma, vap); 695186904Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 696186904Ssam } 697186904Ssam if (ts->tdma_slot != 0) /* only on master */ 698186904Ssam return; 699186904Ssam if (ts->tdma_count <= 0) { 700186904Ssam /* 701186904Ssam * Time to update the mask of active/inuse stations. 702186904Ssam * We track stations that we've received a beacon 703186904Ssam * frame from and update this mask periodically. 704186904Ssam * This allows us to miss a few beacons before marking 705186904Ssam * a slot free for re-use. 706186904Ssam */ 707186904Ssam ts->tdma_inuse[0] = ts->tdma_active[0]; 708186904Ssam ts->tdma_active[0] = 0x01; 709186904Ssam /* update next time 'round */ 710186904Ssam /* XXX use notify framework */ 711186904Ssam setbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 712186904Ssam /* NB: use s/w beacon miss threshold; may be too high */ 713186904Ssam ts->tdma_count = vap->iv_bmissthreshold-1; 714186904Ssam } else 715186904Ssam ts->tdma_count--; 716186904Ssam} 717186904Ssam 718190384Ssamstatic int 719190384Ssamtdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 720186904Ssam{ 721186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 722186904Ssam 723186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 724186904Ssam return EOPNOTSUPP; 725186904Ssam 726186904Ssam switch (ireq->i_type) { 727186904Ssam case IEEE80211_IOC_TDMA_SLOT: 728186904Ssam ireq->i_val = ts->tdma_slot; 729186904Ssam break; 730186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 731186904Ssam ireq->i_val = ts->tdma_slotcnt; 732186904Ssam break; 733186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 734186904Ssam ireq->i_val = ts->tdma_slotlen; 735186904Ssam break; 736186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 737186904Ssam ireq->i_val = ts->tdma_bintval; 738186904Ssam break; 739186904Ssam default: 740190384Ssam return ENOSYS; 741186904Ssam } 742186904Ssam return 0; 743186904Ssam} 744190384SsamIEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211); 745186904Ssam 746190384Ssamstatic int 747190384Ssamtdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 748186904Ssam{ 749186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 750186904Ssam 751186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 752186904Ssam return EOPNOTSUPP; 753186904Ssam 754186904Ssam switch (ireq->i_type) { 755186904Ssam case IEEE80211_IOC_TDMA_SLOT: 756186904Ssam if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt)) 757186904Ssam return EINVAL; 758186904Ssam if (ireq->i_val != ts->tdma_slot) { 759186904Ssam ts->tdma_slot = ireq->i_val; 760186904Ssam return ERESTART; 761186904Ssam } 762186904Ssam break; 763186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 764189980Ssam if (!TDMA_SLOTCNT_VALID(ireq->i_val)) 765186904Ssam return EINVAL; 766186904Ssam if (ireq->i_val != ts->tdma_slotcnt) { 767186904Ssam ts->tdma_slotcnt = ireq->i_val; 768186904Ssam return ERESTART; 769186904Ssam } 770186904Ssam break; 771186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 772186904Ssam /* 773186904Ssam * XXX 774186904Ssam * 150 insures at least 1/8 TU 775186904Ssam * 0xfffff is the max duration for bursting 776186904Ssam * (implict by way of 16-bit data type for i_val) 777186904Ssam */ 778189980Ssam if (!TDMA_SLOTLEN_VALID(ireq->i_val)) 779186904Ssam return EINVAL; 780186904Ssam if (ireq->i_val != ts->tdma_slotlen) { 781186904Ssam ts->tdma_slotlen = ireq->i_val; 782186904Ssam return ERESTART; 783186904Ssam } 784186904Ssam break; 785186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 786189980Ssam if (!TDMA_BINTVAL_VALID(ireq->i_val)) 787186904Ssam return EINVAL; 788186904Ssam if (ireq->i_val != ts->tdma_bintval) { 789186904Ssam ts->tdma_bintval = ireq->i_val; 790186904Ssam return ERESTART; 791186904Ssam } 792186904Ssam break; 793186904Ssam default: 794190384Ssam return ENOSYS; 795186904Ssam } 796186904Ssam return 0; 797186904Ssam} 798190384SsamIEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211); 799