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: stable/11/sys/net80211/ieee80211_tdma.c 344225 2019-02-17 03:21:42Z avos $"); 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 *, 118283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, 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{ 128344225Savos if (isclr(vap->iv_ic->ic_modecaps, mode)) 129344225Savos return; 130344225Savos 131187899Ssam vap->iv_txparms[mode].ucastrate = rate; 132187899Ssam vap->iv_txparms[mode].mcastrate = rate; 133187899Ssam} 134187899Ssam 135187899Ssamstatic void 136186904Ssamsetackpolicy(struct ieee80211com *ic, int noack) 137186904Ssam{ 138186904Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 139186904Ssam int ac; 140186904Ssam 141186904Ssam for (ac = 0; ac < WME_NUM_AC; ac++) { 142186904Ssam wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 143186904Ssam wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack; 144186904Ssam } 145186904Ssam} 146186904Ssam 147186904Ssamvoid 148186904Ssamieee80211_tdma_vattach(struct ieee80211vap *vap) 149186904Ssam{ 150186904Ssam struct ieee80211_tdma_state *ts; 151186904Ssam 152186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 153186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 154186904Ssam 155283538Sadrian ts = (struct ieee80211_tdma_state *) IEEE80211_MALLOC( 156283538Sadrian sizeof(struct ieee80211_tdma_state), M_80211_VAP, 157283538Sadrian IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 158186904Ssam if (ts == NULL) { 159186904Ssam printf("%s: cannot allocate TDMA state block\n", __func__); 160186904Ssam /* NB: fall back to adhdemo mode */ 161186904Ssam vap->iv_caps &= ~IEEE80211_C_TDMA; 162186904Ssam return; 163186904Ssam } 164186904Ssam /* NB: default configuration is passive so no beacons */ 165189980Ssam ts->tdma_version = TDMA_VERSION; 166186904Ssam ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT; 167186904Ssam ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT; 168186904Ssam ts->tdma_bintval = TDMA_BINTVAL_DEFAULT; 169186904Ssam ts->tdma_slot = 1; /* passive operation */ 170186904Ssam 171186904Ssam /* setup default fixed rates */ 172187899Ssam settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT); 173187899Ssam settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT); 174187899Ssam settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT); 175191018Ssam settxparms(vap, IEEE80211_MODE_TURBO_A, TDMA_TXRATE_TURBO_DEFAULT); 176191018Ssam settxparms(vap, IEEE80211_MODE_TURBO_G, TDMA_TXRATE_TURBO_DEFAULT); 177191018Ssam settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_TURBO_DEFAULT); 178187899Ssam settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT); 179187899Ssam settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT); 180188782Ssam settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT); 181188782Ssam settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT); 182186904Ssam 183186904Ssam setackpolicy(vap->iv_ic, 1); /* disable ACK's */ 184186904Ssam 185186904Ssam ts->tdma_opdetach = vap->iv_opdetach; 186186904Ssam vap->iv_opdetach = tdma_vdetach; 187186904Ssam ts->tdma_newstate = vap->iv_newstate; 188186904Ssam vap->iv_newstate = tdma_newstate; 189186904Ssam vap->iv_bmiss = tdma_beacon_miss; 190186904Ssam ts->tdma_recv_mgmt = vap->iv_recv_mgmt; 191186904Ssam vap->iv_recv_mgmt = tdma_recv_mgmt; 192186904Ssam 193186904Ssam vap->iv_tdma = ts; 194186904Ssam} 195186904Ssam 196186904Ssamstatic void 197186904Ssamtdma_vdetach(struct ieee80211vap *vap) 198186904Ssam{ 199186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 200186904Ssam 201193114Ssam if (ts == NULL) { 202193114Ssam /* NB: should not have touched any ic state */ 203193114Ssam return; 204193114Ssam } 205186904Ssam ts->tdma_opdetach(vap); 206283538Sadrian IEEE80211_FREE(vap->iv_tdma, M_80211_VAP); 207193114Ssam vap->iv_tdma = NULL; 208186904Ssam 209186904Ssam setackpolicy(vap->iv_ic, 0); /* enable ACK's */ 210186904Ssam} 211186904Ssam 212188467Ssamstatic void 213188467Ssamsta_leave(void *arg, struct ieee80211_node *ni) 214188467Ssam{ 215188467Ssam struct ieee80211vap *vap = arg; 216188467Ssam 217188467Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 218188467Ssam ieee80211_node_leave(ni); 219188467Ssam} 220188467Ssam 221186904Ssam/* 222186904Ssam * TDMA state machine handler. 223186904Ssam */ 224186904Ssamstatic int 225186904Ssamtdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 226186904Ssam{ 227186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 228188467Ssam struct ieee80211com *ic = vap->iv_ic; 229186904Ssam enum ieee80211_state ostate; 230186904Ssam int status; 231186904Ssam 232188467Ssam IEEE80211_LOCK_ASSERT(ic); 233186904Ssam 234186904Ssam ostate = vap->iv_state; 235186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 236186904Ssam __func__, ieee80211_state_name[ostate], 237186904Ssam ieee80211_state_name[nstate], arg); 238186904Ssam 239186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 240186904Ssam callout_stop(&vap->iv_swbmiss); 241186904Ssam if (nstate == IEEE80211_S_SCAN && 242186904Ssam (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) && 243186904Ssam ts->tdma_slot != 0) { 244186904Ssam /* 245186904Ssam * Override adhoc behaviour when operating as a slave; 246186904Ssam * we need to scan even if the channel is locked. 247186904Ssam */ 248186904Ssam vap->iv_state = nstate; /* state transition */ 249186904Ssam ieee80211_cancel_scan(vap); /* background scan */ 250188467Ssam if (ostate == IEEE80211_S_RUN) { 251188467Ssam /* purge station table; entries are stale */ 252188467Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 253188467Ssam } 254186904Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 255186904Ssam ieee80211_check_scan(vap, 256186904Ssam vap->iv_scanreq_flags, 257186904Ssam vap->iv_scanreq_duration, 258186904Ssam vap->iv_scanreq_mindwell, 259186904Ssam vap->iv_scanreq_maxdwell, 260186904Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 261186904Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 262186904Ssam } else 263186904Ssam ieee80211_check_scan_current(vap); 264186904Ssam status = 0; 265186904Ssam } else { 266186904Ssam status = ts->tdma_newstate(vap, nstate, arg); 267186904Ssam } 268186904Ssam if (status == 0 && 269186904Ssam nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN && 270186904Ssam (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) && 271188925Ssam ts->tdma_slot != 0 && 272188925Ssam vap->iv_des_chan == IEEE80211_CHAN_ANYC) { 273186904Ssam /* 274186904Ssam * Start s/w beacon miss timer for slave devices w/o 275188925Ssam * hardware support. Note we do this only if we're 276188925Ssam * not locked to a channel (i.e. roam to follow the 277188925Ssam * master). The 2x is a fudge for our doing this in 278188925Ssam * software. 279186904Ssam */ 280186904Ssam vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 281186904Ssam 2 * vap->iv_bmissthreshold * ts->tdma_bintval * 282186904Ssam ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024)); 283186904Ssam vap->iv_swbmiss_count = 0; 284186904Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 285186904Ssam ieee80211_swbmiss, vap); 286186904Ssam } 287186904Ssam return status; 288186904Ssam} 289186904Ssam 290186904Ssamstatic void 291186904Ssamtdma_beacon_miss(struct ieee80211vap *vap) 292186904Ssam{ 293186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 294186904Ssam 295226296Sadrian IEEE80211_LOCK_ASSERT(vap->iv_ic); 296225913Sadrian 297186904Ssam KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 298186904Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 299186904Ssam ("wrong state %d", vap->iv_state)); 300186904Ssam 301186904Ssam IEEE80211_DPRINTF(vap, 302186904Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG, 303186904Ssam "beacon miss, mode %u state %s\n", 304186904Ssam vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 305186904Ssam 306205140Sweongyo callout_stop(&vap->iv_swbmiss); 307205140Sweongyo 308186904Ssam if (ts->tdma_peer != NULL) { /* XXX? can this be null? */ 309186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 310186904Ssam ts->tdma_peer = NULL; 311186904Ssam /* 312186904Ssam * Treat beacon miss like an associate failure wrt the 313186904Ssam * scan policy; this forces the entry in the scan cache 314186904Ssam * to be ignored after several tries. 315186904Ssam */ 316186904Ssam ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, 317186904Ssam IEEE80211_STATUS_TIMEOUT); 318186904Ssam } 319186904Ssam#if 0 320186904Ssam ts->tdma_inuse = 0; /* clear slot usage */ 321186904Ssam#endif 322186904Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 323186904Ssam} 324186904Ssam 325186904Ssamstatic void 326186904Ssamtdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 327283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) 328186904Ssam{ 329186904Ssam struct ieee80211com *ic = ni->ni_ic; 330186904Ssam struct ieee80211vap *vap = ni->ni_vap; 331186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 332186904Ssam 333186904Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && 334186904Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 335186904Ssam struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); 336186904Ssam struct ieee80211_scanparams scan; 337186904Ssam 338283535Sadrian /* XXX TODO: use rxstatus to determine off-channel beacons */ 339283535Sadrian if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) != 0) 340186904Ssam return; 341186904Ssam if (scan.tdma == NULL) { 342186904Ssam /* 343186904Ssam * TDMA stations must beacon a TDMA ie; ignore 344186904Ssam * any other station. 345186904Ssam * XXX detect overlapping bss and change channel 346186904Ssam */ 347186904Ssam IEEE80211_DISCARD(vap, 348186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 349298376Savos wh, ieee80211_mgt_subtype_name(subtype), 350186904Ssam "%s", "no TDMA ie"); 351186904Ssam vap->iv_stats.is_rx_mgtdiscard++; 352186904Ssam return; 353186904Ssam } 354186904Ssam if (ni == vap->iv_bss && 355186904Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 356186904Ssam /* 357186904Ssam * Fake up a node for this newly 358186904Ssam * discovered member of the IBSS. 359186904Ssam */ 360186904Ssam ni = ieee80211_add_neighbor(vap, wh, &scan); 361186904Ssam if (ni == NULL) { 362186904Ssam /* NB: stat kept for alloc failure */ 363186904Ssam return; 364186904Ssam } 365186904Ssam } 366186904Ssam /* 367186904Ssam * Check for state updates. 368186904Ssam */ 369191016Ssam if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) { 370186904Ssam /* 371186904Ssam * Count frame now that we know it's to be processed. 372186904Ssam */ 373186904Ssam vap->iv_stats.is_rx_beacon++; 374186904Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 375186904Ssam /* 376186904Ssam * Record tsf of last beacon. NB: this must be 377186904Ssam * done before calling tdma_process_params 378186904Ssam * as deeper routines reference it. 379186904Ssam */ 380186904Ssam memcpy(&ni->ni_tstamp.data, scan.tstamp, 381186904Ssam sizeof(ni->ni_tstamp.data)); 382186904Ssam /* 383186904Ssam * Count beacon frame for s/w bmiss handling. 384186904Ssam */ 385186904Ssam vap->iv_swbmiss_count++; 386186904Ssam /* 387186904Ssam * Process tdma ie. The contents are used to sync 388186904Ssam * the slot timing, reconfigure the bss, etc. 389186904Ssam */ 390192468Ssam (void) tdma_process_params(ni, scan.tdma, rssi, nf, wh); 391186904Ssam return; 392186904Ssam } 393186904Ssam /* 394186904Ssam * NB: defer remaining work to the adhoc code; this causes 395186904Ssam * 2x parsing of the frame but should happen infrequently 396186904Ssam */ 397186904Ssam } 398283535Sadrian ts->tdma_recv_mgmt(ni, m0, subtype, rxs, rssi, nf); 399186904Ssam} 400186904Ssam 401186904Ssam/* 402186904Ssam * Update TDMA state on receipt of a beacon frame with 403186904Ssam * a TDMA information element. The sender's identity 404186904Ssam * is provided so we can track who our peer is. If pickslot 405186904Ssam * is non-zero we scan the slot allocation state in the ie 406189980Ssam * to locate a free slot for our use. 407186904Ssam */ 408186904Ssamstatic int 409186904Ssamtdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma, 410186904Ssam struct ieee80211_node *ni, int pickslot) 411186904Ssam{ 412186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 413189980Ssam int slot, slotlen, update; 414186904Ssam 415186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 416186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 417186904Ssam 418189980Ssam update = 0; 419189980Ssam if (tdma->tdma_slotcnt != ts->tdma_slotcnt) { 420189980Ssam if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) { 421189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 422189981Ssam printf("%s: bad slot cnt %u\n", 423189981Ssam __func__, tdma->tdma_slotcnt); 424189980Ssam return 0; 425189980Ssam } 426189980Ssam update |= TDMA_UPDATE_SLOTCNT; 427189980Ssam } 428189980Ssam slotlen = le16toh(tdma->tdma_slotlen) * 100; 429189980Ssam if (slotlen != ts->tdma_slotlen) { 430189980Ssam if (!TDMA_SLOTLEN_VALID(slotlen)) { 431189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 432189981Ssam printf("%s: bad slot len %u\n", 433189981Ssam __func__, slotlen); 434189980Ssam return 0; 435189980Ssam } 436189980Ssam update |= TDMA_UPDATE_SLOTLEN; 437189980Ssam } 438189980Ssam if (tdma->tdma_bintval != ts->tdma_bintval) { 439189980Ssam if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) { 440189981Ssam if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1)) 441189981Ssam printf("%s: bad beacon interval %u\n", 442189981Ssam __func__, tdma->tdma_bintval); 443189980Ssam return 0; 444189980Ssam } 445189980Ssam update |= TDMA_UPDATE_BINTVAL; 446189980Ssam } 447189980Ssam slot = ts->tdma_slot; 448186904Ssam if (pickslot) { 449186904Ssam /* 450186904Ssam * Pick unoccupied slot. Note we never choose slot 0. 451186904Ssam */ 452189980Ssam for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--) 453186904Ssam if (isclr(tdma->tdma_inuse, slot)) 454186904Ssam break; 455186904Ssam if (slot <= 0) { 456186904Ssam printf("%s: no free slot, slotcnt %u inuse: 0x%x\n", 457189980Ssam __func__, tdma->tdma_slotcnt, 458189980Ssam tdma->tdma_inuse[0]); 459186904Ssam /* XXX need to do something better */ 460186904Ssam return 0; 461186904Ssam } 462189980Ssam if (slot != ts->tdma_slot) 463189980Ssam update |= TDMA_UPDATE_SLOT; 464189980Ssam } 465189980Ssam if (ni != ts->tdma_peer) { 466189980Ssam /* update everything */ 467189980Ssam update = TDMA_UPDATE_SLOT 468189980Ssam | TDMA_UPDATE_SLOTCNT 469189980Ssam | TDMA_UPDATE_SLOTLEN 470189980Ssam | TDMA_UPDATE_BINTVAL; 471189980Ssam } 472186904Ssam 473189980Ssam if (update) { 474186904Ssam /* 475186904Ssam * New/changed parameters; update runtime state. 476186904Ssam */ 477186904Ssam /* XXX overwrites user parameters */ 478189980Ssam if (update & TDMA_UPDATE_SLOTCNT) 479189980Ssam ts->tdma_slotcnt = tdma->tdma_slotcnt; 480189980Ssam if (update & TDMA_UPDATE_SLOTLEN) 481189980Ssam ts->tdma_slotlen = slotlen; 482189980Ssam if (update & TDMA_UPDATE_SLOT) 483189980Ssam ts->tdma_slot = slot; 484189980Ssam if (update & TDMA_UPDATE_BINTVAL) 485189980Ssam ts->tdma_bintval = tdma->tdma_bintval; 486186904Ssam /* mark beacon to be updated before next xmit */ 487186904Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 488186904Ssam 489186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 490186904Ssam "%s: slot %u slotcnt %u slotlen %u us bintval %u\n", 491189980Ssam __func__, ts->tdma_slot, ts->tdma_slotcnt, 492191017Ssam ts->tdma_slotlen, ts->tdma_bintval); 493186904Ssam } 494186904Ssam /* 495186904Ssam * Notify driver. Note we can be called before 496186904Ssam * entering RUN state if we scanned and are 497186904Ssam * joining an existing bss. In that case do not 498186904Ssam * call the driver because not all necessary state 499186904Ssam * has been setup. The next beacon will dtrt. 500186904Ssam */ 501186904Ssam if (vap->iv_state == IEEE80211_S_RUN) 502189980Ssam vap->iv_ic->ic_tdma_update(ni, tdma, update); 503186904Ssam /* 504186904Ssam * Dispatch join event on first beacon from new master. 505186904Ssam */ 506186904Ssam if (ts->tdma_peer != ni) { 507186904Ssam if (ts->tdma_peer != NULL) 508186904Ssam ieee80211_notify_node_leave(vap->iv_bss); 509186904Ssam ieee80211_notify_node_join(ni, 1); 510186904Ssam /* NB: no reference, we just use the address */ 511186904Ssam ts->tdma_peer = ni; 512186904Ssam } 513186904Ssam return 1; 514186904Ssam} 515186904Ssam 516186904Ssam/* 517186904Ssam * Process received TDMA parameters. 518186904Ssam */ 519186904Ssamstatic int 520192468Ssamtdma_process_params(struct ieee80211_node *ni, const u_int8_t *ie, 521192468Ssam int rssi, int nf, const struct ieee80211_frame *wh) 522186904Ssam{ 523186904Ssam struct ieee80211vap *vap = ni->ni_vap; 524186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 525186904Ssam const struct ieee80211_tdma_param *tdma = 526186904Ssam (const struct ieee80211_tdma_param *) ie; 527186904Ssam u_int len = ie[1]; 528186904Ssam 529186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 530186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 531186904Ssam 532186904Ssam if (len < sizeof(*tdma) - 2) { 533186904Ssam IEEE80211_DISCARD_IE(vap, 534186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 535186904Ssam wh, "tdma", "too short, len %u", len); 536186904Ssam return IEEE80211_REASON_IE_INVALID; 537186904Ssam } 538189980Ssam if (tdma->tdma_version != ts->tdma_version) { 539186904Ssam IEEE80211_DISCARD_IE(vap, 540186904Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 541189980Ssam wh, "tdma", "bad version %u (ours %u)", 542189980Ssam tdma->tdma_version, ts->tdma_version); 543186904Ssam return IEEE80211_REASON_IE_INVALID; 544186904Ssam } 545189980Ssam /* 546189980Ssam * NB: ideally we'd check against tdma_slotcnt, but that 547189980Ssam * would require extra effort so do this easy check that 548189980Ssam * covers the work below; more stringent checks are done 549189980Ssam * before we make more extensive use of the ie contents. 550189980Ssam */ 551189980Ssam if (tdma->tdma_slot >= TDMA_MAXSLOTS) { 552189980Ssam IEEE80211_DISCARD_IE(vap, 553189980Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA, 554189980Ssam wh, "tdma", "invalid slot %u", tdma->tdma_slot); 555189980Ssam return IEEE80211_REASON_IE_INVALID; 556189980Ssam } 557186904Ssam /* 558186904Ssam * Can reach here while scanning, update 559186904Ssam * operational state only in RUN state. 560186904Ssam */ 561186904Ssam if (vap->iv_state == IEEE80211_S_RUN) { 562186904Ssam if (tdma->tdma_slot != ts->tdma_slot && 563186904Ssam isclr(ts->tdma_inuse, tdma->tdma_slot)) { 564186904Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni, 565186904Ssam "discovered in slot %u", tdma->tdma_slot); 566186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 567186904Ssam /* XXX dispatch event only when operating as master */ 568186904Ssam if (ts->tdma_slot == 0) 569186904Ssam ieee80211_notify_node_join(ni, 1); 570186904Ssam } 571186904Ssam setbit(ts->tdma_active, tdma->tdma_slot); 572186904Ssam if (tdma->tdma_slot == ts->tdma_slot-1) { 573186904Ssam /* 574186904Ssam * Slave tsf synchronization to station 575186904Ssam * just before us in the schedule. The driver 576186904Ssam * is responsible for copying the timestamp 577186904Ssam * of the received beacon into our beacon 578186904Ssam * frame so the sender can calculate round 579186904Ssam * trip time. We cannot do that here because 580186904Ssam * we don't know how to update our beacon frame. 581186904Ssam */ 582186904Ssam (void) tdma_update(vap, tdma, ni, 0); 583186904Ssam /* XXX reschedule swbmiss timer on parameter change */ 584186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot+1) { 585186904Ssam uint64_t tstamp; 586192468Ssam#if 0 587192468Ssam uint32_t rstamp = (uint32_t) le64toh(rs->tsf); 588186904Ssam int32_t rtt; 589192468Ssam#endif 590186904Ssam /* 591186904Ssam * Use returned timstamp to calculate the 592186904Ssam * roundtrip time. 593186904Ssam */ 594186904Ssam memcpy(&tstamp, tdma->tdma_tstamp, 8); 595192468Ssam#if 0 596186904Ssam /* XXX use only 15 bits of rstamp */ 597186904Ssam rtt = rstamp - (le64toh(tstamp) & 0x7fff); 598186904Ssam if (rtt < 0) 599186904Ssam rtt += 0x7fff; 600186904Ssam /* XXX hack to quiet normal use */ 601186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X, 602186904Ssam "tdma rtt %5u [rstamp %5u tstamp %llu]\n", 603186904Ssam rtt, rstamp, 604186904Ssam (unsigned long long) le64toh(tstamp)); 605192468Ssam#endif 606186904Ssam } else if (tdma->tdma_slot == ts->tdma_slot && 607186904Ssam le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) { 608186904Ssam /* 609186904Ssam * Station using the same slot as us and has 610186904Ssam * been around longer than us; we must move. 611186904Ssam * Note this can happen if stations do not 612186904Ssam * see each other while scanning. 613186904Ssam */ 614186904Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA, 615186904Ssam "slot %u collision rxtsf %llu tsf %llu\n", 616186904Ssam tdma->tdma_slot, 617186904Ssam (unsigned long long) le64toh(ni->ni_tstamp.tsf), 618186904Ssam vap->iv_bss->ni_tstamp.tsf); 619186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 620186904Ssam 621186904Ssam (void) tdma_update(vap, tdma, ni, 1); 622186904Ssam } 623186904Ssam } 624186904Ssam return 0; 625186904Ssam} 626186904Ssam 627186904Ssamint 628186904Ssamieee80211_tdma_getslot(struct ieee80211vap *vap) 629186904Ssam{ 630186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 631186904Ssam 632186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 633186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 634186904Ssam return ts->tdma_slot; 635186904Ssam} 636186904Ssam 637186904Ssam/* 638186904Ssam * Parse a TDMA ie on station join and use it to setup node state. 639186904Ssam */ 640186904Ssamvoid 641186904Ssamieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie) 642186904Ssam{ 643186904Ssam struct ieee80211vap *vap = ni->ni_vap; 644186904Ssam 645186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 646186904Ssam const struct ieee80211_tdma_param *tdma = 647186904Ssam (const struct ieee80211_tdma_param *)ie; 648186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 649186904Ssam /* 650186904Ssam * Adopt TDMA configuration when joining an 651186904Ssam * existing network. 652186904Ssam */ 653186904Ssam setbit(ts->tdma_inuse, tdma->tdma_slot); 654186904Ssam (void) tdma_update(vap, tdma, ni, 1); 655186904Ssam /* 656186904Ssam * Propagate capabilities based on the local 657186904Ssam * configuration and the remote station's advertised 658186904Ssam * capabilities. In particular this permits us to 659186904Ssam * enable use of QoS to disable ACK's. 660186904Ssam */ 661186904Ssam if ((vap->iv_flags & IEEE80211_F_WME) && 662186904Ssam ni->ni_ies.wme_ie != NULL) 663186904Ssam ni->ni_flags |= IEEE80211_NODE_QOS; 664186904Ssam } 665186904Ssam} 666186904Ssam 667186904Ssam#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f 668186904Ssam/* 669186904Ssam * Add a TDMA parameters element to a frame. 670186904Ssam */ 671186904Ssamuint8_t * 672186904Ssamieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap) 673186904Ssam{ 674186904Ssam#define ADDSHORT(frm, v) do { \ 675186904Ssam frm[0] = (v) & 0xff; \ 676186904Ssam frm[1] = (v) >> 8; \ 677186904Ssam frm += 2; \ 678186904Ssam} while (0) 679186904Ssam static const struct ieee80211_tdma_param param = { 680186904Ssam .tdma_id = IEEE80211_ELEMID_VENDOR, 681186904Ssam .tdma_len = sizeof(struct ieee80211_tdma_param) - 2, 682186904Ssam .tdma_oui = { TDMA_OUI_BYTES }, 683186904Ssam .tdma_type = TDMA_OUI_TYPE, 684186904Ssam .tdma_subtype = TDMA_SUBTYPE_PARAM, 685186904Ssam .tdma_version = TDMA_VERSION, 686186904Ssam }; 687189980Ssam const struct ieee80211_tdma_state *ts = vap->iv_tdma; 688186904Ssam uint16_t slotlen; 689186904Ssam 690186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 691186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 692186904Ssam 693186904Ssam memcpy(frm, ¶m, sizeof(param)); 694186904Ssam frm += __offsetof(struct ieee80211_tdma_param, tdma_slot); 695189980Ssam *frm++ = ts->tdma_slot; 696189980Ssam *frm++ = ts->tdma_slotcnt; 697186904Ssam /* NB: convert units to fit in 16-bits */ 698189980Ssam slotlen = ts->tdma_slotlen / 100; /* 100us units */ 699186904Ssam ADDSHORT(frm, slotlen); 700189980Ssam *frm++ = ts->tdma_bintval; 701189980Ssam *frm++ = ts->tdma_inuse[0]; 702186904Ssam frm += 10; /* pad+timestamp */ 703186904Ssam return frm; 704186904Ssam#undef ADDSHORT 705186904Ssam} 706186904Ssam#undef TDMA_OUI_BYTES 707186904Ssam 708186904Ssam/* 709186904Ssam * Update TDMA state at TBTT. 710186904Ssam */ 711186904Ssamvoid 712186904Ssamieee80211_tdma_update_beacon(struct ieee80211vap *vap, 713186904Ssam struct ieee80211_beacon_offsets *bo) 714186904Ssam{ 715186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 716186904Ssam 717186904Ssam KASSERT(vap->iv_caps & IEEE80211_C_TDMA, 718186904Ssam ("not a tdma vap, caps 0x%x", vap->iv_caps)); 719186904Ssam 720186904Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) { 721186904Ssam (void) ieee80211_add_tdma(bo->bo_tdma, vap); 722186904Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 723186904Ssam } 724186904Ssam if (ts->tdma_slot != 0) /* only on master */ 725186904Ssam return; 726186904Ssam if (ts->tdma_count <= 0) { 727186904Ssam /* 728186904Ssam * Time to update the mask of active/inuse stations. 729186904Ssam * We track stations that we've received a beacon 730186904Ssam * frame from and update this mask periodically. 731186904Ssam * This allows us to miss a few beacons before marking 732186904Ssam * a slot free for re-use. 733186904Ssam */ 734186904Ssam ts->tdma_inuse[0] = ts->tdma_active[0]; 735186904Ssam ts->tdma_active[0] = 0x01; 736186904Ssam /* update next time 'round */ 737186904Ssam /* XXX use notify framework */ 738186904Ssam setbit(bo->bo_flags, IEEE80211_BEACON_TDMA); 739186904Ssam /* NB: use s/w beacon miss threshold; may be too high */ 740186904Ssam ts->tdma_count = vap->iv_bmissthreshold-1; 741186904Ssam } else 742186904Ssam ts->tdma_count--; 743186904Ssam} 744186904Ssam 745190384Ssamstatic int 746190384Ssamtdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 747186904Ssam{ 748186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 749186904Ssam 750186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 751254506Sadrian return ENOSYS; 752186904Ssam 753186904Ssam switch (ireq->i_type) { 754186904Ssam case IEEE80211_IOC_TDMA_SLOT: 755186904Ssam ireq->i_val = ts->tdma_slot; 756186904Ssam break; 757186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 758186904Ssam ireq->i_val = ts->tdma_slotcnt; 759186904Ssam break; 760186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 761186904Ssam ireq->i_val = ts->tdma_slotlen; 762186904Ssam break; 763186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 764186904Ssam ireq->i_val = ts->tdma_bintval; 765186904Ssam break; 766186904Ssam default: 767190384Ssam return ENOSYS; 768186904Ssam } 769186904Ssam return 0; 770186904Ssam} 771190384SsamIEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211); 772186904Ssam 773190384Ssamstatic int 774190384Ssamtdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 775186904Ssam{ 776186904Ssam struct ieee80211_tdma_state *ts = vap->iv_tdma; 777186904Ssam 778186904Ssam if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) 779254506Sadrian return ENOSYS; 780186904Ssam 781186904Ssam switch (ireq->i_type) { 782186904Ssam case IEEE80211_IOC_TDMA_SLOT: 783186904Ssam if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt)) 784186904Ssam return EINVAL; 785186904Ssam if (ireq->i_val != ts->tdma_slot) { 786186904Ssam ts->tdma_slot = ireq->i_val; 787193114Ssam goto restart; 788186904Ssam } 789186904Ssam break; 790186904Ssam case IEEE80211_IOC_TDMA_SLOTCNT: 791189980Ssam if (!TDMA_SLOTCNT_VALID(ireq->i_val)) 792186904Ssam return EINVAL; 793186904Ssam if (ireq->i_val != ts->tdma_slotcnt) { 794186904Ssam ts->tdma_slotcnt = ireq->i_val; 795193114Ssam goto restart; 796186904Ssam } 797186904Ssam break; 798186904Ssam case IEEE80211_IOC_TDMA_SLOTLEN: 799186904Ssam /* 800186904Ssam * XXX 801186904Ssam * 150 insures at least 1/8 TU 802186904Ssam * 0xfffff is the max duration for bursting 803186904Ssam * (implict by way of 16-bit data type for i_val) 804186904Ssam */ 805189980Ssam if (!TDMA_SLOTLEN_VALID(ireq->i_val)) 806186904Ssam return EINVAL; 807186904Ssam if (ireq->i_val != ts->tdma_slotlen) { 808186904Ssam ts->tdma_slotlen = ireq->i_val; 809193114Ssam goto restart; 810186904Ssam } 811186904Ssam break; 812186904Ssam case IEEE80211_IOC_TDMA_BINTERVAL: 813189980Ssam if (!TDMA_BINTVAL_VALID(ireq->i_val)) 814186904Ssam return EINVAL; 815186904Ssam if (ireq->i_val != ts->tdma_bintval) { 816186904Ssam ts->tdma_bintval = ireq->i_val; 817193114Ssam goto restart; 818186904Ssam } 819186904Ssam break; 820186904Ssam default: 821190384Ssam return ENOSYS; 822186904Ssam } 823186904Ssam return 0; 824193114Ssamrestart: 825193114Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA); 826193114Ssam return ERESTART; 827186904Ssam} 828190384SsamIEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211); 829230153Sadrian 830230153Sadrian#endif /* IEEE80211_SUPPORT_TDMA */ 831