xref: /illumos-gate/usr/src/uts/common/io/ath/ath_aux.c (revision f808c858)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer,
15  * without modification.
16  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17  * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
18  * redistribution must be conditioned upon including a substantially
19  * similar Disclaimer requirement for further binary redistribution.
20  * 3. Neither the names of the above-listed copyright holders nor the names
21  * of any contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  *
24  * NO WARRANTY
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
28  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
30  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
33  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35  * THE POSSIBILITY OF SUCH DAMAGES.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 #include <sys/param.h>
41 #include <sys/types.h>
42 #include <sys/signal.h>
43 #include <sys/stream.h>
44 #include <sys/termio.h>
45 #include <sys/errno.h>
46 #include <sys/file.h>
47 #include <sys/cmn_err.h>
48 #include <sys/stropts.h>
49 #include <sys/strsubr.h>
50 #include <sys/strtty.h>
51 #include <sys/kbio.h>
52 #include <sys/cred.h>
53 #include <sys/stat.h>
54 #include <sys/consdev.h>
55 #include <sys/kmem.h>
56 #include <sys/modctl.h>
57 #include <sys/ddi.h>
58 #include <sys/sunddi.h>
59 #include <sys/pci.h>
60 #include <sys/errno.h>
61 #include <sys/gld.h>
62 #include <sys/dlpi.h>
63 #include <sys/ethernet.h>
64 #include <sys/list.h>
65 #include <sys/byteorder.h>
66 #include <sys/strsun.h>
67 #include <inet/common.h>
68 #include <inet/nd.h>
69 #include <inet/mi.h>
70 #include <inet/wifi_ioctl.h>
71 #include "ath_hal.h"
72 #include "ath_impl.h"
73 #include "ath_ieee80211.h"
74 
75 static const char *acnames[] = {
76 	"WME_AC_BE",
77 	"WME_AC_BK",
78 	"WME_AC_VI",
79 	"WME_AC_VO",
80 	"WME_UPSD"
81 };
82 
83 extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
84 
85 uint32_t
86 ath_calcrxfilter(ath_t *asc)
87 {
88 	ieee80211com_t *isc = (ieee80211com_t *)asc;
89 	struct ath_hal *ah = asc->asc_ah;
90 	uint32_t rfilt;
91 
92 	rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
93 	    | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
94 	if (isc->isc_opmode != IEEE80211_M_STA)
95 		rfilt |= HAL_RX_FILTER_PROBEREQ;
96 	if (isc->isc_opmode != IEEE80211_M_HOSTAP &&
97 	    (asc->asc_promisc & GLD_MAC_PROMISC_PHYS))	/* promiscuous */
98 		rfilt |= HAL_RX_FILTER_PROM;
99 	if (isc->isc_opmode == IEEE80211_M_STA ||
100 	    isc->isc_opmode == IEEE80211_M_IBSS ||
101 	    isc->isc_state == IEEE80211_S_SCAN)
102 		rfilt |= HAL_RX_FILTER_BEACON;
103 	return (rfilt);
104 }
105 
106 static int
107 ath_set_data_queue(ath_t *asc, int ac, int haltype)
108 {
109 	HAL_TXQ_INFO qi;
110 	int qnum;
111 	struct ath_hal *ah = asc->asc_ah;
112 	struct ath_txq *txq;
113 
114 	if (ac >= ATH_N(asc->asc_ac2q)) {
115 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
116 		    "ac %u out of range, max %u!\n",
117 		    ac, ATH_N(asc->asc_ac2q)));
118 		return (1);
119 	}
120 	(void) memset(&qi, 0, sizeof (qi));
121 	qi.tqi_subtype = haltype;
122 	/*
123 	 * Enable interrupts only for EOL and DESC conditions.
124 	 * We mark tx descriptors to receive a DESC interrupt
125 	 * when a tx queue gets deep; otherwise waiting for the
126 	 * EOL to reap descriptors.  Note that this is done to
127 	 * reduce interrupt load and this only defers reaping
128 	 * descriptors, never transmitting frames.  Aside from
129 	 * reducing interrupts this also permits more concurrency.
130 	 * The only potential downside is if the tx queue backs
131 	 * up in which case the top half of the kernel may backup
132 	 * due to a lack of tx descriptors.
133 	 */
134 	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
135 	qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
136 	if (qnum == -1) {
137 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
138 		    "Unable to setup hardware queue for %s traffic!\n",
139 		    acnames[ac]));
140 		return (1);
141 	}
142 	if (qnum >= ATH_N(asc->asc_txq)) {
143 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
144 		    "hal qnum %u out of range, max %u!\n",
145 		    qnum, ATH_N(asc->asc_txq)));
146 		return (1);
147 	}
148 	if (!ATH_TXQ_SETUP(asc, qnum)) {
149 		txq = &asc->asc_txq[qnum];
150 		txq->axq_qnum = qnum;
151 		txq->axq_depth = 0;
152 		txq->axq_intrcnt = 0;
153 		txq->axq_link = NULL;
154 		list_create(&txq->axq_list, sizeof (struct ath_buf),
155 		    offsetof(struct ath_buf, bf_node));
156 		mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
157 		asc->asc_txqsetup |= 1<<qnum;
158 	}
159 	asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
160 	return (0);
161 }
162 
163 int
164 ath_txq_setup(ath_t *asc)
165 {
166 	if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
167 	    ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
168 	    ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
169 	    ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
170 		return (1);
171 	}
172 
173 	return (0);
174 }
175 
176 void
177 ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
178 {
179 	const HAL_RATE_TABLE *rt;
180 	int i;
181 
182 	for (i = 0; i < sizeof (asc->asc_rixmap); i++)
183 		asc->asc_rixmap[i] = 0xff;
184 
185 	rt = asc->asc_rates[mode];
186 	ASSERT(rt != NULL);
187 
188 	for (i = 0; i < rt->rateCount; i++)
189 		asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
190 
191 	asc->asc_currates = rt;
192 	asc->asc_curmode = mode;
193 }
194 
195 /* Set correct parameters for a certain mode */
196 void
197 ath_mode_init(ath_t *asc)
198 {
199 	ieee80211com_t *isc = (ieee80211com_t *)asc;
200 	struct ath_hal *ah = asc->asc_ah;
201 	uint32_t rfilt;
202 
203 	/* configure rx filter */
204 	rfilt = ath_calcrxfilter(asc);
205 	ATH_HAL_SETRXFILTER(ah, rfilt);
206 	ATH_HAL_SETOPMODE(ah);
207 	ATH_HAL_SETMCASTFILTER(ah, asc->asc_mfilt[0], asc->asc_mfilt[1]);
208 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
209 	    "mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
210 	    isc->isc_opmode, rfilt,
211 	    asc->asc_mfilt[0], asc->asc_mfilt[1]));
212 }
213 
214 
215 /*
216  * Disable the receive h/w in preparation for a reset.
217  */
218 void
219 ath_stoprecv(ath_t *asc)
220 {
221 	ATH_HAL_STOPPCURECV(asc->asc_ah);	/* disable PCU */
222 	ATH_HAL_SETRXFILTER(asc->asc_ah, 0);	/* clear recv filter */
223 	ATH_HAL_STOPDMARECV(asc->asc_ah);	/* disable DMA engine */
224 	drv_usecwait(3000);
225 
226 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
227 	    ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
228 	asc->asc_rxlink = NULL;
229 }
230 
231 uint32_t
232 ath_chan2flags(ieee80211com_t *isc, struct ieee80211channel *chan)
233 {
234 	static const uint32_t modeflags[] = {
235 	    0,				/* IEEE80211_MODE_AUTO */
236 	    CHANNEL_A,			/* IEEE80211_MODE_11A */
237 	    CHANNEL_B,			/* IEEE80211_MODE_11B */
238 	    CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
239 	    CHANNEL_T			/* IEEE80211_MODE_TURBO */
240 	};
241 	return (modeflags[ieee80211_chan2mode(isc, chan)]);
242 }
243 
244 
245 int
246 ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
247 {
248 	ieee80211com_t *isc = (ieee80211com_t *)asc;
249 	struct ath_hal *ah = asc->asc_ah;
250 	HAL_CHANNEL *chans;
251 	int i, ix;
252 	uint32_t nchan;
253 
254 	chans = (HAL_CHANNEL *)
255 	    kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
256 
257 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
258 	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
259 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
260 		    "unable to get channel list\n");
261 		kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL)));
262 		return (EINVAL);
263 	}
264 
265 	/*
266 	 * Convert HAL channels to ieee80211 ones and insert
267 	 * them in the table according to their channel number.
268 	 */
269 	for (i = 0; i < nchan; i++) {
270 		HAL_CHANNEL *c = &chans[i];
271 		ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
272 		if (ix > IEEE80211_CHAN_MAX) {
273 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
274 			    "bad hal channel %u (%u/%x) ignored\n",
275 			    ix, c->channel, c->channelFlags));
276 			continue;
277 		}
278 		/* NB: flags are known to be compatible */
279 		if (isc->isc_channels[ix].ich_freq == 0) {
280 			isc->isc_channels[ix].ich_freq = c->channel;
281 			isc->isc_channels[ix].ich_flags = c->channelFlags;
282 		} else {
283 			/* channels overlap; e.g. 11g and 11b */
284 			isc->isc_channels[ix].ich_flags |= c->channelFlags;
285 		}
286 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
287 			asc->asc_have11g = 1;
288 	}
289 	kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
290 	return (0);
291 }
292 
293 static void
294 ath_drainq(ath_t *asc, struct ath_txq *txq)
295 {
296 	struct ath_buf *bf;
297 
298 	/*
299 	 * This assumes output has been stopped.
300 	 */
301 	for (;;) {
302 		mutex_enter(&txq->axq_lock);
303 		bf = list_head(&txq->axq_list);
304 		if (bf == NULL) {
305 			txq->axq_link = NULL;
306 			mutex_exit(&txq->axq_lock);
307 			break;
308 		}
309 		list_remove(&txq->axq_list, bf);
310 		mutex_exit(&txq->axq_lock);
311 		bf->bf_in = NULL;
312 		mutex_enter(&asc->asc_txbuflock);
313 		list_insert_tail(&asc->asc_txbuf_list, bf);
314 		mutex_exit(&asc->asc_txbuflock);
315 	}
316 }
317 
318 
319 /*
320  * Drain the transmit queues and reclaim resources.
321  */
322 void
323 ath_draintxq(ath_t *asc)
324 {
325 	struct ath_hal *ah = asc->asc_ah;
326 	struct ath_txq *txq;
327 	int i;
328 
329 	if (!asc->asc_invalid) {
330 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
331 			if (ATH_TXQ_SETUP(asc, i)) {
332 				txq = &asc->asc_txq[i];
333 				(void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
334 			}
335 		}
336 	}
337 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
338 		if (ATH_TXQ_SETUP(asc, i)) {
339 			ath_drainq(asc, &asc->asc_txq[i]);
340 		}
341 	}
342 }
343 
344 
345 /* Enable the receive h/w following a reset */
346 int
347 ath_startrecv(ath_t *asc)
348 {
349 	struct ath_buf *bf;
350 
351 	asc->asc_rxlink = NULL;
352 
353 	bf = list_head(&asc->asc_rxbuf_list);
354 	while (bf != NULL) {
355 		ath_setup_desc(asc, bf);
356 		bf = list_next(&asc->asc_rxbuf_list, bf);
357 	}
358 
359 	bf = list_head(&asc->asc_rxbuf_list);
360 	ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
361 	ATH_HAL_RXENA(asc->asc_ah);		/* enable recv descriptors */
362 	ath_mode_init(asc);			/* set filters, etc. */
363 	ATH_HAL_STARTPCURECV(asc->asc_ah);	/* re-enable PCU/DMA engine */
364 	return (0);
365 }
366 
367 /*
368  * Set/change channels.  If the channel is really being changed,
369  * it's done by resetting the chip.  To accomplish this we must
370  * first cleanup any pending DMA.
371  */
372 int
373 ath_chan_set(ath_t *asc, struct ieee80211channel *chan)
374 {
375 	struct ath_hal *ah = asc->asc_ah;
376 	ieee80211com_t *isc = &asc->asc_isc;
377 
378 	if (chan != isc->isc_ibss_chan) {
379 		HAL_STATUS status;
380 		HAL_CHANNEL hchan;
381 		enum ieee80211_phymode mode;
382 
383 		/*
384 		 * To switch channels clear any pending DMA operations;
385 		 * wait long enough for the RX fifo to drain, reset the
386 		 * hardware at the new frequency, and then re-enable
387 		 * the relevant bits of the h/w.
388 		 */
389 		ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
390 		ath_draintxq(asc);		/* clear pending tx frames */
391 		ath_stoprecv(asc);		/* turn off frame recv */
392 		/*
393 		 * Convert to a HAL channel description with
394 		 * the flags constrained to reflect the current
395 		 * operating mode.
396 		 */
397 		hchan.channel = chan->ich_freq;
398 		hchan.channelFlags = ath_chan2flags(isc, chan);
399 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode,
400 		    &hchan, AH_TRUE, &status)) {
401 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
402 			    "unable to reset channel %u (%uMhz)\n",
403 			    ieee80211_chan2ieee(isc, chan), chan->ich_freq));
404 			return (EIO);
405 		}
406 
407 		/*
408 		 * Re-enable rx framework.
409 		 */
410 		if (ath_startrecv(asc) != 0) {
411 			ath_problem("ath: ath_chan_set(): "
412 			    "restarting receiving logic failed\n");
413 			return (EIO);
414 		}
415 
416 		/*
417 		 * Change channels and update the h/w rate map
418 		 * if we're switching; e.g. 11a to 11b/g.
419 		 */
420 		isc->isc_ibss_chan = chan;
421 		mode = ieee80211_chan2mode(isc, chan);
422 		if (mode != asc->asc_curmode)
423 			ath_setcurmode(asc, mode);
424 		/*
425 		 * Re-enable interrupts.
426 		 */
427 		ATH_HAL_INTRSET(ah, asc->asc_imask);
428 	}
429 	return (0);
430 }
431 
432 
433 /*
434  * Configure the beacon and sleep timers.
435  *
436  * When operating as an AP this resets the TSF and sets
437  * up the hardware to notify us when we need to issue beacons.
438  *
439  * When operating in station mode this sets up the beacon
440  * timers according to the timestamp of the last received
441  * beacon and the current TSF, configures PCF and DTIM
442  * handling, programs the sleep registers so the hardware
443  * will wakeup in time to receive beacons, and configures
444  * the beacon miss handling so we'll receive a BMISS
445  * interrupt when we stop seeing beacons from the AP
446  * we've associated with.
447  */
448 void
449 ath_beacon_config(ath_t *asc)
450 {
451 	struct ath_hal *ah = asc->asc_ah;
452 	ieee80211com_t *isc = (ieee80211com_t *)asc;
453 	struct ieee80211_node *in = isc->isc_bss;
454 	uint32_t nexttbtt;
455 
456 	nexttbtt = (ATH_LE_READ_4(in->in_tstamp + 4) << 22) |
457 	    (ATH_LE_READ_4(in->in_tstamp) >> 10);
458 	nexttbtt += in->in_intval;
459 	if (isc->isc_opmode != IEEE80211_M_HOSTAP) {
460 		HAL_BEACON_STATE bs;
461 		uint32_t bmisstime;
462 
463 		/* NB: no PCF support right now */
464 		bzero(&bs, sizeof (bs));
465 		bs.bs_intval = in->in_intval;
466 		bs.bs_nexttbtt = nexttbtt;
467 		bs.bs_dtimperiod = bs.bs_intval;
468 		bs.bs_nextdtim = nexttbtt;
469 
470 		/*
471 		 * Calculate the number of consecutive beacons to miss
472 		 * before taking a BMISS interrupt.  The configuration
473 		 * is specified in ms, so we need to convert that to
474 		 * TU's and then calculate based on the beacon interval.
475 		 * Note that we clamp the result to at most 10 beacons.
476 		 */
477 		bmisstime = (isc->isc_bmisstimeout * 1000) / 1024;
478 		bs.bs_bmissthreshold = howmany(bmisstime, in->in_intval);
479 		if (bs.bs_bmissthreshold > 10)
480 			bs.bs_bmissthreshold = 10;
481 		else if (bs.bs_bmissthreshold <= 0)
482 			bs.bs_bmissthreshold = 1;
483 		/*
484 		 * Calculate sleep duration.  The configuration is
485 		 * given in ms.  We insure a multiple of the beacon
486 		 * period is used.  Also, if the sleep duration is
487 		 * greater than the DTIM period then it makes senses
488 		 * to make it a multiple of that.
489 		 */
490 		bs.bs_sleepduration =
491 		    roundup((100 * 1000) / 1024, bs.bs_intval);
492 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
493 			bs.bs_sleepduration =
494 			    roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
495 
496 
497 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
498 		    "intval %u nexttbtt %u dtim %u"
499 		    " nextdtim %u bmiss %u sleep %u\n",
500 		    bs.bs_intval,
501 		    bs.bs_nexttbtt,
502 		    bs.bs_dtimperiod,
503 		    bs.bs_nextdtim,
504 		    bs.bs_bmissthreshold,
505 		    bs.bs_sleepduration));
506 		ATH_HAL_INTRSET(ah, 0);
507 		/*
508 		 * Reset our tsf so the hardware will update the
509 		 * tsf register to reflect timestamps found in
510 		 * received beacons.
511 		 */
512 		ATH_HAL_RESETTSF(ah);
513 		ATH_HAL_BEACONTIMERS(ah, &bs);
514 		asc->asc_imask |= HAL_INT_BMISS;
515 		ATH_HAL_INTRSET(ah, asc->asc_imask);
516 	} else {
517 		ATH_HAL_INTRSET(ah, 0);
518 		ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
519 		asc->asc_imask |= HAL_INT_SWBA;	/* beacon prepare */
520 		ATH_HAL_INTRSET(ah, asc->asc_imask);
521 	}
522 }
523 
524 
525 
526 /*
527  * Fill the hardware key cache with key entries.
528  */
529 void
530 ath_initkeytable(ath_t *asc)
531 {
532 	ieee80211com_t *isc = (ieee80211com_t *)asc;
533 	struct ath_hal *ah = asc->asc_ah;
534 	int32_t i;
535 
536 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
537 		struct ieee80211_wepkey *k = &isc->isc_nw_keys[i];
538 		if (k->iwk_len == 0)
539 			ATH_HAL_KEYRESET(ah, i);
540 		else {
541 			HAL_KEYVAL hk;
542 
543 #ifdef DEBUG
544 			char tmp[200], stmp[10];
545 			int j;
546 			bzero(tmp, 200);
547 			bzero(stmp, 10);
548 			for (j = 0; j < k->iwk_len; j++) {
549 				(void) sprintf(stmp, "0x%02x ", k->iwk_key[j]);
550 				(void) strcat(tmp, stmp);
551 			}
552 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_initkeytable(): "
553 			    "key%d val=%s\n", i, tmp));
554 #endif /* DEBUG */
555 			bzero(&hk, sizeof (hk));
556 			hk.kv_type = HAL_CIPHER_WEP;
557 			hk.kv_len = k->iwk_len;
558 			bcopy(k->iwk_key, hk.kv_val, k->iwk_len);
559 			ATH_HAL_KEYSET(ah, i, &hk);
560 		}
561 	}
562 }
563 
564 void
565 ath_reset(ath_t *asc)
566 {
567 	ieee80211com_t *isc = (ieee80211com_t *)asc;
568 	struct ath_hal *ah = asc->asc_ah;
569 	struct ieee80211channel *ch;
570 	HAL_STATUS status;
571 	HAL_CHANNEL hchan;
572 
573 	/*
574 	 * Convert to a HAL channel description with the flags
575 	 * constrained to reflect the current operating mode.
576 	 */
577 	ch = isc->isc_ibss_chan;
578 	hchan.channel = ch->ich_freq;
579 	hchan.channelFlags = ath_chan2flags(isc, ch);
580 
581 	ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
582 	ath_draintxq(asc);		/* stop xmit side */
583 	if (asc->asc_invalid == 0)
584 		ath_stoprecv(asc);		/* stop recv side */
585 	/* indicate channel change so we do a full reset */
586 	if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode, &hchan,
587 	    AH_TRUE, &status)) {
588 		ath_problem("ath: ath_reset(): "
589 		    "reseting hardware failed, HAL status %u\n", status);
590 	}
591 	if (asc->asc_invalid == 0) {
592 		ath_initkeytable(asc);
593 		if (ath_startrecv(asc) != 0)	/* restart recv */
594 			ath_problem("ath: ath_reset(): "
595 			    "starting receiving logic failed\n");
596 		if (isc->isc_state == IEEE80211_S_RUN) {
597 			ath_beacon_config(asc);	/* restart beacons */
598 		}
599 		ATH_HAL_INTRSET(ah, asc->asc_imask);
600 	}
601 }
602