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