xref: /illumos-gate/usr/src/uts/common/io/arn/arn_beacon.c (revision d8a7fe16f62711cdc5c4267da8b34ff24a6b668c)
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 /* LINTED E_STATIC_UNUSED */
38 arn_beaconq_config(struct arn_softc *sc)
39 {
40 	struct ath_hal *ah = sc->sc_ah;
41 	struct ath9k_tx_queue_info qi;
42 
43 	(void) ath9k_hw_get_txq_props(ah, sc->sc_beaconq, &qi);
44 	if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
45 		/* Always burst out beacon and CAB traffic. */
46 		qi.tqi_aifs = 1;
47 		qi.tqi_cwmin = 0;
48 		qi.tqi_cwmax = 0;
49 	} else {
50 		/* Adhoc mode; important thing is to use 2x cwmin. */
51 		qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
52 		qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
53 		qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
54 	}
55 
56 	if (!ath9k_hw_set_txq_props(ah, sc->sc_beaconq, &qi)) {
57 		arn_problem("unable to update h/w beacon queue parameters\n");
58 		return (0);
59 	} else {
60 		/* push to h/w */
61 		(void) ath9k_hw_resettxqueue(ah, sc->sc_beaconq);
62 		return (1);
63 	}
64 }
65 
66 /*
67  * Associates the beacon frame buffer with a transmit descriptor.  Will set
68  * up all required antenna switch parameters, rate codes, and channel flags.
69  * Beacons are always sent out at the lowest rate, and are not retried.
70  */
71 
72 static void
73 arn_beacon_setup(struct arn_softc *sc, struct ath_buf *bf)
74 {
75 #define	USE_SHPREAMBLE(_ic) \
76 	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
77 	    == IEEE80211_F_SHPREAMBLE)
78 	mblk_t *mp = bf->bf_m;
79 	struct ath_hal *ah = sc->sc_ah;
80 	struct ath_desc *ds;
81 	/* LINTED E_FUNC_SET_NOT_USED */
82 	int flags, antenna = 0;
83 	struct ath_rate_table *rt;
84 	uint8_t rix, rate;
85 	struct ath9k_11n_rate_series series[4];
86 	int ctsrate = 0;
87 	int ctsduration = 0;
88 
89 	/* set up descriptors */
90 	ds = bf->bf_desc;
91 
92 	flags = ATH9K_TXDESC_NOACK;
93 	if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
94 	    (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
95 		ds->ds_link = bf->bf_daddr;	/* self-linked */
96 		flags |= ATH9K_TXDESC_VEOL;
97 		/*
98 		 * Let hardware handle antenna switching.
99 		 */
100 		antenna = 0;
101 	} else {
102 		ds->ds_link = 0;
103 		/*
104 		 * Switch antenna every 4 beacons.
105 		 * NB: assumes two antenna
106 		 */
107 		antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
108 	}
109 
110 	ds->ds_data = bf->bf_dma.cookie.dmac_address;
111 	/*
112 	 * Calculate rate code.
113 	 * XXX everything at min xmit rate
114 	 */
115 	rix = 0;
116 	rt = sc->hw_rate_table[sc->sc_curmode];
117 	rate = rt->info[rix].ratecode;
118 	if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
119 		rate |= rt->info[rix].short_preamble;
120 
121 	ath9k_hw_set11n_txdesc(ah, ds,
122 	    MBLKL(mp) + IEEE80211_CRC_LEN, /* frame length */
123 	    ATH9K_PKT_TYPE_BEACON,	/* Atheros packet type */
124 	    MAX_RATE_POWER,		/* FIXME */
125 	    ATH9K_TXKEYIX_INVALID,	/* no encryption */
126 	    ATH9K_KEY_TYPE_CLEAR,	/* no encryption */
127 	    flags);			/* no ack, veol for beacons */
128 
129 	/* NB: beacon's BufLen must be a multiple of 4 bytes */
130 	(void) ath9k_hw_filltxdesc(ah, ds,
131 	    roundup(MBLKL(mp), 4),	/* buffer length */
132 	    B_TRUE,			/* first segment */
133 	    B_TRUE,			/* last segment */
134 	    ds);			/* first descriptor */
135 
136 	(void) memset(series, 0, sizeof (struct ath9k_11n_rate_series) * 4);
137 	series[0].Tries = 1;
138 	series[0].Rate = rate;
139 	series[0].ChSel = sc->sc_tx_chainmask;
140 	series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
141 	ath9k_hw_set11n_ratescenario(ah, ds, ds, 0,
142 	    ctsrate, ctsduration, series, 4, 0);
143 #undef	USE_SHPREAMBLE
144 }
145 
146 /*
147  * Startup beacon transmission for adhoc mode when they are sent entirely
148  * by the hardware using the self-linked descriptor + veol trick.
149  */
150 static void
151 /* LINTED E_STATIC_UNUSED */
152 arn_beacon_start_adhoc(struct arn_softc *sc)
153 
154 {
155 	struct ath_buf *bf = list_head(&sc->sc_bcbuf_list);
156 	struct ieee80211_node *in = bf->bf_in;
157 	struct ieee80211com *ic = in->in_ic;
158 	struct ath_hal *ah = sc->sc_ah;
159 	mblk_t *mp;
160 
161 	mp = bf->bf_m;
162 	if (ieee80211_beacon_update(ic, bf->bf_in, &sc->asc_boff, mp, 0))
163 		bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
164 
165 	/* Construct tx descriptor. */
166 	arn_beacon_setup(sc, bf);
167 
168 	/*
169 	 * Stop any current dma and put the new frame on the queue.
170 	 * This should never fail since we check above that no frames
171 	 * are still pending on the queue.
172 	 */
173 	if (!ath9k_hw_stoptxdma(ah, sc->sc_beaconq)) {
174 		arn_problem("ath: beacon queue %d did not stop?\n",
175 		    sc->sc_beaconq);
176 	}
177 	ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
178 
179 	/* NB: caller is known to have already stopped tx dma */
180 	(void) ath9k_hw_puttxbuf(ah, sc->sc_beaconq, bf->bf_daddr);
181 	(void) ath9k_hw_txstart(ah, sc->sc_beaconq);
182 
183 	ARN_DBG((ARN_DBG_BEACON, "arn: arn_bstuck_process(): "
184 	    "TXDP%u = %llx (%p)\n", sc->sc_beaconq,
185 	    ito64(bf->bf_daddr), bf->bf_desc));
186 }
187 
188 uint32_t
189 arn_beaconq_setup(struct ath_hal *ah)
190 {
191 	struct ath9k_tx_queue_info qi;
192 
193 	(void) memset(&qi, 0, sizeof (qi));
194 	qi.tqi_aifs = 1;
195 	qi.tqi_cwmin = 0;
196 	qi.tqi_cwmax = 0;
197 	/* NB: don't enable any interrupts */
198 	return (ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi));
199 }
200 
201 int
202 arn_beacon_alloc(struct arn_softc *sc, struct ieee80211_node *in)
203 {
204 	ieee80211com_t	*ic = in->in_ic;
205 	struct ath_buf *bf;
206 	mblk_t *mp;
207 
208 	mutex_enter(&sc->sc_bcbuflock);
209 	bf = list_head(&sc->sc_bcbuf_list);
210 	if (bf == NULL) {
211 		arn_problem("arn: arn_beacon_alloc():"
212 		    "no dma buffers");
213 		mutex_exit(&sc->sc_bcbuflock);
214 		return (ENOMEM);
215 	}
216 
217 	mp = ieee80211_beacon_alloc(ic, in, &sc->asc_boff);
218 	if (mp == NULL) {
219 		arn_problem("ath: arn_beacon_alloc():"
220 		    "cannot get mbuf\n");
221 		mutex_exit(&sc->sc_bcbuflock);
222 		return (ENOMEM);
223 	}
224 	ASSERT(mp->b_cont == NULL);
225 	bf->bf_m = mp;
226 	bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
227 	bf->bf_in = ieee80211_ref_node(in);
228 	mutex_exit(&sc->sc_bcbuflock);
229 
230 	return (0);
231 }
232 
233 
234 void
235 arn_beacon_return(struct arn_softc *sc)
236 {
237 	struct ath_buf *bf;
238 
239 	mutex_enter(&sc->sc_bcbuflock);
240 	bf = list_head(&sc->sc_bcbuf_list);
241 	while (bf != NULL) {
242 		if (bf->bf_m != NULL) {
243 			freemsg(bf->bf_m);
244 			bf->bf_m = NULL;
245 		}
246 		if (bf->bf_in != NULL) {
247 			ieee80211_free_node(bf->bf_in);
248 			bf->bf_in = NULL;
249 		}
250 		bf = list_next(&sc->sc_bcbuf_list, bf);
251 	}
252 	mutex_exit(&sc->sc_bcbuflock);
253 }
254 
255 void
256 arn_beacon_config(struct arn_softc *sc)
257 {
258 	struct ath_beacon_config conf;
259 	ieee80211com_t *ic = (ieee80211com_t *)sc;
260 	struct ieee80211_node *in = ic->ic_bss;
261 
262 	struct ath9k_beacon_state bs;
263 	int dtimperiod, dtimcount, sleepduration;
264 	int cfpperiod, cfpcount;
265 	uint32_t nexttbtt = 0, intval, tsftu;
266 	uint64_t tsf;
267 
268 	(void) memset(&conf, 0, sizeof (struct ath_beacon_config));
269 
270 	/* XXX fix me */
271 	conf.beacon_interval = in->in_intval ?
272 	    in->in_intval : ATH_DEFAULT_BINTVAL;
273 	conf.listen_interval = 1;
274 	conf.dtim_period = conf.beacon_interval;
275 	conf.dtim_count = 1;
276 	conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
277 
278 	(void) memset(&bs, 0, sizeof (bs));
279 	intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
280 
281 	/*
282 	 * Setup dtim and cfp parameters according to
283 	 * last beacon we received (which may be none).
284 	 */
285 	dtimperiod = conf.dtim_period;
286 	if (dtimperiod <= 0)		/* NB: 0 if not known */
287 		dtimperiod = 1;
288 	dtimcount = conf.dtim_count;
289 	if (dtimcount >= dtimperiod)	/* NB: sanity check */
290 		dtimcount = 0;
291 	cfpperiod = 1;			/* NB: no PCF support yet */
292 	cfpcount = 0;
293 
294 	sleepduration = conf.listen_interval * intval;
295 	if (sleepduration <= 0)
296 		sleepduration = intval;
297 
298 	/*
299 	 * Pull nexttbtt forward to reflect the current
300 	 * TSF and calculate dtim+cfp state for the result.
301 	 */
302 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
303 	tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
304 	do {
305 		nexttbtt += intval;
306 		if (--dtimcount < 0) {
307 			dtimcount = dtimperiod - 1;
308 			if (--cfpcount < 0)
309 				cfpcount = cfpperiod - 1;
310 		}
311 	} while (nexttbtt < tsftu);
312 
313 	bs.bs_intval = intval;
314 	bs.bs_nexttbtt = nexttbtt;
315 	bs.bs_dtimperiod = dtimperiod*intval;
316 	bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
317 	bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
318 	bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
319 	bs.bs_cfpmaxduration = 0;
320 
321 	/*
322 	 * Calculate the number of consecutive beacons to miss* before taking
323 	 * a BMISS interrupt. The configuration is specified in TU so we only
324 	 * need calculate based on the beacon interval.  Note that we clamp the
325 	 * result to at most 15 beacons.
326 	 */
327 	if (sleepduration > intval) {
328 		bs.bs_bmissthreshold = conf.listen_interval *
329 		    ATH_DEFAULT_BMISS_LIMIT / 2;
330 	} else {
331 		bs.bs_bmissthreshold = DIV_ROUND_UP(conf.bmiss_timeout, intval);
332 		if (bs.bs_bmissthreshold > 15)
333 			bs.bs_bmissthreshold = 15;
334 		else if (bs.bs_bmissthreshold == 0)
335 			bs.bs_bmissthreshold = 1;
336 	}
337 
338 	/*
339 	 * Calculate sleep duration. The configuration is given in ms.
340 	 * We ensure a multiple of the beacon period is used. Also, if the sleep
341 	 * duration is greater than the DTIM period then it makes senses
342 	 * to make it a multiple of that.
343 	 *
344 	 * XXX fixed at 100ms
345 	 */
346 
347 	bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
348 	if (bs.bs_sleepduration > bs.bs_dtimperiod)
349 		bs.bs_sleepduration = bs.bs_dtimperiod;
350 
351 	/* TSF out of range threshold fixed at 1 second */
352 	bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
353 
354 	ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
355 	    "tsf: %llu tsftu: %u\n", tsf, tsftu));
356 	ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
357 	    "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
358 	    bs.bs_bmissthreshold, bs.bs_sleepduration,
359 	    bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext));
360 
361 	/* Set the computed STA beacon timers */
362 
363 	(void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
364 	ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
365 	sc->sc_imask |= ATH9K_INT_BMISS;
366 	(void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
367 }
368 
369 void
370 ath_beacon_sync(struct arn_softc *sc)
371 {
372 	/*
373 	 * Resync beacon timers using the tsf of the
374 	 * beacon frame we just received.
375 	 */
376 	arn_beacon_config(sc);
377 	sc->sc_flags |= SC_OP_BEACONS;
378 }
379 
380 void
381 arn_bmiss_proc(void *arg)
382 {
383 	struct arn_softc *sc = (struct arn_softc *)arg;
384 	ieee80211com_t *ic = (ieee80211com_t *)sc;
385 	uint64_t tsf, lastrx;
386 	uint_t  bmisstimeout;
387 
388 	if (ic->ic_opmode != IEEE80211_M_STA ||
389 	    ic->ic_state != IEEE80211_S_RUN) {
390 		return;
391 	}
392 
393 	ARN_LOCK(sc);
394 	lastrx = sc->sc_lastrx;
395 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
396 	bmisstimeout = ic->ic_bmissthreshold * ic->ic_bss->in_intval * 1024;
397 
398 	ARN_DBG((ARN_DBG_BEACON, "arn_bmiss_proc():"
399 	    " tsf %llu, lastrx %llu (%lld), bmiss %u\n",
400 	    (unsigned long long)tsf, (unsigned long long)sc->sc_lastrx,
401 	    (long long)(tsf - lastrx), bmisstimeout));
402 	ARN_UNLOCK(sc);
403 
404 	/* temp workaround */
405 	if (tsf - lastrx > bmisstimeout)
406 		ieee80211_beacon_miss(ic);
407 }
408