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