132176cfdSRui Paulo /*-
232176cfdSRui Paulo * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
332176cfdSRui Paulo * Copyright (c) 2007-2009 Intel Corporation
432176cfdSRui Paulo * All rights reserved.
532176cfdSRui Paulo *
632176cfdSRui Paulo * Redistribution and use in source and binary forms, with or without
732176cfdSRui Paulo * modification, are permitted provided that the following conditions
832176cfdSRui Paulo * are met:
932176cfdSRui Paulo * 1. Redistributions of source code must retain the above copyright
1032176cfdSRui Paulo * notice, this list of conditions and the following disclaimer.
1132176cfdSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright
1232176cfdSRui Paulo * notice, this list of conditions and the following disclaimer in the
1332176cfdSRui Paulo * documentation and/or other materials provided with the distribution.
1432176cfdSRui Paulo *
1532176cfdSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1632176cfdSRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1732176cfdSRui Paulo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1832176cfdSRui Paulo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1932176cfdSRui Paulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2032176cfdSRui Paulo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2132176cfdSRui Paulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2232176cfdSRui Paulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2332176cfdSRui Paulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2432176cfdSRui Paulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2532176cfdSRui Paulo */
2632176cfdSRui Paulo
27085ff963SMatthew Dillon #include <sys/cdefs.h>
28085ff963SMatthew Dillon #ifdef __FreeBSD__
29085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
30085ff963SMatthew Dillon #endif
31085ff963SMatthew Dillon
3232176cfdSRui Paulo /*
3332176cfdSRui Paulo * IEEE 802.11 TDMA mode support.
3432176cfdSRui Paulo */
3532176cfdSRui Paulo #include "opt_inet.h"
3632176cfdSRui Paulo #include "opt_tdma.h"
3732176cfdSRui Paulo #include "opt_wlan.h"
3832176cfdSRui Paulo
39085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_TDMA
40085ff963SMatthew Dillon
4132176cfdSRui Paulo #include <sys/param.h>
4232176cfdSRui Paulo #include <sys/systm.h>
4332176cfdSRui Paulo #include <sys/mbuf.h>
4432176cfdSRui Paulo #include <sys/malloc.h>
4532176cfdSRui Paulo #include <sys/kernel.h>
4632176cfdSRui Paulo
4732176cfdSRui Paulo #include <sys/socket.h>
4832176cfdSRui Paulo #include <sys/sockio.h>
4932176cfdSRui Paulo #include <sys/endian.h>
5032176cfdSRui Paulo #include <sys/errno.h>
5132176cfdSRui Paulo #include <sys/proc.h>
5232176cfdSRui Paulo #include <sys/sysctl.h>
5332176cfdSRui Paulo
5432176cfdSRui Paulo #include <net/if.h>
55*bff82488SAaron LI #include <net/if_var.h>
5632176cfdSRui Paulo #include <net/if_media.h>
5732176cfdSRui Paulo #include <net/if_llc.h>
5832176cfdSRui Paulo #include <net/ethernet.h>
5932176cfdSRui Paulo
6032176cfdSRui Paulo #include <net/bpf.h>
6132176cfdSRui Paulo
6232176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_tdma.h>
6432176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6532176cfdSRui Paulo
6632176cfdSRui Paulo #ifndef TDMA_SLOTLEN_DEFAULT
6732176cfdSRui Paulo #define TDMA_SLOTLEN_DEFAULT 10*1000 /* 10ms */
6832176cfdSRui Paulo #endif
6932176cfdSRui Paulo #ifndef TDMA_SLOTCNT_DEFAULT
7032176cfdSRui Paulo #define TDMA_SLOTCNT_DEFAULT 2 /* 2x (pt-to-pt) */
7132176cfdSRui Paulo #endif
7232176cfdSRui Paulo #ifndef TDMA_BINTVAL_DEFAULT
7332176cfdSRui Paulo #define TDMA_BINTVAL_DEFAULT 5 /* 5x ~= 100TU beacon intvl */
7432176cfdSRui Paulo #endif
7532176cfdSRui Paulo #ifndef TDMA_TXRATE_11B_DEFAULT
7632176cfdSRui Paulo #define TDMA_TXRATE_11B_DEFAULT 2*11
7732176cfdSRui Paulo #endif
7832176cfdSRui Paulo #ifndef TDMA_TXRATE_11G_DEFAULT
7932176cfdSRui Paulo #define TDMA_TXRATE_11G_DEFAULT 2*24
8032176cfdSRui Paulo #endif
8132176cfdSRui Paulo #ifndef TDMA_TXRATE_11A_DEFAULT
8232176cfdSRui Paulo #define TDMA_TXRATE_11A_DEFAULT 2*24
8332176cfdSRui Paulo #endif
8432176cfdSRui Paulo #ifndef TDMA_TXRATE_TURBO_DEFAULT
8532176cfdSRui Paulo #define TDMA_TXRATE_TURBO_DEFAULT 2*24
8632176cfdSRui Paulo #endif
8732176cfdSRui Paulo #ifndef TDMA_TXRATE_HALF_DEFAULT
8832176cfdSRui Paulo #define TDMA_TXRATE_HALF_DEFAULT 2*12
8932176cfdSRui Paulo #endif
9032176cfdSRui Paulo #ifndef TDMA_TXRATE_QUARTER_DEFAULT
9132176cfdSRui Paulo #define TDMA_TXRATE_QUARTER_DEFAULT 2*6
9232176cfdSRui Paulo #endif
9332176cfdSRui Paulo #ifndef TDMA_TXRATE_11NA_DEFAULT
9432176cfdSRui Paulo #define TDMA_TXRATE_11NA_DEFAULT (4 | IEEE80211_RATE_MCS)
9532176cfdSRui Paulo #endif
9632176cfdSRui Paulo #ifndef TDMA_TXRATE_11NG_DEFAULT
9732176cfdSRui Paulo #define TDMA_TXRATE_11NG_DEFAULT (4 | IEEE80211_RATE_MCS)
9832176cfdSRui Paulo #endif
9932176cfdSRui Paulo
10032176cfdSRui Paulo #define TDMA_VERSION_VALID(_version) \
10132176cfdSRui Paulo (TDMA_VERSION_V2 <= (_version) && (_version) <= TDMA_VERSION)
10232176cfdSRui Paulo #define TDMA_SLOTCNT_VALID(_slotcnt) \
10332176cfdSRui Paulo (2 <= (_slotcnt) && (_slotcnt) <= TDMA_MAXSLOTS)
10432176cfdSRui Paulo /* XXX magic constants */
10532176cfdSRui Paulo #define TDMA_SLOTLEN_VALID(_slotlen) \
10632176cfdSRui Paulo (2*100 <= (_slotlen) && (unsigned)(_slotlen) <= 0xfffff)
10732176cfdSRui Paulo /* XXX probably should set a max */
10832176cfdSRui Paulo #define TDMA_BINTVAL_VALID(_bintval) (1 <= (_bintval))
10932176cfdSRui Paulo
11032176cfdSRui Paulo /*
11132176cfdSRui Paulo * This code is not prepared to handle more than 2 slots.
11232176cfdSRui Paulo */
11332176cfdSRui Paulo CTASSERT(TDMA_MAXSLOTS == 2);
11432176cfdSRui Paulo
11532176cfdSRui Paulo static void tdma_vdetach(struct ieee80211vap *vap);
11632176cfdSRui Paulo static int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int);
11732176cfdSRui Paulo static void tdma_beacon_miss(struct ieee80211vap *vap);
11832176cfdSRui Paulo static void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *,
1194f898719SImre Vadász int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf);
12032176cfdSRui Paulo static int tdma_update(struct ieee80211vap *vap,
12132176cfdSRui Paulo const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni,
12232176cfdSRui Paulo int pickslot);
12332176cfdSRui Paulo static int tdma_process_params(struct ieee80211_node *ni,
12432176cfdSRui Paulo const u_int8_t *ie, int rssi, int nf, const struct ieee80211_frame *wh);
12532176cfdSRui Paulo
12632176cfdSRui Paulo static void
settxparms(struct ieee80211vap * vap,enum ieee80211_phymode mode,int rate)12732176cfdSRui Paulo settxparms(struct ieee80211vap *vap, enum ieee80211_phymode mode, int rate)
12832176cfdSRui Paulo {
12932176cfdSRui Paulo vap->iv_txparms[mode].ucastrate = rate;
13032176cfdSRui Paulo vap->iv_txparms[mode].mcastrate = rate;
13132176cfdSRui Paulo }
13232176cfdSRui Paulo
13332176cfdSRui Paulo static void
setackpolicy(struct ieee80211com * ic,int noack)13432176cfdSRui Paulo setackpolicy(struct ieee80211com *ic, int noack)
13532176cfdSRui Paulo {
13632176cfdSRui Paulo struct ieee80211_wme_state *wme = &ic->ic_wme;
13732176cfdSRui Paulo int ac;
13832176cfdSRui Paulo
13932176cfdSRui Paulo for (ac = 0; ac < WME_NUM_AC; ac++) {
14032176cfdSRui Paulo wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack;
14132176cfdSRui Paulo wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack;
14232176cfdSRui Paulo }
14332176cfdSRui Paulo }
14432176cfdSRui Paulo
14532176cfdSRui Paulo void
ieee80211_tdma_vattach(struct ieee80211vap * vap)14632176cfdSRui Paulo ieee80211_tdma_vattach(struct ieee80211vap *vap)
14732176cfdSRui Paulo {
14832176cfdSRui Paulo struct ieee80211_tdma_state *ts;
14932176cfdSRui Paulo
15032176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
15132176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
15232176cfdSRui Paulo
1534f655ef5SMatthew Dillon #if defined(__DragonFly__)
15432176cfdSRui Paulo ts = (struct ieee80211_tdma_state *) kmalloc(
1554f655ef5SMatthew Dillon sizeof(struct ieee80211_tdma_state), M_80211_VAP,
1564f655ef5SMatthew Dillon M_INTWAIT | M_ZERO);
1574f655ef5SMatthew Dillon #else
1584f655ef5SMatthew Dillon ts = (struct ieee80211_tdma_state *) IEEE80211_MALLOC(
1594f655ef5SMatthew Dillon sizeof(struct ieee80211_tdma_state), M_80211_VAP,
1604f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
1614f655ef5SMatthew Dillon #endif
16232176cfdSRui Paulo if (ts == NULL) {
16332176cfdSRui Paulo kprintf("%s: cannot allocate TDMA state block\n", __func__);
16432176cfdSRui Paulo /* NB: fall back to adhdemo mode */
16532176cfdSRui Paulo vap->iv_caps &= ~IEEE80211_C_TDMA;
16632176cfdSRui Paulo return;
16732176cfdSRui Paulo }
16832176cfdSRui Paulo /* NB: default configuration is passive so no beacons */
16932176cfdSRui Paulo ts->tdma_version = TDMA_VERSION;
17032176cfdSRui Paulo ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT;
17132176cfdSRui Paulo ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT;
17232176cfdSRui Paulo ts->tdma_bintval = TDMA_BINTVAL_DEFAULT;
17332176cfdSRui Paulo ts->tdma_slot = 1; /* passive operation */
17432176cfdSRui Paulo
17532176cfdSRui Paulo /* setup default fixed rates */
17632176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT);
17732176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT);
17832176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT);
17932176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_TURBO_A, TDMA_TXRATE_TURBO_DEFAULT);
18032176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_TURBO_G, TDMA_TXRATE_TURBO_DEFAULT);
18132176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_TURBO_DEFAULT);
18232176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT);
18332176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT);
18432176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT);
18532176cfdSRui Paulo settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT);
18632176cfdSRui Paulo
18732176cfdSRui Paulo setackpolicy(vap->iv_ic, 1); /* disable ACK's */
18832176cfdSRui Paulo
18932176cfdSRui Paulo ts->tdma_opdetach = vap->iv_opdetach;
19032176cfdSRui Paulo vap->iv_opdetach = tdma_vdetach;
19132176cfdSRui Paulo ts->tdma_newstate = vap->iv_newstate;
19232176cfdSRui Paulo vap->iv_newstate = tdma_newstate;
19332176cfdSRui Paulo vap->iv_bmiss = tdma_beacon_miss;
19432176cfdSRui Paulo ts->tdma_recv_mgmt = vap->iv_recv_mgmt;
19532176cfdSRui Paulo vap->iv_recv_mgmt = tdma_recv_mgmt;
19632176cfdSRui Paulo
19732176cfdSRui Paulo vap->iv_tdma = ts;
19832176cfdSRui Paulo }
19932176cfdSRui Paulo
20032176cfdSRui Paulo static void
tdma_vdetach(struct ieee80211vap * vap)20132176cfdSRui Paulo tdma_vdetach(struct ieee80211vap *vap)
20232176cfdSRui Paulo {
20332176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
20432176cfdSRui Paulo
20532176cfdSRui Paulo if (ts == NULL) {
20632176cfdSRui Paulo /* NB: should not have touched any ic state */
20732176cfdSRui Paulo return;
20832176cfdSRui Paulo }
20932176cfdSRui Paulo ts->tdma_opdetach(vap);
2104f655ef5SMatthew Dillon IEEE80211_FREE(vap->iv_tdma, M_80211_VAP);
21132176cfdSRui Paulo vap->iv_tdma = NULL;
21232176cfdSRui Paulo
21332176cfdSRui Paulo setackpolicy(vap->iv_ic, 0); /* enable ACK's */
21432176cfdSRui Paulo }
21532176cfdSRui Paulo
21632176cfdSRui Paulo static void
sta_leave(void * arg,struct ieee80211_node * ni)21732176cfdSRui Paulo sta_leave(void *arg, struct ieee80211_node *ni)
21832176cfdSRui Paulo {
21932176cfdSRui Paulo struct ieee80211vap *vap = arg;
22032176cfdSRui Paulo
22132176cfdSRui Paulo if (ni->ni_vap == vap && ni != vap->iv_bss)
22232176cfdSRui Paulo ieee80211_node_leave(ni);
22332176cfdSRui Paulo }
22432176cfdSRui Paulo
22532176cfdSRui Paulo /*
22632176cfdSRui Paulo * TDMA state machine handler.
22732176cfdSRui Paulo */
22832176cfdSRui Paulo static int
tdma_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)22932176cfdSRui Paulo tdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
23032176cfdSRui Paulo {
23132176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
23232176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
23332176cfdSRui Paulo enum ieee80211_state ostate;
23432176cfdSRui Paulo int status;
23532176cfdSRui Paulo
236085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic);
237085ff963SMatthew Dillon
23832176cfdSRui Paulo ostate = vap->iv_state;
23932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
24032176cfdSRui Paulo __func__, ieee80211_state_name[ostate],
24132176cfdSRui Paulo ieee80211_state_name[nstate], arg);
24232176cfdSRui Paulo
24332176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
24432176cfdSRui Paulo callout_stop(&vap->iv_swbmiss);
24532176cfdSRui Paulo if (nstate == IEEE80211_S_SCAN &&
24632176cfdSRui Paulo (ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) &&
24732176cfdSRui Paulo ts->tdma_slot != 0) {
24832176cfdSRui Paulo /*
24932176cfdSRui Paulo * Override adhoc behaviour when operating as a slave;
25032176cfdSRui Paulo * we need to scan even if the channel is locked.
25132176cfdSRui Paulo */
25232176cfdSRui Paulo vap->iv_state = nstate; /* state transition */
25332176cfdSRui Paulo ieee80211_cancel_scan(vap); /* background scan */
25432176cfdSRui Paulo if (ostate == IEEE80211_S_RUN) {
25532176cfdSRui Paulo /* purge station table; entries are stale */
25632176cfdSRui Paulo ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap);
25732176cfdSRui Paulo }
25832176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
25932176cfdSRui Paulo ieee80211_check_scan(vap,
26032176cfdSRui Paulo vap->iv_scanreq_flags,
26132176cfdSRui Paulo vap->iv_scanreq_duration,
26232176cfdSRui Paulo vap->iv_scanreq_mindwell,
26332176cfdSRui Paulo vap->iv_scanreq_maxdwell,
26432176cfdSRui Paulo vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
26532176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
26632176cfdSRui Paulo } else
26732176cfdSRui Paulo ieee80211_check_scan_current(vap);
26832176cfdSRui Paulo status = 0;
26932176cfdSRui Paulo } else {
27032176cfdSRui Paulo status = ts->tdma_newstate(vap, nstate, arg);
27132176cfdSRui Paulo }
27232176cfdSRui Paulo if (status == 0 &&
27332176cfdSRui Paulo nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN &&
27432176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) &&
27532176cfdSRui Paulo ts->tdma_slot != 0 &&
27632176cfdSRui Paulo vap->iv_des_chan == IEEE80211_CHAN_ANYC) {
27732176cfdSRui Paulo /*
27832176cfdSRui Paulo * Start s/w beacon miss timer for slave devices w/o
27932176cfdSRui Paulo * hardware support. Note we do this only if we're
28032176cfdSRui Paulo * not locked to a channel (i.e. roam to follow the
28132176cfdSRui Paulo * master). The 2x is a fudge for our doing this in
28232176cfdSRui Paulo * software.
28332176cfdSRui Paulo */
28432176cfdSRui Paulo vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
28532176cfdSRui Paulo 2 * vap->iv_bmissthreshold * ts->tdma_bintval *
28632176cfdSRui Paulo ((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024));
28732176cfdSRui Paulo vap->iv_swbmiss_count = 0;
28832176cfdSRui Paulo callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
289085ff963SMatthew Dillon ieee80211_swbmiss, vap);
29032176cfdSRui Paulo }
29132176cfdSRui Paulo return status;
29232176cfdSRui Paulo }
29332176cfdSRui Paulo
29432176cfdSRui Paulo static void
tdma_beacon_miss(struct ieee80211vap * vap)29532176cfdSRui Paulo tdma_beacon_miss(struct ieee80211vap *vap)
29632176cfdSRui Paulo {
29732176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
29832176cfdSRui Paulo
299085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(vap->iv_ic);
300085ff963SMatthew Dillon
30132176cfdSRui Paulo KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
30232176cfdSRui Paulo KASSERT(vap->iv_state == IEEE80211_S_RUN,
30332176cfdSRui Paulo ("wrong state %d", vap->iv_state));
30432176cfdSRui Paulo
30532176cfdSRui Paulo IEEE80211_DPRINTF(vap,
30632176cfdSRui Paulo IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG,
30732176cfdSRui Paulo "beacon miss, mode %u state %s\n",
30832176cfdSRui Paulo vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
30932176cfdSRui Paulo
310085ff963SMatthew Dillon callout_stop(&vap->iv_swbmiss);
311085ff963SMatthew Dillon
31232176cfdSRui Paulo if (ts->tdma_peer != NULL) { /* XXX? can this be null? */
31332176cfdSRui Paulo ieee80211_notify_node_leave(vap->iv_bss);
31432176cfdSRui Paulo ts->tdma_peer = NULL;
31532176cfdSRui Paulo /*
31632176cfdSRui Paulo * Treat beacon miss like an associate failure wrt the
31732176cfdSRui Paulo * scan policy; this forces the entry in the scan cache
31832176cfdSRui Paulo * to be ignored after several tries.
31932176cfdSRui Paulo */
32032176cfdSRui Paulo ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr,
32132176cfdSRui Paulo IEEE80211_STATUS_TIMEOUT);
32232176cfdSRui Paulo }
32332176cfdSRui Paulo #if 0
32432176cfdSRui Paulo ts->tdma_inuse = 0; /* clear slot usage */
32532176cfdSRui Paulo #endif
32632176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
32732176cfdSRui Paulo }
32832176cfdSRui Paulo
32932176cfdSRui Paulo static void
tdma_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)33032176cfdSRui Paulo tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
3314f898719SImre Vadász int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
33232176cfdSRui Paulo {
33332176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
33432176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
33532176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
33632176cfdSRui Paulo
33732176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_BEACON &&
33832176cfdSRui Paulo (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
33932176cfdSRui Paulo struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
34032176cfdSRui Paulo struct ieee80211_scanparams scan;
34132176cfdSRui Paulo
3424f898719SImre Vadász /* XXX TODO: use rxstatus to determine off-channel beacons */
3434f898719SImre Vadász if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) != 0)
34432176cfdSRui Paulo return;
34532176cfdSRui Paulo if (scan.tdma == NULL) {
34632176cfdSRui Paulo /*
34732176cfdSRui Paulo * TDMA stations must beacon a TDMA ie; ignore
34832176cfdSRui Paulo * any other station.
34932176cfdSRui Paulo * XXX detect overlapping bss and change channel
35032176cfdSRui Paulo */
35132176cfdSRui Paulo IEEE80211_DISCARD(vap,
35232176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
3534f655ef5SMatthew Dillon wh, ieee80211_mgt_subtype_name(subtype),
35432176cfdSRui Paulo "%s", "no TDMA ie");
35532176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
35632176cfdSRui Paulo return;
35732176cfdSRui Paulo }
35832176cfdSRui Paulo if (ni == vap->iv_bss &&
35932176cfdSRui Paulo !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
36032176cfdSRui Paulo /*
36132176cfdSRui Paulo * Fake up a node for this newly
36232176cfdSRui Paulo * discovered member of the IBSS.
36332176cfdSRui Paulo */
36432176cfdSRui Paulo ni = ieee80211_add_neighbor(vap, wh, &scan);
36532176cfdSRui Paulo if (ni == NULL) {
36632176cfdSRui Paulo /* NB: stat kept for alloc failure */
36732176cfdSRui Paulo return;
36832176cfdSRui Paulo }
36932176cfdSRui Paulo }
37032176cfdSRui Paulo /*
37132176cfdSRui Paulo * Check for state updates.
37232176cfdSRui Paulo */
37332176cfdSRui Paulo if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) {
37432176cfdSRui Paulo /*
37532176cfdSRui Paulo * Count frame now that we know it's to be processed.
37632176cfdSRui Paulo */
37732176cfdSRui Paulo vap->iv_stats.is_rx_beacon++;
37832176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_beacons);
37932176cfdSRui Paulo /*
38032176cfdSRui Paulo * Record tsf of last beacon. NB: this must be
38132176cfdSRui Paulo * done before calling tdma_process_params
38232176cfdSRui Paulo * as deeper routines reference it.
38332176cfdSRui Paulo */
38432176cfdSRui Paulo memcpy(&ni->ni_tstamp.data, scan.tstamp,
38532176cfdSRui Paulo sizeof(ni->ni_tstamp.data));
38632176cfdSRui Paulo /*
38732176cfdSRui Paulo * Count beacon frame for s/w bmiss handling.
38832176cfdSRui Paulo */
38932176cfdSRui Paulo vap->iv_swbmiss_count++;
39032176cfdSRui Paulo /*
39132176cfdSRui Paulo * Process tdma ie. The contents are used to sync
39232176cfdSRui Paulo * the slot timing, reconfigure the bss, etc.
39332176cfdSRui Paulo */
39432176cfdSRui Paulo (void) tdma_process_params(ni, scan.tdma, rssi, nf, wh);
39532176cfdSRui Paulo return;
39632176cfdSRui Paulo }
39732176cfdSRui Paulo /*
39832176cfdSRui Paulo * NB: defer remaining work to the adhoc code; this causes
39932176cfdSRui Paulo * 2x parsing of the frame but should happen infrequently
40032176cfdSRui Paulo */
40132176cfdSRui Paulo }
4024f898719SImre Vadász ts->tdma_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
40332176cfdSRui Paulo }
40432176cfdSRui Paulo
40532176cfdSRui Paulo /*
40632176cfdSRui Paulo * Update TDMA state on receipt of a beacon frame with
40732176cfdSRui Paulo * a TDMA information element. The sender's identity
40832176cfdSRui Paulo * is provided so we can track who our peer is. If pickslot
40932176cfdSRui Paulo * is non-zero we scan the slot allocation state in the ie
41032176cfdSRui Paulo * to locate a free slot for our use.
41132176cfdSRui Paulo */
41232176cfdSRui Paulo static int
tdma_update(struct ieee80211vap * vap,const struct ieee80211_tdma_param * tdma,struct ieee80211_node * ni,int pickslot)41332176cfdSRui Paulo tdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma,
41432176cfdSRui Paulo struct ieee80211_node *ni, int pickslot)
41532176cfdSRui Paulo {
41632176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
41732176cfdSRui Paulo int slot, slotlen, update;
41832176cfdSRui Paulo
41932176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
42032176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
42132176cfdSRui Paulo
42232176cfdSRui Paulo update = 0;
42332176cfdSRui Paulo if (tdma->tdma_slotcnt != ts->tdma_slotcnt) {
42432176cfdSRui Paulo if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) {
42532176cfdSRui Paulo if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
42632176cfdSRui Paulo kprintf("%s: bad slot cnt %u\n",
42732176cfdSRui Paulo __func__, tdma->tdma_slotcnt);
42832176cfdSRui Paulo return 0;
42932176cfdSRui Paulo }
43032176cfdSRui Paulo update |= TDMA_UPDATE_SLOTCNT;
43132176cfdSRui Paulo }
43232176cfdSRui Paulo slotlen = le16toh(tdma->tdma_slotlen) * 100;
43332176cfdSRui Paulo if (slotlen != ts->tdma_slotlen) {
43432176cfdSRui Paulo if (!TDMA_SLOTLEN_VALID(slotlen)) {
43532176cfdSRui Paulo if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
43632176cfdSRui Paulo kprintf("%s: bad slot len %u\n",
43732176cfdSRui Paulo __func__, slotlen);
43832176cfdSRui Paulo return 0;
43932176cfdSRui Paulo }
44032176cfdSRui Paulo update |= TDMA_UPDATE_SLOTLEN;
44132176cfdSRui Paulo }
44232176cfdSRui Paulo if (tdma->tdma_bintval != ts->tdma_bintval) {
44332176cfdSRui Paulo if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) {
44432176cfdSRui Paulo if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
44532176cfdSRui Paulo kprintf("%s: bad beacon interval %u\n",
44632176cfdSRui Paulo __func__, tdma->tdma_bintval);
44732176cfdSRui Paulo return 0;
44832176cfdSRui Paulo }
44932176cfdSRui Paulo update |= TDMA_UPDATE_BINTVAL;
45032176cfdSRui Paulo }
45132176cfdSRui Paulo slot = ts->tdma_slot;
45232176cfdSRui Paulo if (pickslot) {
45332176cfdSRui Paulo /*
45432176cfdSRui Paulo * Pick unoccupied slot. Note we never choose slot 0.
45532176cfdSRui Paulo */
45632176cfdSRui Paulo for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--)
45732176cfdSRui Paulo if (isclr(tdma->tdma_inuse, slot))
45832176cfdSRui Paulo break;
45932176cfdSRui Paulo if (slot <= 0) {
46032176cfdSRui Paulo kprintf("%s: no free slot, slotcnt %u inuse: 0x%x\n",
46132176cfdSRui Paulo __func__, tdma->tdma_slotcnt,
46232176cfdSRui Paulo tdma->tdma_inuse[0]);
46332176cfdSRui Paulo /* XXX need to do something better */
46432176cfdSRui Paulo return 0;
46532176cfdSRui Paulo }
46632176cfdSRui Paulo if (slot != ts->tdma_slot)
46732176cfdSRui Paulo update |= TDMA_UPDATE_SLOT;
46832176cfdSRui Paulo }
46932176cfdSRui Paulo if (ni != ts->tdma_peer) {
47032176cfdSRui Paulo /* update everything */
47132176cfdSRui Paulo update = TDMA_UPDATE_SLOT
47232176cfdSRui Paulo | TDMA_UPDATE_SLOTCNT
47332176cfdSRui Paulo | TDMA_UPDATE_SLOTLEN
47432176cfdSRui Paulo | TDMA_UPDATE_BINTVAL;
47532176cfdSRui Paulo }
47632176cfdSRui Paulo
47732176cfdSRui Paulo if (update) {
47832176cfdSRui Paulo /*
47932176cfdSRui Paulo * New/changed parameters; update runtime state.
48032176cfdSRui Paulo */
48132176cfdSRui Paulo /* XXX overwrites user parameters */
48232176cfdSRui Paulo if (update & TDMA_UPDATE_SLOTCNT)
48332176cfdSRui Paulo ts->tdma_slotcnt = tdma->tdma_slotcnt;
48432176cfdSRui Paulo if (update & TDMA_UPDATE_SLOTLEN)
48532176cfdSRui Paulo ts->tdma_slotlen = slotlen;
48632176cfdSRui Paulo if (update & TDMA_UPDATE_SLOT)
48732176cfdSRui Paulo ts->tdma_slot = slot;
48832176cfdSRui Paulo if (update & TDMA_UPDATE_BINTVAL)
48932176cfdSRui Paulo ts->tdma_bintval = tdma->tdma_bintval;
49032176cfdSRui Paulo /* mark beacon to be updated before next xmit */
49132176cfdSRui Paulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA);
49232176cfdSRui Paulo
49332176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA,
49432176cfdSRui Paulo "%s: slot %u slotcnt %u slotlen %u us bintval %u\n",
49532176cfdSRui Paulo __func__, ts->tdma_slot, ts->tdma_slotcnt,
49632176cfdSRui Paulo ts->tdma_slotlen, ts->tdma_bintval);
49732176cfdSRui Paulo }
49832176cfdSRui Paulo /*
49932176cfdSRui Paulo * Notify driver. Note we can be called before
50032176cfdSRui Paulo * entering RUN state if we scanned and are
50132176cfdSRui Paulo * joining an existing bss. In that case do not
50232176cfdSRui Paulo * call the driver because not all necessary state
50332176cfdSRui Paulo * has been setup. The next beacon will dtrt.
50432176cfdSRui Paulo */
50532176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_RUN)
50632176cfdSRui Paulo vap->iv_ic->ic_tdma_update(ni, tdma, update);
50732176cfdSRui Paulo /*
50832176cfdSRui Paulo * Dispatch join event on first beacon from new master.
50932176cfdSRui Paulo */
51032176cfdSRui Paulo if (ts->tdma_peer != ni) {
51132176cfdSRui Paulo if (ts->tdma_peer != NULL)
51232176cfdSRui Paulo ieee80211_notify_node_leave(vap->iv_bss);
51332176cfdSRui Paulo ieee80211_notify_node_join(ni, 1);
51432176cfdSRui Paulo /* NB: no reference, we just use the address */
51532176cfdSRui Paulo ts->tdma_peer = ni;
51632176cfdSRui Paulo }
51732176cfdSRui Paulo return 1;
51832176cfdSRui Paulo }
51932176cfdSRui Paulo
52032176cfdSRui Paulo /*
52132176cfdSRui Paulo * Process received TDMA parameters.
52232176cfdSRui Paulo */
52332176cfdSRui Paulo static int
tdma_process_params(struct ieee80211_node * ni,const u_int8_t * ie,int rssi,int nf,const struct ieee80211_frame * wh)52432176cfdSRui Paulo tdma_process_params(struct ieee80211_node *ni, const u_int8_t *ie,
52532176cfdSRui Paulo int rssi, int nf, const struct ieee80211_frame *wh)
52632176cfdSRui Paulo {
52732176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
52832176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
52932176cfdSRui Paulo const struct ieee80211_tdma_param *tdma =
53032176cfdSRui Paulo (const struct ieee80211_tdma_param *) ie;
53132176cfdSRui Paulo u_int len = ie[1];
53232176cfdSRui Paulo
53332176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
53432176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
53532176cfdSRui Paulo
53632176cfdSRui Paulo if (len < sizeof(*tdma) - 2) {
53732176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
53832176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
53932176cfdSRui Paulo wh, "tdma", "too short, len %u", len);
54032176cfdSRui Paulo return IEEE80211_REASON_IE_INVALID;
54132176cfdSRui Paulo }
54232176cfdSRui Paulo if (tdma->tdma_version != ts->tdma_version) {
54332176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
54432176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
54532176cfdSRui Paulo wh, "tdma", "bad version %u (ours %u)",
54632176cfdSRui Paulo tdma->tdma_version, ts->tdma_version);
54732176cfdSRui Paulo return IEEE80211_REASON_IE_INVALID;
54832176cfdSRui Paulo }
54932176cfdSRui Paulo /*
55032176cfdSRui Paulo * NB: ideally we'd check against tdma_slotcnt, but that
55132176cfdSRui Paulo * would require extra effort so do this easy check that
55232176cfdSRui Paulo * covers the work below; more stringent checks are done
55332176cfdSRui Paulo * before we make more extensive use of the ie contents.
55432176cfdSRui Paulo */
55532176cfdSRui Paulo if (tdma->tdma_slot >= TDMA_MAXSLOTS) {
55632176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
55732176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
55832176cfdSRui Paulo wh, "tdma", "invalid slot %u", tdma->tdma_slot);
55932176cfdSRui Paulo return IEEE80211_REASON_IE_INVALID;
56032176cfdSRui Paulo }
56132176cfdSRui Paulo /*
56232176cfdSRui Paulo * Can reach here while scanning, update
56332176cfdSRui Paulo * operational state only in RUN state.
56432176cfdSRui Paulo */
56532176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_RUN) {
56632176cfdSRui Paulo if (tdma->tdma_slot != ts->tdma_slot &&
56732176cfdSRui Paulo isclr(ts->tdma_inuse, tdma->tdma_slot)) {
56832176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni,
56932176cfdSRui Paulo "discovered in slot %u", tdma->tdma_slot);
57032176cfdSRui Paulo setbit(ts->tdma_inuse, tdma->tdma_slot);
57132176cfdSRui Paulo /* XXX dispatch event only when operating as master */
57232176cfdSRui Paulo if (ts->tdma_slot == 0)
57332176cfdSRui Paulo ieee80211_notify_node_join(ni, 1);
57432176cfdSRui Paulo }
57532176cfdSRui Paulo setbit(ts->tdma_active, tdma->tdma_slot);
57632176cfdSRui Paulo if (tdma->tdma_slot == ts->tdma_slot-1) {
57732176cfdSRui Paulo /*
57832176cfdSRui Paulo * Slave tsf synchronization to station
57932176cfdSRui Paulo * just before us in the schedule. The driver
58032176cfdSRui Paulo * is responsible for copying the timestamp
58132176cfdSRui Paulo * of the received beacon into our beacon
58232176cfdSRui Paulo * frame so the sender can calculate round
58332176cfdSRui Paulo * trip time. We cannot do that here because
58432176cfdSRui Paulo * we don't know how to update our beacon frame.
58532176cfdSRui Paulo */
58632176cfdSRui Paulo (void) tdma_update(vap, tdma, ni, 0);
58732176cfdSRui Paulo /* XXX reschedule swbmiss timer on parameter change */
58832176cfdSRui Paulo } else if (tdma->tdma_slot == ts->tdma_slot+1) {
58932176cfdSRui Paulo uint64_t tstamp;
59032176cfdSRui Paulo #if 0
59132176cfdSRui Paulo uint32_t rstamp = (uint32_t) le64toh(rs->tsf);
59232176cfdSRui Paulo int32_t rtt;
59332176cfdSRui Paulo #endif
59432176cfdSRui Paulo /*
59532176cfdSRui Paulo * Use returned timstamp to calculate the
59632176cfdSRui Paulo * roundtrip time.
59732176cfdSRui Paulo */
59832176cfdSRui Paulo memcpy(&tstamp, tdma->tdma_tstamp, 8);
59932176cfdSRui Paulo #if 0
60032176cfdSRui Paulo /* XXX use only 15 bits of rstamp */
60132176cfdSRui Paulo rtt = rstamp - (le64toh(tstamp) & 0x7fff);
60232176cfdSRui Paulo if (rtt < 0)
60332176cfdSRui Paulo rtt += 0x7fff;
60432176cfdSRui Paulo /* XXX hack to quiet normal use */
60532176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X,
60632176cfdSRui Paulo "tdma rtt %5u [rstamp %5u tstamp %llu]\n",
60732176cfdSRui Paulo rtt, rstamp,
60832176cfdSRui Paulo (unsigned long long) le64toh(tstamp));
60932176cfdSRui Paulo #endif
61032176cfdSRui Paulo } else if (tdma->tdma_slot == ts->tdma_slot &&
61132176cfdSRui Paulo le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) {
61232176cfdSRui Paulo /*
61332176cfdSRui Paulo * Station using the same slot as us and has
61432176cfdSRui Paulo * been around longer than us; we must move.
61532176cfdSRui Paulo * Note this can happen if stations do not
61632176cfdSRui Paulo * see each other while scanning.
61732176cfdSRui Paulo */
61832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA,
61932176cfdSRui Paulo "slot %u collision rxtsf %llu tsf %llu\n",
62032176cfdSRui Paulo tdma->tdma_slot,
62132176cfdSRui Paulo (unsigned long long) le64toh(ni->ni_tstamp.tsf),
6222c7ccc4aSSascha Wildner (unsigned long long) vap->iv_bss->ni_tstamp.tsf);
62332176cfdSRui Paulo setbit(ts->tdma_inuse, tdma->tdma_slot);
62432176cfdSRui Paulo
62532176cfdSRui Paulo (void) tdma_update(vap, tdma, ni, 1);
62632176cfdSRui Paulo }
62732176cfdSRui Paulo }
62832176cfdSRui Paulo return 0;
62932176cfdSRui Paulo }
63032176cfdSRui Paulo
63132176cfdSRui Paulo int
ieee80211_tdma_getslot(struct ieee80211vap * vap)63232176cfdSRui Paulo ieee80211_tdma_getslot(struct ieee80211vap *vap)
63332176cfdSRui Paulo {
63432176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
63532176cfdSRui Paulo
63632176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
63732176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
63832176cfdSRui Paulo return ts->tdma_slot;
63932176cfdSRui Paulo }
64032176cfdSRui Paulo
64132176cfdSRui Paulo /*
64232176cfdSRui Paulo * Parse a TDMA ie on station join and use it to setup node state.
64332176cfdSRui Paulo */
64432176cfdSRui Paulo void
ieee80211_parse_tdma(struct ieee80211_node * ni,const uint8_t * ie)64532176cfdSRui Paulo ieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie)
64632176cfdSRui Paulo {
64732176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
64832176cfdSRui Paulo
64932176cfdSRui Paulo if (vap->iv_caps & IEEE80211_C_TDMA) {
65032176cfdSRui Paulo const struct ieee80211_tdma_param *tdma =
65132176cfdSRui Paulo (const struct ieee80211_tdma_param *)ie;
65232176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
65332176cfdSRui Paulo /*
65432176cfdSRui Paulo * Adopt TDMA configuration when joining an
65532176cfdSRui Paulo * existing network.
65632176cfdSRui Paulo */
65732176cfdSRui Paulo setbit(ts->tdma_inuse, tdma->tdma_slot);
65832176cfdSRui Paulo (void) tdma_update(vap, tdma, ni, 1);
65932176cfdSRui Paulo /*
66032176cfdSRui Paulo * Propagate capabilities based on the local
66132176cfdSRui Paulo * configuration and the remote station's advertised
66232176cfdSRui Paulo * capabilities. In particular this permits us to
66332176cfdSRui Paulo * enable use of QoS to disable ACK's.
66432176cfdSRui Paulo */
66532176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_WME) &&
66632176cfdSRui Paulo ni->ni_ies.wme_ie != NULL)
66732176cfdSRui Paulo ni->ni_flags |= IEEE80211_NODE_QOS;
66832176cfdSRui Paulo }
66932176cfdSRui Paulo }
67032176cfdSRui Paulo
67132176cfdSRui Paulo #define TDMA_OUI_BYTES 0x00, 0x03, 0x7f
67232176cfdSRui Paulo /*
67332176cfdSRui Paulo * Add a TDMA parameters element to a frame.
67432176cfdSRui Paulo */
67532176cfdSRui Paulo uint8_t *
ieee80211_add_tdma(uint8_t * frm,struct ieee80211vap * vap)67632176cfdSRui Paulo ieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap)
67732176cfdSRui Paulo {
67832176cfdSRui Paulo #define ADDSHORT(frm, v) do { \
67932176cfdSRui Paulo frm[0] = (v) & 0xff; \
68032176cfdSRui Paulo frm[1] = (v) >> 8; \
68132176cfdSRui Paulo frm += 2; \
68232176cfdSRui Paulo } while (0)
68332176cfdSRui Paulo static const struct ieee80211_tdma_param param = {
68432176cfdSRui Paulo .tdma_id = IEEE80211_ELEMID_VENDOR,
68532176cfdSRui Paulo .tdma_len = sizeof(struct ieee80211_tdma_param) - 2,
68632176cfdSRui Paulo .tdma_oui = { TDMA_OUI_BYTES },
68732176cfdSRui Paulo .tdma_type = TDMA_OUI_TYPE,
68832176cfdSRui Paulo .tdma_subtype = TDMA_SUBTYPE_PARAM,
68932176cfdSRui Paulo .tdma_version = TDMA_VERSION,
69032176cfdSRui Paulo };
69132176cfdSRui Paulo const struct ieee80211_tdma_state *ts = vap->iv_tdma;
69232176cfdSRui Paulo uint16_t slotlen;
69332176cfdSRui Paulo
69432176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
69532176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
69632176cfdSRui Paulo
69732176cfdSRui Paulo memcpy(frm, ¶m, sizeof(param));
69832176cfdSRui Paulo frm += __offsetof(struct ieee80211_tdma_param, tdma_slot);
69932176cfdSRui Paulo *frm++ = ts->tdma_slot;
70032176cfdSRui Paulo *frm++ = ts->tdma_slotcnt;
70132176cfdSRui Paulo /* NB: convert units to fit in 16-bits */
70232176cfdSRui Paulo slotlen = ts->tdma_slotlen / 100; /* 100us units */
70332176cfdSRui Paulo ADDSHORT(frm, slotlen);
70432176cfdSRui Paulo *frm++ = ts->tdma_bintval;
70532176cfdSRui Paulo *frm++ = ts->tdma_inuse[0];
70632176cfdSRui Paulo frm += 10; /* pad+timestamp */
70732176cfdSRui Paulo return frm;
70832176cfdSRui Paulo #undef ADDSHORT
70932176cfdSRui Paulo }
71032176cfdSRui Paulo #undef TDMA_OUI_BYTES
71132176cfdSRui Paulo
71232176cfdSRui Paulo /*
71332176cfdSRui Paulo * Update TDMA state at TBTT.
71432176cfdSRui Paulo */
71532176cfdSRui Paulo void
ieee80211_tdma_update_beacon(struct ieee80211vap * vap,struct ieee80211_beacon_offsets * bo)71632176cfdSRui Paulo ieee80211_tdma_update_beacon(struct ieee80211vap *vap,
71732176cfdSRui Paulo struct ieee80211_beacon_offsets *bo)
71832176cfdSRui Paulo {
71932176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
72032176cfdSRui Paulo
72132176cfdSRui Paulo KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
72232176cfdSRui Paulo ("not a tdma vap, caps 0x%x", vap->iv_caps));
72332176cfdSRui Paulo
72432176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) {
72532176cfdSRui Paulo (void) ieee80211_add_tdma(bo->bo_tdma, vap);
72632176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
72732176cfdSRui Paulo }
72832176cfdSRui Paulo if (ts->tdma_slot != 0) /* only on master */
72932176cfdSRui Paulo return;
73032176cfdSRui Paulo if (ts->tdma_count <= 0) {
73132176cfdSRui Paulo /*
73232176cfdSRui Paulo * Time to update the mask of active/inuse stations.
73332176cfdSRui Paulo * We track stations that we've received a beacon
73432176cfdSRui Paulo * frame from and update this mask periodically.
73532176cfdSRui Paulo * This allows us to miss a few beacons before marking
73632176cfdSRui Paulo * a slot free for re-use.
73732176cfdSRui Paulo */
73832176cfdSRui Paulo ts->tdma_inuse[0] = ts->tdma_active[0];
73932176cfdSRui Paulo ts->tdma_active[0] = 0x01;
74032176cfdSRui Paulo /* update next time 'round */
74132176cfdSRui Paulo /* XXX use notify framework */
74232176cfdSRui Paulo setbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
74332176cfdSRui Paulo /* NB: use s/w beacon miss threshold; may be too high */
74432176cfdSRui Paulo ts->tdma_count = vap->iv_bmissthreshold-1;
74532176cfdSRui Paulo } else
74632176cfdSRui Paulo ts->tdma_count--;
74732176cfdSRui Paulo }
74832176cfdSRui Paulo
74932176cfdSRui Paulo static int
tdma_ioctl_get80211(struct ieee80211vap * vap,struct ieee80211req * ireq)75032176cfdSRui Paulo tdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
75132176cfdSRui Paulo {
75232176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
75332176cfdSRui Paulo
75432176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
755085ff963SMatthew Dillon return ENOSYS;
75632176cfdSRui Paulo
75732176cfdSRui Paulo switch (ireq->i_type) {
75832176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOT:
75932176cfdSRui Paulo ireq->i_val = ts->tdma_slot;
76032176cfdSRui Paulo break;
76132176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOTCNT:
76232176cfdSRui Paulo ireq->i_val = ts->tdma_slotcnt;
76332176cfdSRui Paulo break;
76432176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOTLEN:
76532176cfdSRui Paulo ireq->i_val = ts->tdma_slotlen;
76632176cfdSRui Paulo break;
76732176cfdSRui Paulo case IEEE80211_IOC_TDMA_BINTERVAL:
76832176cfdSRui Paulo ireq->i_val = ts->tdma_bintval;
76932176cfdSRui Paulo break;
77032176cfdSRui Paulo default:
77132176cfdSRui Paulo return ENOSYS;
77232176cfdSRui Paulo }
77332176cfdSRui Paulo return 0;
77432176cfdSRui Paulo }
77532176cfdSRui Paulo IEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211);
77632176cfdSRui Paulo
77732176cfdSRui Paulo static int
tdma_ioctl_set80211(struct ieee80211vap * vap,struct ieee80211req * ireq)77832176cfdSRui Paulo tdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
77932176cfdSRui Paulo {
78032176cfdSRui Paulo struct ieee80211_tdma_state *ts = vap->iv_tdma;
78132176cfdSRui Paulo
78232176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
783085ff963SMatthew Dillon return ENOSYS;
78432176cfdSRui Paulo
78532176cfdSRui Paulo switch (ireq->i_type) {
78632176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOT:
78732176cfdSRui Paulo if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt))
78832176cfdSRui Paulo return EINVAL;
78932176cfdSRui Paulo if (ireq->i_val != ts->tdma_slot) {
79032176cfdSRui Paulo ts->tdma_slot = ireq->i_val;
79132176cfdSRui Paulo goto restart;
79232176cfdSRui Paulo }
79332176cfdSRui Paulo break;
79432176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOTCNT:
79532176cfdSRui Paulo if (!TDMA_SLOTCNT_VALID(ireq->i_val))
79632176cfdSRui Paulo return EINVAL;
79732176cfdSRui Paulo if (ireq->i_val != ts->tdma_slotcnt) {
79832176cfdSRui Paulo ts->tdma_slotcnt = ireq->i_val;
79932176cfdSRui Paulo goto restart;
80032176cfdSRui Paulo }
80132176cfdSRui Paulo break;
80232176cfdSRui Paulo case IEEE80211_IOC_TDMA_SLOTLEN:
80332176cfdSRui Paulo /*
80432176cfdSRui Paulo * XXX
80532176cfdSRui Paulo * 150 insures at least 1/8 TU
80632176cfdSRui Paulo * 0xfffff is the max duration for bursting
80732176cfdSRui Paulo * (implict by way of 16-bit data type for i_val)
80832176cfdSRui Paulo */
80932176cfdSRui Paulo if (!TDMA_SLOTLEN_VALID(ireq->i_val))
81032176cfdSRui Paulo return EINVAL;
81132176cfdSRui Paulo if (ireq->i_val != ts->tdma_slotlen) {
81232176cfdSRui Paulo ts->tdma_slotlen = ireq->i_val;
81332176cfdSRui Paulo goto restart;
81432176cfdSRui Paulo }
81532176cfdSRui Paulo break;
81632176cfdSRui Paulo case IEEE80211_IOC_TDMA_BINTERVAL:
81732176cfdSRui Paulo if (!TDMA_BINTVAL_VALID(ireq->i_val))
81832176cfdSRui Paulo return EINVAL;
81932176cfdSRui Paulo if (ireq->i_val != ts->tdma_bintval) {
82032176cfdSRui Paulo ts->tdma_bintval = ireq->i_val;
82132176cfdSRui Paulo goto restart;
82232176cfdSRui Paulo }
82332176cfdSRui Paulo break;
82432176cfdSRui Paulo default:
82532176cfdSRui Paulo return ENOSYS;
82632176cfdSRui Paulo }
82732176cfdSRui Paulo return 0;
82832176cfdSRui Paulo restart:
82932176cfdSRui Paulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA);
83032176cfdSRui Paulo return ERESTART;
83132176cfdSRui Paulo }
83232176cfdSRui Paulo IEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211);
833085ff963SMatthew Dillon
834085ff963SMatthew Dillon #endif /* IEEE80211_SUPPORT_TDMA */
835