xref: /illumos-gate/usr/src/uts/common/io/arn/arn_beacon.c (revision d8d810637042c2a73a269f125dee75e8ed1421d1)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2008 Atheros Communications Inc.
8  *
9  * Permission to use, copy, modify, and/or distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/param.h>
23 #include <sys/strsun.h>
24 #include <inet/common.h>
25 #include <inet/nd.h>
26 #include <inet/mi.h>
27 #include <inet/wifi_ioctl.h>
28 
29 #include "arn_core.h"
30 
31 /*
32  * This function will modify certain transmit queue properties depending on
33  * the operating mode of the station (AP or AdHoc).  Parameters are AIFS
34  * settings and channel width min/max
35  */
36 static int
37 arn_beaconq_config(struct arn_softc *sc)
38 {
39 	struct ath_hal *ah = sc->sc_ah;
40 	struct ath9k_tx_queue_info qi;
41 
42 	(void) ath9k_hw_get_txq_props(ah, sc->sc_beaconq, &qi);
43 	if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
44 		/* Always burst out beacon and CAB traffic. */
45 		qi.tqi_aifs = 1;
46 		qi.tqi_cwmin = 0;
47 		qi.tqi_cwmax = 0;
48 	} else {
49 		/* Adhoc mode; important thing is to use 2x cwmin. */
50 		qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
51 		qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
52 		qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
53 	}
54 
55 	if (!ath9k_hw_set_txq_props(ah, sc->sc_beaconq, &qi)) {
56 		arn_problem("unable to update h/w beacon queue parameters\n");
57 		return (0);
58 	} else {
59 		/* push to h/w */
60 		(void) ath9k_hw_resettxqueue(ah, sc->sc_beaconq);
61 		return (1);
62 	}
63 }
64 
65 /*
66  * Associates the beacon frame buffer with a transmit descriptor.  Will set
67  * up all required antenna switch parameters, rate codes, and channel flags.
68  * Beacons are always sent out at the lowest rate, and are not retried.
69  */
70 
71 static void
72 arn_beacon_setup(struct arn_softc *sc, struct ath_buf *bf)
73 {
74 #define	USE_SHPREAMBLE(_ic) \
75 	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
76 	    == IEEE80211_F_SHPREAMBLE)
77 	mblk_t *mp = bf->bf_m;
78 	struct ath_hal *ah = sc->sc_ah;
79 	struct ath_desc *ds;
80 	/* LINTED E_FUNC_SET_NOT_USED */
81 	int flags, antenna = 0;
82 	struct ath_rate_table *rt;
83 	uint8_t rix, rate;
84 	struct ath9k_11n_rate_series series[4];
85 	int ctsrate = 0;
86 	int ctsduration = 0;
87 
88 	/* set up descriptors */
89 	ds = bf->bf_desc;
90 
91 	flags = ATH9K_TXDESC_NOACK;
92 	if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
93 	    (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
94 		ds->ds_link = bf->bf_daddr;	/* self-linked */
95 		flags |= ATH9K_TXDESC_VEOL;
96 		/*
97 		 * Let hardware handle antenna switching.
98 		 */
99 		antenna = 0;
100 	} else {
101 		ds->ds_link = 0;
102 		/*
103 		 * Switch antenna every 4 beacons.
104 		 * NB: assumes two antenna
105 		 */
106 		antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
107 	}
108 
109 	ds->ds_data = bf->bf_dma.cookie.dmac_address;
110 	/*
111 	 * Calculate rate code.
112 	 * XXX everything at min xmit rate
113 	 */
114 	rix = 0;
115 	rt = sc->hw_rate_table[sc->sc_curmode];
116 	rate = rt->info[rix].ratecode;
117 	if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
118 		rate |= rt->info[rix].short_preamble;
119 
120 	ath9k_hw_set11n_txdesc(ah, ds,
121 	    MBLKL(mp) + IEEE80211_CRC_LEN, /* frame length */
122 	    ATH9K_PKT_TYPE_BEACON,	/* Atheros packet type */
123 	    MAX_RATE_POWER,		/* FIXME */
124 	    ATH9K_TXKEYIX_INVALID,	/* no encryption */
125 	    ATH9K_KEY_TYPE_CLEAR,	/* no encryption */
126 	    flags);			/* no ack, veol for beacons */
127 
128 	/* NB: beacon's BufLen must be a multiple of 4 bytes */
129 	(void) ath9k_hw_filltxdesc(ah, ds,
130 	    roundup(MBLKL(mp), 4),	/* buffer length */
131 	    B_TRUE,			/* first segment */
132 	    B_TRUE,			/* last segment */
133 	    ds);			/* first descriptor */
134 
135 	(void) memset(series, 0, sizeof (struct ath9k_11n_rate_series) * 4);
136 	series[0].Tries = 1;
137 	series[0].Rate = rate;
138 	series[0].ChSel = sc->sc_tx_chainmask;
139 	series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
140 	ath9k_hw_set11n_ratescenario(ah, ds, ds, 0,
141 	    ctsrate, ctsduration, series, 4, 0);
142 #undef	USE_SHPREAMBLE
143 }
144 
145 /*
146  * Startup beacon transmission for adhoc mode when they are sent entirely
147  * by the hardware using the self-linked descriptor + veol trick.
148  */
149 static void
150 arn_beacon_start_adhoc(struct arn_softc *sc)
151 
152 {
153 	struct ath_buf *bf = list_head(&sc->sc_bcbuf_list);
154 	struct ieee80211_node *in = bf->bf_in;
155 	struct ieee80211com *ic = in->in_ic;
156 	struct ath_hal *ah = sc->sc_ah;
157 	mblk_t *mp;
158 
159 	mp = bf->bf_m;
160 	if (ieee80211_beacon_update(ic, bf->bf_in, &sc->asc_boff, mp, 0))
161 		bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
162 
163 	/* Construct tx descriptor. */
164 	arn_beacon_setup(sc, bf);
165 
166 	/*
167 	 * Stop any current dma and put the new frame on the queue.
168 	 * This should never fail since we check above that no frames
169 	 * are still pending on the queue.
170 	 */
171 	if (!ath9k_hw_stoptxdma(ah, sc->sc_beaconq)) {
172 		arn_problem("ath: beacon queue %d did not stop?\n",
173 		    sc->sc_beaconq);
174 	}
175 	ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
176 
177 	/* NB: caller is known to have already stopped tx dma */
178 	(void) ath9k_hw_puttxbuf(ah, sc->sc_beaconq, bf->bf_daddr);
179 	(void) ath9k_hw_txstart(ah, sc->sc_beaconq);
180 
181 	ARN_DBG((ARN_DBG_BEACON, "arn: arn_bstuck_process(): "
182 	    "TXDP%u = %llx (%p)\n", sc->sc_beaconq,
183 	    ito64(bf->bf_daddr), bf->bf_desc));
184 }
185 
186 uint32_t
187 arn_beaconq_setup(struct ath_hal *ah)
188 {
189 	struct ath9k_tx_queue_info qi;
190 
191 	(void) memset(&qi, 0, sizeof (qi));
192 	qi.tqi_aifs = 1;
193 	qi.tqi_cwmin = 0;
194 	qi.tqi_cwmax = 0;
195 	/* NB: don't enable any interrupts */
196 	return (ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi));
197 }
198 
199 int
200 arn_beacon_alloc(struct arn_softc *sc, struct ieee80211_node *in)
201 {
202 	ieee80211com_t	*ic = in->in_ic;
203 	struct ath_buf *bf;
204 	mblk_t *mp;
205 
206 	mutex_enter(&sc->sc_bcbuflock);
207 	bf = list_head(&sc->sc_bcbuf_list);
208 	if (bf == NULL) {
209 		arn_problem("arn: arn_beacon_alloc():"
210 		    "no dma buffers");
211 		mutex_exit(&sc->sc_bcbuflock);
212 		return (ENOMEM);
213 	}
214 
215 	mp = ieee80211_beacon_alloc(ic, in, &sc->asc_boff);
216 	if (mp == NULL) {
217 		arn_problem("ath: arn_beacon_alloc():"
218 		    "cannot get mbuf\n");
219 		mutex_exit(&sc->sc_bcbuflock);
220 		return (ENOMEM);
221 	}
222 	ASSERT(mp->b_cont == NULL);
223 	bf->bf_m = mp;
224 	bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
225 	bf->bf_in = ieee80211_ref_node(in);
226 	mutex_exit(&sc->sc_bcbuflock);
227 
228 	return (0);
229 }
230 
231 
232 void
233 arn_beacon_return(struct arn_softc *sc)
234 {
235 	struct ath_buf *bf;
236 
237 	mutex_enter(&sc->sc_bcbuflock);
238 	bf = list_head(&sc->sc_bcbuf_list);
239 	while (bf != NULL) {
240 		if (bf->bf_m != NULL) {
241 			freemsg(bf->bf_m);
242 			bf->bf_m = NULL;
243 		}
244 		if (bf->bf_in != NULL) {
245 			ieee80211_free_node(bf->bf_in);
246 			bf->bf_in = NULL;
247 		}
248 		bf = list_next(&sc->sc_bcbuf_list, bf);
249 	}
250 	mutex_exit(&sc->sc_bcbuflock);
251 }
252 
253 void
254 arn_beacon_config(struct arn_softc *sc)
255 
256 {
257 	struct ath_hal *ah = sc->sc_ah;
258 	struct ath_beacon_config conf;
259 	ieee80211com_t *ic = (ieee80211com_t *)sc;
260 	struct ieee80211_node *in = ic->ic_bss;
261 	uint32_t nexttbtt, intval;
262 
263 	(void) memset(&conf, 0, sizeof (struct ath_beacon_config));
264 
265 	/* XXX fix me */
266 	conf.beacon_interval = in->in_intval ?
267 	    in->in_intval : ATH_DEFAULT_BINTVAL;
268 	conf.listen_interval = 1;
269 	conf.dtim_period = conf.beacon_interval;
270 	conf.dtim_count = 1;
271 	conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
272 
273 	/* extract tstamp from last beacon and convert to TU */
274 	// nexttbtt = TSF_TO_TU(sc->bc_tstamp >> 32, sc->bc_tstamp);
275 	/* XXX fix me */
276 	nexttbtt = (ARN_LE_READ_32(in->in_tstamp.data + 4) << 22) |
277 	    (ARN_LE_READ_32(in->in_tstamp.data) >> 10);
278 
279 	/* XXX conditionalize multi-bss support? */
280 	if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
281 		/*
282 		 * For multi-bss ap support beacons are either staggered
283 		 * evenly over N slots or burst together.  For the former
284 		 * arrange for the SWBA to be delivered for each slot.
285 		 * Slots that are not occupied will generate nothing.
286 		 */
287 		/* NB: the beacon interval is kept internally in TU's */
288 		intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
289 		intval /= ATH_BCBUF; /* for staggered beacons */
290 	} else {
291 		intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
292 	}
293 
294 	if (nexttbtt == 0)	/* e.g. for ap mode */
295 		nexttbtt = intval;
296 	else if (intval)	/* NB: can be 0 for monitor mode */
297 		nexttbtt = roundup(nexttbtt, intval);
298 
299 	ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
300 	    "nexttbtt %u intval %u (%u)\n",
301 	    nexttbtt, intval, conf.beacon_interval));
302 
303 	/* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */
304 	if (sc->sc_ah->ah_opmode == ATH9K_M_STA) {
305 		struct ath9k_beacon_state bs;
306 		uint64_t tsf;
307 		uint32_t tsftu;
308 		int dtimperiod, dtimcount, sleepduration;
309 		int cfpperiod, cfpcount;
310 
311 		/*
312 		 * Setup dtim and cfp parameters according to
313 		 * last beacon we received (which may be none).
314 		 */
315 		dtimperiod = conf.dtim_period;
316 		if (dtimperiod <= 0)		/* NB: 0 if not known */
317 			dtimperiod = 1;
318 		dtimcount = conf.dtim_count;
319 		if (dtimcount >= dtimperiod)	/* NB: sanity check */
320 			dtimcount = 0;
321 		cfpperiod = 1;			/* NB: no PCF support yet */
322 		cfpcount = 0;
323 
324 		sleepduration = conf.listen_interval * intval;
325 		if (sleepduration <= 0)
326 			sleepduration = intval;
327 
328 #define	FUDGE	2
329 		/*
330 		 * Pull nexttbtt forward to reflect the current
331 		 * TSF and calculate dtim+cfp state for the result.
332 		 */
333 		tsf = ath9k_hw_gettsf64(ah);
334 		tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
335 		do {
336 			nexttbtt += intval;
337 			if (--dtimcount < 0) {
338 				dtimcount = dtimperiod - 1;
339 				if (--cfpcount < 0)
340 					cfpcount = cfpperiod - 1;
341 			}
342 		} while (nexttbtt < tsftu);
343 #undef FUDGE
344 		(void) memset(&bs, 0, sizeof (bs));
345 		bs.bs_intval = intval;
346 		bs.bs_nexttbtt = nexttbtt;
347 		bs.bs_dtimperiod = dtimperiod*intval;
348 		bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
349 		bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
350 		bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
351 		bs.bs_cfpmaxduration = 0;
352 
353 		/*
354 		 * Calculate the number of consecutive beacons to miss
355 		 * before taking a BMISS interrupt.  The configuration
356 		 * is specified in TU so we only need calculate based
357 		 * on the beacon interval.  Note that we clamp the
358 		 * result to at most 15 beacons.
359 		 */
360 		if (sleepduration > intval) {
361 			bs.bs_bmissthreshold = conf.listen_interval *
362 			    ATH_DEFAULT_BMISS_LIMIT / 2;
363 		} else {
364 			bs.bs_bmissthreshold =
365 				DIV_ROUND_UP(conf.bmiss_timeout, intval);
366 			if (bs.bs_bmissthreshold > 15)
367 				bs.bs_bmissthreshold = 15;
368 			/* LINTED E_SUSPICIOUS_COMPARISON */
369 			else if (bs.bs_bmissthreshold <= 0)
370 				bs.bs_bmissthreshold = 1;
371 		}
372 
373 		/*
374 		 * Calculate sleep duration.  The configuration is
375 		 * given in ms.  We insure a multiple of the beacon
376 		 * period is used.  Also, if the sleep duration is
377 		 * greater than the DTIM period then it makes senses
378 		 * to make it a multiple of that.
379 		 *
380 		 * XXX fixed at 100ms
381 		 */
382 
383 		bs.bs_sleepduration =
384 		    roundup(IEEE80211_MS_TO_TU(100), sleepduration);
385 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
386 			bs.bs_sleepduration = bs.bs_dtimperiod;
387 
388 		ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
389 		    "tsf %llu "
390 		    "tsf:tu %u "
391 		    "intval %u "
392 		    "nexttbtt %u "
393 		    "dtim %u "
394 		    "nextdtim %u "
395 		    "bmiss %u "
396 		    "sleep %u "
397 		    "cfp:period %u "
398 		    "maxdur %u "
399 		    "next %u "
400 		    "timoffset %u\n",
401 		    (unsigned long long)tsf, tsftu,
402 		    bs.bs_intval,
403 		    bs.bs_nexttbtt,
404 		    bs.bs_dtimperiod,
405 		    bs.bs_nextdtim,
406 		    bs.bs_bmissthreshold,
407 		    bs.bs_sleepduration,
408 		    bs.bs_cfpperiod,
409 		    bs.bs_cfpmaxduration,
410 		    bs.bs_cfpnext,
411 		    bs.bs_timoffset));
412 
413 		(void) ath9k_hw_set_interrupts(ah, 0);
414 		ath9k_hw_set_sta_beacon_timers(ah, &bs);
415 		sc->sc_imask |= ATH9K_INT_BMISS;
416 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
417 	} else {
418 		uint64_t tsf;
419 		uint32_t tsftu;
420 		(void) ath9k_hw_set_interrupts(ah, 0);
421 		if (nexttbtt == intval)
422 			intval |= ATH9K_BEACON_RESET_TSF;
423 		if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
424 			/*
425 			 * Pull nexttbtt forward to reflect the current
426 			 * TSF
427 			 */
428 #define	FUDGE	2
429 			if (!(intval & ATH9K_BEACON_RESET_TSF)) {
430 				tsf = ath9k_hw_gettsf64(ah);
431 				tsftu = TSF_TO_TU((uint32_t)(tsf>>32),
432 				    (uint32_t)tsf) + FUDGE;
433 				do {
434 					nexttbtt += intval;
435 				} while (nexttbtt < tsftu);
436 			}
437 #undef FUDGE
438 			ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
439 			    "IBSS nexttbtt %u intval %u (%u)\n",
440 			    nexttbtt, intval & ~ATH9K_BEACON_RESET_TSF,
441 			    conf.beacon_interval));
442 
443 			/*
444 			 * In IBSS mode enable the beacon timers but only
445 			 * enable SWBA interrupts if we need to manually
446 			 * prepare beacon frames.  Otherwise we use a
447 			 * self-linked tx descriptor and let the hardware
448 			 * deal with things.
449 			 */
450 			intval |= ATH9K_BEACON_ENA;
451 			if (!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL))
452 				sc->sc_imask |= ATH9K_INT_SWBA;
453 			(void) arn_beaconq_config(sc);
454 		} else if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
455 			/*
456 			 * In AP mode we enable the beacon timers and
457 			 * SWBA interrupts to prepare beacon frames.
458 			 */
459 			intval |= ATH9K_BEACON_ENA;
460 			sc->sc_imask |= ATH9K_INT_SWBA;   /* beacon prepare */
461 			(void) arn_beaconq_config(sc);
462 		}
463 		ath9k_hw_beaconinit(ah, nexttbtt, intval);
464 		sc->sc_bmisscount = 0;
465 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
466 		/*
467 		 * When using a self-linked beacon descriptor in
468 		 * ibss mode load it once here.
469 		 */
470 		if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
471 		    (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL))
472 			arn_beacon_start_adhoc(sc);
473 	}
474 	sc->sc_bsync = 0;
475 }
476 
477 void
478 ath_beacon_sync(struct arn_softc *sc)
479 {
480 	/*
481 	 * Resync beacon timers using the tsf of the
482 	 * beacon frame we just received.
483 	 */
484 	arn_beacon_config(sc);
485 	sc->sc_flags |= SC_OP_BEACONS;
486 }
487 
488 void
489 arn_bmiss_proc(void *arg)
490 {
491 	struct arn_softc *sc = (struct arn_softc *)arg;
492 	ieee80211com_t *ic = (ieee80211com_t *)sc;
493 	uint64_t tsf, lastrx;
494 	uint_t  bmisstimeout;
495 
496 	if (ic->ic_opmode != IEEE80211_M_STA ||
497 	    ic->ic_state != IEEE80211_S_RUN) {
498 		return;
499 	}
500 
501 	ARN_LOCK(sc);
502 	lastrx = sc->sc_lastrx;
503 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
504 	bmisstimeout = ic->ic_bmissthreshold * ic->ic_bss->in_intval * 1024;
505 
506 	ARN_DBG((ARN_DBG_BEACON, "arn_bmiss_proc():"
507 	    " tsf %llu, lastrx %llu (%lld), bmiss %u\n",
508 	    (unsigned long long)tsf, (unsigned long long)sc->sc_lastrx,
509 	    (long long)(tsf - lastrx), bmisstimeout));
510 	ARN_UNLOCK(sc);
511 
512 	/* temp workaround */
513 	if (tsf - lastrx > bmisstimeout)
514 		ieee80211_beacon_miss(ic);
515 }
516