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, &param, 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