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