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$"); 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 39230153Sadrian#ifdef IEEE80211_SUPPORT_TDMA 40230153Sadrian 41186904Ssam#include <sys/param.h> 42186904Ssam#include <sys/systm.h> 43186904Ssam#include <sys/mbuf.h> 44186904Ssam#include <sys/malloc.h> 45186904Ssam#include <sys/kernel.h> 46186904Ssam 47186904Ssam#include <sys/socket.h> 48186904Ssam#include <sys/sockio.h> 49186904Ssam#include <sys/endian.h> 50186904Ssam#include <sys/errno.h> 51186904Ssam#include <sys/proc.h> 52186904Ssam#include <sys/sysctl.h> 53186904Ssam 54186904Ssam#include <net/if.h> 55186904Ssam#include <net/if_media.h> 56186904Ssam#include <net/if_llc.h> 57186904Ssam#include <net/ethernet.h> 58186904Ssam 59186904Ssam#include <net/bpf.h> 60186904Ssam 61186904Ssam#include <net80211/ieee80211_var.h> 62186904Ssam#include <net80211/ieee80211_tdma.h> 63186904Ssam#include <net80211/ieee80211_input.h> 64186904Ssam 65186904Ssam#ifndef TDMA_SLOTLEN_DEFAULT 66186904Ssam#define TDMA_SLOTLEN_DEFAULT 10*1000 /* 10ms */ 67186904Ssam#endif 68186904Ssam#ifndef TDMA_SLOTCNT_DEFAULT 69186904Ssam#define TDMA_SLOTCNT_DEFAULT 2 /* 2x (pt-to-pt) */ 70186904Ssam#endif 71186904Ssam#ifndef TDMA_BINTVAL_DEFAULT 72186904Ssam#define TDMA_BINTVAL_DEFAULT 5 /* 5x ~= 100TU beacon intvl */ 73186904Ssam#endif 74186904Ssam#ifndef TDMA_TXRATE_11B_DEFAULT 75186904Ssam#define TDMA_TXRATE_11B_DEFAULT 2*11 76186904Ssam#endif 77186904Ssam#ifndef TDMA_TXRATE_11G_DEFAULT 78186904Ssam#define TDMA_TXRATE_11G_DEFAULT 2*24 79186904Ssam#endif 80186904Ssam#ifndef TDMA_TXRATE_11A_DEFAULT 81186904Ssam#define TDMA_TXRATE_11A_DEFAULT 2*24 82186904Ssam#endif 83191018Ssam#ifndef TDMA_TXRATE_TURBO_DEFAULT 84191018Ssam#define TDMA_TXRATE_TURBO_DEFAULT 2*24 85187899Ssam#endif 86188782Ssam#ifndef TDMA_TXRATE_HALF_DEFAULT 87188782Ssam#define TDMA_TXRATE_HALF_DEFAULT 2*12 88188782Ssam#endif 89188782Ssam#ifndef TDMA_TXRATE_QUARTER_DEFAULT 90188782Ssam#define TDMA_TXRATE_QUARTER_DEFAULT 2*6 91188782Ssam#endif 92187899Ssam#ifndef TDMA_TXRATE_11NA_DEFAULT 93187899Ssam#define TDMA_TXRATE_11NA_DEFAULT (4 | IEEE80211_RATE_MCS) 94187899Ssam#endif 95187899Ssam#ifndef TDMA_TXRATE_11NG_DEFAULT 96187899Ssam#define TDMA_TXRATE_11NG_DEFAULT (4 | IEEE80211_RATE_MCS) 97187899Ssam#endif 98186904Ssam 99190455Ssam#define TDMA_VERSION_VALID(_version) \ 100190455Ssam (TDMA_VERSION_V2 <= (_version) && (_version) <= TDMA_VERSION) 101190455Ssam#define TDMA_SLOTCNT_VALID(_slotcnt) \ 102190455Ssam (2 <= (_slotcnt) && (_slotcnt) <= TDMA_MAXSLOTS) 103190455Ssam/* XXX magic constants */ 104190455Ssam#define TDMA_SLOTLEN_VALID(_slotlen) \ 105190455Ssam (2*100 <= (_slotlen) && (unsigned)(_slotlen) <= 0xfffff) 106190455Ssam/* XXX probably should set a max */ 107190455Ssam#define TDMA_BINTVAL_VALID(_bintval) (1 <= (_bintval)) 108190455Ssam 109193114Ssam/* 110193114Ssam * This code is not prepared to handle more than 2 slots. 111193114Ssam */ 112193114SsamCTASSERT(TDMA_MAXSLOTS == 2); 113193114Ssam 114186904Ssamstatic void tdma_vdetach(struct ieee80211vap *vap); 115186904Ssamstatic int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int); 116186904Ssamstatic void tdma_beacon_miss(struct ieee80211vap *vap); 117186904Ssamstatic void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *, 118192468Ssam int subtype, int rssi, int nf); 119186904Ssamstatic int tdma_update(struct ieee80211vap *vap, 120186904Ssam const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni, 121186904Ssam int pickslot); 122186904Ssamstatic int tdma_process_params(struct ieee80211_node *ni, 123192468Ssam const u_int8_t *ie, int rssi, int nf, const struct ieee80211_frame *wh); 124186904Ssam 125186904Ssamstatic void 126187899Ssamsettxparms(struct ieee80211vap *vap, enum ieee80211_phymode mode, int rate) 127187899Ssam{ 128187899Ssam vap->iv_txparms[mode].ucastrate = rate; 129187899Ssam vap->iv_txparms[mode].mcastrate = rate; 130187899Ssam} 131187899Ssam 132187899Ssamstatic void 133186904Ssamsetackpolicy(struct ieee80211com *ic, int noack) 134186904Ssam{ 135186904Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 136186904Ssam int ac; 137186904Ssam 138186904Ssam for (ac = 0; ac < WME_NUM_AC; ac++) { 139186904Ssam wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 140186904Ssam wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 141186904Ssam } 142186904Ssam} 143186904Ssam 144186904Ssamvoid 145186904Ssamieee80211_tdma_vattach(struct ieee80211vap *vap) 146186904Ssam{ 147186904Ssam struct ieee80211_tdma_state *ts; 148186904Ssam 149186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 150186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 151186904Ssam 152186904Ssam ts = (struct ieee80211_tdma_state *) malloc( 153186904Ssam sizeof(struct ieee80211_tdma_state), M_80211_VAP, M_NOWAIT | M_ZERO); 154186904Ssam if (ts == NULL) { 155186904Ssam printf("%s: cannot allocate TDMA state block\n", __func__); 156186904Ssam /* NB: fall back to adhdemo mode */ 157186904Ssam vap->iv_caps &= ~IEEE80211_C_TDMA; 158186904Ssam return; 159186904Ssam } 160186904Ssam /* NB: default configuration is passive so no beacons */ 161189980Ssam ts->tdma_version = TDMA_VERSION; 162186904Ssam ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT; 163186904Ssam ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT; 164186904Ssam ts->tdma_bintval = TDMA_BINTVAL_DEFAULT; 165186904Ssam ts->tdma_slot = 1; /* passive operation */ 166186904Ssam 167186904Ssam /* setup default fixed rates */ 168187899Ssam settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT); 169187899Ssam settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT); 170187899Ssam settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT); 171191018Ssam settxparms(vap, IEEE80211_MODE_TURBO_A, TDMA_TXRATE_TURBO_DEFAULT); 172191018Ssam settxparms(vap, IEEE80211_MODE_TURBO_G, TDMA_TXRATE_TURBO_DEFAULT); 173191018Ssam settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_TURBO_DEFAULT); 174187899Ssam settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT); 175187899Ssam settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT); 176188782Ssam settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT); 177188782Ssam settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT); 178186904Ssam 179186904Ssam setackpolicy(vap->iv_ic, 1); /* disable ACK's */ 180186904Ssam 181186904Ssam ts->tdma_opdetach = vap->iv_opdetach; 182186904Ssam vap->iv_opdetach = tdma_vdetach; 183186904Ssam ts->tdma_newstate = vap->iv_newstate; 184186904Ssam vap->iv_newstate = tdma_newstate; 185186904Ssam vap->iv_bmiss = tdma_beacon_miss; 186186904Ssam ts->tdma_recv_mgmt = vap->iv_recv_mgmt; 187186904Ssam vap->iv_recv_mgmt = tdma_recv_mgmt; 188186904Ssam 189186904Ssam vap->iv_tdma = ts; 190186904Ssam} 191186904Ssam 192186904Ssamstatic void 193186904Ssamtdma_vdetach(struct ieee80211vap *vap) 194186904Ssam{ 195186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 196186904Ssam 197193114Ssam if (ts == NULL) { 198193114Ssam /* NB: should not have touched any ic state */ 199193114Ssam return; 200193114Ssam } 201186904Ssam ts->tdma_opdetach(vap); 202186904Ssam free(vap->iv_tdma, M_80211_VAP); 203193114Ssam vap->iv_tdma = NULL; 204186904Ssam 205186904Ssam setackpolicy(vap->iv_ic, 0); /* enable ACK's */ 206186904Ssam} 207186904Ssam 208188467Ssamstatic void 209188467Ssamsta_leave(void *arg, struct ieee80211_node *ni) 210188467Ssam{ 211188467Ssam struct ieee80211vap *vap = arg; 212188467Ssam 213188467Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 214188467Ssam ieee80211_node_leave(ni); 215188467Ssam} 216188467Ssam 217186904Ssam/* 218186904Ssam * TDMA state machine handler. 219186904Ssam */ 220186904Ssamstatic int 221186904Ssamtdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 222186904Ssam{ 223186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 224188467Ssam struct ieee80211com *ic = vap->iv_ic; 225186904Ssam enum ieee80211_state ostate; 226186904Ssam int status; 227186904Ssam 228188467Ssam IEEE80211_LOCK_ASSERT(ic); 229186904Ssam 230186904Ssam ostate = vap->iv_state; 231186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 232186904Ssam __func__, ieee80211_state_name[ostate], 233186904Ssam ieee80211_state_name[nstate], arg); 234186904Ssam 235186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 236186904Ssam callout_stop(&vap->iv_swbmiss); 237186904Ssam if (nstate == IEEE80211_S_SCAN && 238186904Ssam (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) && 239186904Ssam ts->tdma_slot != 0) { 240186904Ssam /* 241186904Ssam * Override adhoc behaviour when operating as a slave; 242186904Ssam * we need to scan even if the channel is locked. 243186904Ssam */ 244186904Ssam vap->iv_state = nstate; /* state transition */ 245186904Ssam ieee80211_cancel_scan(vap); /* background scan */ 246188467Ssam if (ostate == IEEE80211_S_RUN) { 247188467Ssam /* purge station table; entries are stale */ 248188467Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 249188467Ssam } 250186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 251186904Ssam ieee80211_check_scan(vap, 252186904Ssam vap->iv_scanreq_flags, 253186904Ssam vap->iv_scanreq_duration, 254186904Ssam vap->iv_scanreq_mindwell, 255186904Ssam vap->iv_scanreq_maxdwell, 256186904Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 257186904Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 258186904Ssam } else 259186904Ssam ieee80211_check_scan_current(vap); 260186904Ssam status = 0; 261186904Ssam } else { 262186904Ssam status = ts->tdma_newstate(vap, nstate, arg); 263186904Ssam } 264186904Ssam if (status == 0 && 265186904Ssam nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN && 266186904Ssam (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) && 267188925Ssam ts->tdma_slot != 0 && 268188925Ssam vap->iv_des_chan == IEEE80211_CHAN_ANYC) { 269186904Ssam /* 270186904Ssam * Start s/w beacon miss timer for slave devices w/o 271188925Ssam * hardware support. Note we do this only if we're 272188925Ssam * not locked to a channel (i.e. roam to follow the 273188925Ssam * master). The 2x is a fudge for our doing this in 274188925Ssam * software. 275186904Ssam */ 276186904Ssam vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 277186904Ssam 2 * vap->iv_bmissthreshold * ts->tdma_bintval * 278186904Ssam ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024)); 279186904Ssam vap->iv_swbmiss_count = 0; 280186904Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 281186904Ssam ieee80211_swbmiss, vap); 282186904Ssam } 283186904Ssam return status; 284186904Ssam} 285186904Ssam 286186904Ssamstatic void 287186904Ssamtdma_beacon_miss(struct ieee80211vap *vap) 288186904Ssam{ 289186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 290186904Ssam 291226296Sadrian IEEE80211_LOCK_ASSERT(vap->iv_ic); 292225913Sadrian 293186904Ssam KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 294186904Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 295186904Ssam ("wrong state %d", vap->iv_state)); 296186904Ssam 297186904Ssam IEEE80211_DPRINTF(vap, 298186904Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG, 299186904Ssam "beacon miss, mode %u state %s\n", 300186904Ssam vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 301186904Ssam 302205140Sweongyo callout_stop(&vap->iv_swbmiss); 303205140Sweongyo 304186904Ssam if (ts->tdma_peer != NULL) { /* XXX? can this be null? */ 305186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 306186904Ssam ts->tdma_peer = NULL; 307186904Ssam /* 308186904Ssam * Treat beacon miss like an associate failure wrt the 309186904Ssam * scan policy; this forces the entry in the scan cache 310186904Ssam * to be ignored after several tries. 311186904Ssam */ 312186904Ssam ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, 313186904Ssam IEEE80211_STATUS_TIMEOUT); 314186904Ssam } 315186904Ssam#if 0 316186904Ssam ts->tdma_inuse = 0; /* clear slot usage */ 317186904Ssam#endif 318186904Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 319186904Ssam} 320186904Ssam 321186904Ssamstatic void 322186904Ssamtdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 323192468Ssam int subtype, int rssi, int nf) 324186904Ssam{ 325186904Ssam struct ieee80211com *ic = ni->ni_ic; 326186904Ssam struct ieee80211vap *vap = ni->ni_vap; 327186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 328186904Ssam 329186904Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && 330186904Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 331186904Ssam struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); 332186904Ssam struct ieee80211_scanparams scan; 333186904Ssam 334186904Ssam if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 335186904Ssam return; 336186904Ssam if (scan.tdma == NULL) { 337186904Ssam /* 338186904Ssam * TDMA stations must beacon a TDMA ie; ignore 339186904Ssam * any other station. 340186904Ssam * XXX detect overlapping bss and change channel 341186904Ssam */ 342186904Ssam IEEE80211_DISCARD(vap, 343186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 344186904Ssam wh, ieee80211_mgt_subtype_name[subtype >> 345186904Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 346186904Ssam "%s", "no TDMA ie"); 347186904Ssam vap->iv_stats.is_rx_mgtdiscard++; 348186904Ssam return; 349186904Ssam } 350186904Ssam if (ni == vap->iv_bss && 351186904Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 352186904Ssam /* 353186904Ssam * Fake up a node for this newly 354186904Ssam * discovered member of the IBSS. 355186904Ssam */ 356186904Ssam ni = ieee80211_add_neighbor(vap, wh, &scan); 357186904Ssam if (ni == NULL) { 358186904Ssam /* NB: stat kept for alloc failure */ 359186904Ssam return; 360186904Ssam } 361186904Ssam } 362186904Ssam /* 363186904Ssam * Check for state updates. 364186904Ssam */ 365191016Ssam if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) { 366186904Ssam /* 367186904Ssam * Count frame now that we know it's to be processed. 368186904Ssam */ 369186904Ssam vap->iv_stats.is_rx_beacon++; 370186904Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 371186904Ssam /* 372186904Ssam * Record tsf of last beacon. NB: this must be 373186904Ssam * done before calling tdma_process_params 374186904Ssam * as deeper routines reference it. 375186904Ssam */ 376186904Ssam memcpy(&ni->ni_tstamp.data, scan.tstamp, 377186904Ssam sizeof(ni->ni_tstamp.data)); 378186904Ssam /* 379186904Ssam * Count beacon frame for s/w bmiss handling. 380186904Ssam */ 381186904Ssam vap->iv_swbmiss_count++; 382186904Ssam /* 383186904Ssam * Process tdma ie. The contents are used to sync 384186904Ssam * the slot timing, reconfigure the bss, etc. 385186904Ssam */ 386192468Ssam (void) tdma_process_params(ni, scan.tdma, rssi, nf, wh); 387186904Ssam return; 388186904Ssam } 389186904Ssam /* 390186904Ssam * NB: defer remaining work to the adhoc code; this causes 391186904Ssam * 2x parsing of the frame but should happen infrequently 392186904Ssam */ 393186904Ssam } 394192468Ssam ts->tdma_recv_mgmt(ni, m0, subtype, rssi, nf); 395186904Ssam} 396186904Ssam 397186904Ssam/* 398186904Ssam * Update TDMA state on receipt of a beacon frame with 399186904Ssam * a TDMA information element. The sender's identity 400186904Ssam * is provided so we can track who our peer is. If pickslot 401186904Ssam * is non-zero we scan the slot allocation state in the ie 402189980Ssam * to locate a free slot for our use. 403186904Ssam */ 404186904Ssamstatic int 405186904Ssamtdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma, 406186904Ssam struct ieee80211_node *ni, int pickslot) 407186904Ssam{ 408186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 409189980Ssam int slot, slotlen, update; 410186904Ssam 411186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 412186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 413186904Ssam 414189980Ssam update = 0; 415189980Ssam if (tdma->tdma_slotcnt != ts->tdma_slotcnt) { 416189980Ssam if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) { 417189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 418189981Ssam printf("%s: bad slot cnt %u\n", 419189981Ssam __func__, tdma->tdma_slotcnt); 420189980Ssam return 0; 421189980Ssam } 422189980Ssam update |= TDMA_UPDATE_SLOTCNT; 423189980Ssam } 424189980Ssam slotlen = le16toh(tdma->tdma_slotlen) * 100; 425189980Ssam if (slotlen != ts->tdma_slotlen) { 426189980Ssam if (!TDMA_SLOTLEN_VALID(slotlen)) { 427189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 428189981Ssam printf("%s: bad slot len %u\n", 429189981Ssam __func__, slotlen); 430189980Ssam return 0; 431189980Ssam } 432189980Ssam update |= TDMA_UPDATE_SLOTLEN; 433189980Ssam } 434189980Ssam if (tdma->tdma_bintval != ts->tdma_bintval) { 435189980Ssam if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) { 436189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 437189981Ssam printf("%s: bad beacon interval %u\n", 438189981Ssam __func__, tdma->tdma_bintval); 439189980Ssam return 0; 440189980Ssam } 441189980Ssam update |= TDMA_UPDATE_BINTVAL; 442189980Ssam } 443189980Ssam slot = ts->tdma_slot; 444186904Ssam if (pickslot) { 445186904Ssam /* 446186904Ssam * Pick unoccupied slot. Note we never choose slot 0. 447186904Ssam */ 448189980Ssam for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--) 449186904Ssam if (isclr(tdma->tdma_inuse, slot)) 450186904Ssam break; 451186904Ssam if (slot <= 0) { 452186904Ssam printf("%s: no free slot, slotcnt %u inuse: 0x%x\n", 453189980Ssam __func__, tdma->tdma_slotcnt, 454189980Ssam tdma->tdma_inuse[0]); 455186904Ssam /* XXX need to do something better */ 456186904Ssam return 0; 457186904Ssam } 458189980Ssam if (slot != ts->tdma_slot) 459189980Ssam update |= TDMA_UPDATE_SLOT; 460189980Ssam } 461189980Ssam if (ni != ts->tdma_peer) { 462189980Ssam /* update everything */ 463189980Ssam update = TDMA_UPDATE_SLOT 464189980Ssam | TDMA_UPDATE_SLOTCNT 465189980Ssam | TDMA_UPDATE_SLOTLEN 466189980Ssam | TDMA_UPDATE_BINTVAL; 467189980Ssam } 468186904Ssam 469189980Ssam if (update) { 470186904Ssam /* 471186904Ssam * New/changed parameters; update runtime state. 472186904Ssam */ 473186904Ssam /* XXX overwrites user parameters */ 474189980Ssam if (update & TDMA_UPDATE_SLOTCNT) 475189980Ssam ts->tdma_slotcnt = tdma->tdma_slotcnt; 476189980Ssam if (update & TDMA_UPDATE_SLOTLEN) 477189980Ssam ts->tdma_slotlen = slotlen; 478189980Ssam if (update & TDMA_UPDATE_SLOT) 479189980Ssam ts->tdma_slot = slot; 480189980Ssam if (update & TDMA_UPDATE_BINTVAL) 481189980Ssam ts->tdma_bintval = tdma->tdma_bintval; 482186904Ssam /* mark beacon to be updated before next xmit */ 483186904Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 484186904Ssam 485186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 486186904Ssam "%s: slot %u slotcnt %u slotlen %u us bintval %u\n", 487189980Ssam __func__, ts->tdma_slot, ts->tdma_slotcnt, 488191017Ssam ts->tdma_slotlen, ts->tdma_bintval); 489186904Ssam } 490186904Ssam /* 491186904Ssam * Notify driver. Note we can be called before 492186904Ssam * entering RUN state if we scanned and are 493186904Ssam * joining an existing bss. In that case do not 494186904Ssam * call the driver because not all necessary state 495186904Ssam * has been setup. The next beacon will dtrt. 496186904Ssam */ 497186904Ssam if (vap->iv_state == IEEE80211_S_RUN) 498189980Ssam vap->iv_ic->ic_tdma_update(ni, tdma, update); 499186904Ssam /* 500186904Ssam * Dispatch join event on first beacon from new master. 501186904Ssam */ 502186904Ssam if (ts->tdma_peer != ni) { 503186904Ssam if (ts->tdma_peer != NULL) 504186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 505186904Ssam ieee80211_notify_node_join(ni, 1); 506186904Ssam /* NB: no reference, we just use the address */ 507186904Ssam ts->tdma_peer = ni; 508186904Ssam } 509186904Ssam return 1; 510186904Ssam} 511186904Ssam 512186904Ssam/* 513186904Ssam * Process received TDMA parameters. 514186904Ssam */ 515186904Ssamstatic int 516192468Ssamtdma_process_params(struct ieee80211_node *ni, const u_int8_t *ie, 517192468Ssam int rssi, int nf, const struct ieee80211_frame *wh) 518186904Ssam{ 519186904Ssam struct ieee80211vap *vap = ni->ni_vap; 520186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 521186904Ssam const struct ieee80211_tdma_param *tdma = 522186904Ssam (const struct ieee80211_tdma_param *) ie; 523186904Ssam u_int len = ie[1]; 524186904Ssam 525186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 526186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 527186904Ssam 528186904Ssam if (len < sizeof(*tdma) - 2) { 529186904Ssam IEEE80211_DISCARD_IE(vap, 530186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 531186904Ssam wh, "tdma", "too short, len %u", len); 532186904Ssam return IEEE80211_REASON_IE_INVALID; 533186904Ssam } 534189980Ssam if (tdma->tdma_version != ts->tdma_version) { 535186904Ssam IEEE80211_DISCARD_IE(vap, 536186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 537189980Ssam wh, "tdma", "bad version %u (ours %u)", 538189980Ssam tdma->tdma_version, ts->tdma_version); 539186904Ssam return IEEE80211_REASON_IE_INVALID; 540186904Ssam } 541189980Ssam /* 542189980Ssam * NB: ideally we'd check against tdma_slotcnt, but that 543189980Ssam * would require extra effort so do this easy check that 544189980Ssam * covers the work below; more stringent checks are done 545189980Ssam * before we make more extensive use of the ie contents. 546189980Ssam */ 547189980Ssam if (tdma->tdma_slot >= TDMA_MAXSLOTS) { 548189980Ssam IEEE80211_DISCARD_IE(vap, 549189980Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 550189980Ssam wh, "tdma", "invalid slot %u", tdma->tdma_slot); 551189980Ssam return IEEE80211_REASON_IE_INVALID; 552189980Ssam } 553186904Ssam /* 554186904Ssam * Can reach here while scanning, update 555186904Ssam * operational state only in RUN state. 556186904Ssam */ 557186904Ssam if (vap->iv_state == IEEE80211_S_RUN) { 558186904Ssam if (tdma->tdma_slot != ts->tdma_slot && 559186904Ssam isclr(ts->tdma_inuse, tdma->tdma_slot)) { 560186904Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni, 561186904Ssam "discovered in slot %u", tdma->tdma_slot); 562186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 563186904Ssam /* XXX dispatch event only when operating as master */ 564186904Ssam if (ts->tdma_slot == 0) 565186904Ssam ieee80211_notify_node_join(ni, 1); 566186904Ssam } 567186904Ssam setbit(ts->tdma_active, tdma->tdma_slot); 568186904Ssam if (tdma->tdma_slot == ts->tdma_slot-1) { 569186904Ssam /* 570186904Ssam * Slave tsf synchronization to station 571186904Ssam * just before us in the schedule. The driver 572186904Ssam * is responsible for copying the timestamp 573186904Ssam * of the received beacon into our beacon 574186904Ssam * frame so the sender can calculate round 575186904Ssam * trip time. We cannot do that here because 576186904Ssam * we don't know how to update our beacon frame. 577186904Ssam */ 578186904Ssam (void) tdma_update(vap, tdma, ni, 0); 579186904Ssam /* XXX reschedule swbmiss timer on parameter change */ 580186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot+1) { 581186904Ssam uint64_t tstamp; 582192468Ssam#if 0 583192468Ssam uint32_t rstamp = (uint32_t) le64toh(rs->tsf); 584186904Ssam int32_t rtt; 585192468Ssam#endif 586186904Ssam /* 587186904Ssam * Use returned timstamp to calculate the 588186904Ssam * roundtrip time. 589186904Ssam */ 590186904Ssam memcpy(&tstamp, tdma->tdma_tstamp, 8); 591192468Ssam#if 0 592186904Ssam /* XXX use only 15 bits of rstamp */ 593186904Ssam rtt = rstamp - (le64toh(tstamp) & 0x7fff); 594186904Ssam if (rtt < 0) 595186904Ssam rtt += 0x7fff; 596186904Ssam /* XXX hack to quiet normal use */ 597186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X, 598186904Ssam "tdma rtt %5u [rstamp %5u tstamp %llu]\n", 599186904Ssam rtt, rstamp, 600186904Ssam (unsigned long long) le64toh(tstamp)); 601192468Ssam#endif 602186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot && 603186904Ssam le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) { 604186904Ssam /* 605186904Ssam * Station using the same slot as us and has 606186904Ssam * been around longer than us; we must move. 607186904Ssam * Note this can happen if stations do not 608186904Ssam * see each other while scanning. 609186904Ssam */ 610186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 611186904Ssam "slot %u collision rxtsf %llu tsf %llu\n", 612186904Ssam tdma->tdma_slot, 613186904Ssam (unsigned long long) le64toh(ni->ni_tstamp.tsf), 614186904Ssam vap->iv_bss->ni_tstamp.tsf); 615186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 616186904Ssam 617186904Ssam (void) tdma_update(vap, tdma, ni, 1); 618186904Ssam } 619186904Ssam } 620186904Ssam return 0; 621186904Ssam} 622186904Ssam 623186904Ssamint 624186904Ssamieee80211_tdma_getslot(struct ieee80211vap *vap) 625186904Ssam{ 626186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 627186904Ssam 628186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 629186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 630186904Ssam return ts->tdma_slot; 631186904Ssam} 632186904Ssam 633186904Ssam/* 634186904Ssam * Parse a TDMA ie on station join and use it to setup node state. 635186904Ssam */ 636186904Ssamvoid 637186904Ssamieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie) 638186904Ssam{ 639186904Ssam struct ieee80211vap *vap = ni->ni_vap; 640186904Ssam 641186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 642186904Ssam const struct ieee80211_tdma_param *tdma = 643186904Ssam (const struct ieee80211_tdma_param *)ie; 644186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 645186904Ssam /* 646186904Ssam * Adopt TDMA configuration when joining an 647186904Ssam * existing network. 648186904Ssam */ 649186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 650186904Ssam (void) tdma_update(vap, tdma, ni, 1); 651186904Ssam /* 652186904Ssam * Propagate capabilities based on the local 653186904Ssam * configuration and the remote station's advertised 654186904Ssam * capabilities. In particular this permits us to 655186904Ssam * enable use of QoS to disable ACK's. 656186904Ssam */ 657186904Ssam if ((vap->iv_flags & IEEE80211_F_WME) && 658186904Ssam ni->ni_ies.wme_ie != NULL) 659186904Ssam ni->ni_flags |= IEEE80211_NODE_QOS; 660186904Ssam } 661186904Ssam} 662186904Ssam 663186904Ssam#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f 664186904Ssam/* 665186904Ssam * Add a TDMA parameters element to a frame. 666186904Ssam */ 667186904Ssamuint8_t * 668186904Ssamieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap) 669186904Ssam{ 670186904Ssam#define ADDSHORT(frm, v) do { \ 671186904Ssam frm[0] = (v) & 0xff; \ 672186904Ssam frm[1] = (v) >> 8; \ 673186904Ssam frm += 2; \ 674186904Ssam} while (0) 675186904Ssam static const struct ieee80211_tdma_param param = { 676186904Ssam .tdma_id = IEEE80211_ELEMID_VENDOR, 677186904Ssam .tdma_len = sizeof(struct ieee80211_tdma_param) - 2, 678186904Ssam .tdma_oui = { TDMA_OUI_BYTES }, 679186904Ssam .tdma_type = TDMA_OUI_TYPE, 680186904Ssam .tdma_subtype = TDMA_SUBTYPE_PARAM, 681186904Ssam .tdma_version = TDMA_VERSION, 682186904Ssam }; 683189980Ssam const struct ieee80211_tdma_state *ts = vap->iv_tdma; 684186904Ssam uint16_t slotlen; 685186904Ssam 686186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 687186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 688186904Ssam 689186904Ssam memcpy(frm, ¶m, sizeof(param)); 690186904Ssam frm += __offsetof(struct ieee80211_tdma_param, tdma_slot); 691189980Ssam *frm++ = ts->tdma_slot; 692189980Ssam *frm++ = ts->tdma_slotcnt; 693186904Ssam /* NB: convert units to fit in 16-bits */ 694189980Ssam slotlen = ts->tdma_slotlen / 100; /* 100us units */ 695186904Ssam ADDSHORT(frm, slotlen); 696189980Ssam *frm++ = ts->tdma_bintval; 697189980Ssam *frm++ = ts->tdma_inuse[0]; 698186904Ssam frm += 10; /* pad+timestamp */ 699186904Ssam return frm; 700186904Ssam#undef ADDSHORT 701186904Ssam} 702186904Ssam#undef TDMA_OUI_BYTES 703186904Ssam 704186904Ssam/* 705186904Ssam * Update TDMA state at TBTT. 706186904Ssam */ 707186904Ssamvoid 708186904Ssamieee80211_tdma_update_beacon(struct ieee80211vap *vap, 709186904Ssam struct ieee80211_beacon_offsets *bo) 710186904Ssam{ 711186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 712186904Ssam 713186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 714186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 715186904Ssam 716186904Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) { 717186904Ssam (void) ieee80211_add_tdma(bo->bo_tdma, vap); 718186904Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 719186904Ssam } 720186904Ssam if (ts->tdma_slot != 0) /* only on master */ 721186904Ssam return; 722186904Ssam if (ts->tdma_count <= 0) { 723186904Ssam /* 724186904Ssam * Time to update the mask of active/inuse stations. 725186904Ssam * We track stations that we've received a beacon 726186904Ssam * frame from and update this mask periodically. 727186904Ssam * This allows us to miss a few beacons before marking 728186904Ssam * a slot free for re-use. 729186904Ssam */ 730186904Ssam ts->tdma_inuse[0] = ts->tdma_active[0]; 731186904Ssam ts->tdma_active[0] = 0x01; 732186904Ssam /* update next time 'round */ 733186904Ssam /* XXX use notify framework */ 734186904Ssam setbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 735186904Ssam /* NB: use s/w beacon miss threshold; may be too high */ 736186904Ssam ts->tdma_count = vap->iv_bmissthreshold-1; 737186904Ssam } else 738186904Ssam ts->tdma_count--; 739186904Ssam} 740186904Ssam 741190384Ssamstatic int 742190384Ssamtdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 743186904Ssam{ 744186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 745186904Ssam 746186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 747254506Sadrian return ENOSYS; 748186904Ssam 749186904Ssam switch (ireq->i_type) { 750186904Ssam case IEEE80211_IOC_TDMA_SLOT: 751186904Ssam ireq->i_val = ts->tdma_slot; 752186904Ssam break; 753186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 754186904Ssam ireq->i_val = ts->tdma_slotcnt; 755186904Ssam break; 756186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 757186904Ssam ireq->i_val = ts->tdma_slotlen; 758186904Ssam break; 759186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 760186904Ssam ireq->i_val = ts->tdma_bintval; 761186904Ssam break; 762186904Ssam default: 763190384Ssam return ENOSYS; 764186904Ssam } 765186904Ssam return 0; 766186904Ssam} 767190384SsamIEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211); 768186904Ssam 769190384Ssamstatic int 770190384Ssamtdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 771186904Ssam{ 772186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 773186904Ssam 774186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 775254506Sadrian return ENOSYS; 776186904Ssam 777186904Ssam switch (ireq->i_type) { 778186904Ssam case IEEE80211_IOC_TDMA_SLOT: 779186904Ssam if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt)) 780186904Ssam return EINVAL; 781186904Ssam if (ireq->i_val != ts->tdma_slot) { 782186904Ssam ts->tdma_slot = ireq->i_val; 783193114Ssam goto restart; 784186904Ssam } 785186904Ssam break; 786186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 787189980Ssam if (!TDMA_SLOTCNT_VALID(ireq->i_val)) 788186904Ssam return EINVAL; 789186904Ssam if (ireq->i_val != ts->tdma_slotcnt) { 790186904Ssam ts->tdma_slotcnt = ireq->i_val; 791193114Ssam goto restart; 792186904Ssam } 793186904Ssam break; 794186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 795186904Ssam /* 796186904Ssam * XXX 797186904Ssam * 150 insures at least 1/8 TU 798186904Ssam * 0xfffff is the max duration for bursting 799186904Ssam * (implict by way of 16-bit data type for i_val) 800186904Ssam */ 801189980Ssam if (!TDMA_SLOTLEN_VALID(ireq->i_val)) 802186904Ssam return EINVAL; 803186904Ssam if (ireq->i_val != ts->tdma_slotlen) { 804186904Ssam ts->tdma_slotlen = ireq->i_val; 805193114Ssam goto restart; 806186904Ssam } 807186904Ssam break; 808186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 809189980Ssam if (!TDMA_BINTVAL_VALID(ireq->i_val)) 810186904Ssam return EINVAL; 811186904Ssam if (ireq->i_val != ts->tdma_bintval) { 812186904Ssam ts->tdma_bintval = ireq->i_val; 813193114Ssam goto restart; 814186904Ssam } 815186904Ssam break; 816186904Ssam default: 817190384Ssam return ENOSYS; 818186904Ssam } 819186904Ssam return 0; 820193114Ssamrestart: 821193114Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 822193114Ssam return ERESTART; 823186904Ssam} 824190384SsamIEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211); 825230153Sadrian 826230153Sadrian#endif /* IEEE80211_SUPPORT_TDMA */ 827