#include <sys/param.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/stream.h>
#include <sys/termio.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/strtty.h>
#include <sys/kbio.h>
#include <sys/cred.h>
#include <sys/stat.h>
#include <sys/consdev.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/pci.h>
#include <sys/errno.h>
#include <sys/gld.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/list.h>
#include <sys/byteorder.h>
#include <sys/strsun.h>
#include <inet/common.h>
#include <inet/nd.h>
#include <inet/mi.h>
#include <inet/wifi_ioctl.h>
#include "ath_hal.h"
#include "ath_impl.h"
static const char *acnames[] = {
"WME_AC_BE",
"WME_AC_BK",
"WME_AC_VI",
"WME_AC_VO",
"WME_UPSD"
};
extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
const char *
ath_get_hal_status_desc(HAL_STATUS status)
{
static const char *hal_status_desc[] = {
"No error",
"No hardware present or device not yet supported",
"Memory allocation failed",
"Hardware didn't respond as expected",
"EEPROM magic number invalid",
"EEPROM version invalid",
"EEPROM unreadable",
"EEPROM checksum invalid",
"EEPROM read problem",
"EEPROM mac address invalid",
"EEPROM size not supported",
"Attempt to change write-locked EEPROM",
"Invalid parameter to function",
"Hardware revision not supported",
"Hardware self-test failed",
"Operation incomplete"
};
if (status >= 0 && status < sizeof (hal_status_desc)/sizeof (char *))
return (hal_status_desc[status]);
else
return ("");
}
uint32_t
ath_calcrxfilter(ath_t *asc)
{
ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
uint32_t rfilt;
rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
| HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
if (ic->ic_opmode != IEEE80211_M_STA)
rfilt |= HAL_RX_FILTER_PROBEREQ;
if (ic->ic_opmode != IEEE80211_M_HOSTAP && asc->asc_promisc)
rfilt |= HAL_RX_FILTER_PROM;
if (ic->ic_opmode == IEEE80211_M_STA ||
ic->ic_opmode == IEEE80211_M_IBSS ||
ic->ic_state == IEEE80211_S_SCAN)
rfilt |= HAL_RX_FILTER_BEACON;
return (rfilt);
}
static int
ath_set_data_queue(ath_t *asc, int ac, int haltype)
{
HAL_TXQ_INFO qi;
int qnum;
struct ath_hal *ah = asc->asc_ah;
struct ath_txq *txq;
if (ac >= ATH_N(asc->asc_ac2q)) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
"ac %u out of range, max %u!\n",
ac, ATH_N(asc->asc_ac2q)));
return (1);
}
(void) memset(&qi, 0, sizeof (qi));
qi.tqi_subtype = haltype;
qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE;
qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
if (qnum == -1) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
"Unable to setup hardware queue for %s traffic!\n",
acnames[ac]));
return (1);
}
if (qnum >= ATH_N(asc->asc_txq)) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
"hal qnum %u out of range, max %u!\n",
qnum, ATH_N(asc->asc_txq)));
return (1);
}
if (!ATH_TXQ_SETUP(asc, qnum)) {
txq = &asc->asc_txq[qnum];
txq->axq_qnum = qnum;
txq->axq_depth = 0;
txq->axq_intrcnt = 0;
txq->axq_link = NULL;
list_create(&txq->axq_list, sizeof (struct ath_buf),
offsetof(struct ath_buf, bf_node));
mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
asc->asc_txqsetup |= 1<<qnum;
}
asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
return (0);
}
int
ath_txq_setup(ath_t *asc)
{
if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
return (1);
}
return (0);
}
void
ath_txq_cleanup(ath_t *asc)
{
int i;
mutex_destroy(&asc->asc_txbuflock);
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(asc, i)) {
struct ath_txq *txq = &asc->asc_txq[i];
ATH_HAL_RELEASETXQUEUE(asc->asc_ah, txq->axq_qnum);
mutex_destroy(&txq->axq_lock);
asc->asc_txqsetup &= ~(1 << txq->axq_qnum);
}
}
}
void
ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
{
const HAL_RATE_TABLE *rt;
int i;
for (i = 0; i < sizeof (asc->asc_rixmap); i++)
asc->asc_rixmap[i] = 0xff;
rt = asc->asc_rates[mode];
ASSERT(rt != NULL);
for (i = 0; i < rt->rateCount; i++)
asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] =
(uint8_t)i;
asc->asc_currates = rt;
asc->asc_curmode = mode;
asc->asc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
}
void
ath_mode_init(ath_t *asc)
{
ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
uint32_t rfilt;
rfilt = ath_calcrxfilter(asc);
ATH_HAL_SETRXFILTER(ah, rfilt);
ATH_HAL_SETOPMODE(ah);
ATH_HAL_SETMCASTFILTER(ah, asc->asc_mcast_hash[0],
asc->asc_mcast_hash[1]);
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
"mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
ic->ic_opmode, rfilt,
asc->asc_mcast_hash[0], asc->asc_mcast_hash[1]));
}
void
ath_stoprecv(ath_t *asc)
{
ATH_HAL_STOPPCURECV(asc->asc_ah);
ATH_HAL_SETRXFILTER(asc->asc_ah, 0);
ATH_HAL_STOPDMARECV(asc->asc_ah);
drv_usecwait(3000);
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
asc->asc_rxlink = NULL;
}
uint32_t
ath_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
{
static const uint32_t modeflags[] = {
0,
CHANNEL_A,
CHANNEL_B,
CHANNEL_PUREG,
0,
CHANNEL_108A,
CHANNEL_108G
};
return (modeflags[ieee80211_chan2mode(isc, chan)]);
}
int
ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
{
ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
HAL_CHANNEL *chans;
int i, ix;
uint32_t nchan;
chans = (HAL_CHANNEL *)
kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
"unable to get channel list\n"));
kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
return (EINVAL);
}
for (i = 0; i < nchan; i++) {
HAL_CHANNEL *c = &chans[i];
uint16_t flags;
ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
if (ix > IEEE80211_CHAN_MAX) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
"bad hal channel %d (%u/%x) ignored\n",
ix, c->channel, c->channelFlags));
continue;
}
if (ix < 0) {
ATH_DEBUG((ATH_DBG_AUX, "ath:ath_getchannels(): "
"hal channel %d (%u/%x) "
"cannot be handled, ignored\n",
ix, c->channel, c->channelFlags));
continue;
}
flags = c->channelFlags & CHANNEL_COMPAT;
if (c->channelFlags & CHANNEL_STURBO)
flags |= IEEE80211_CHAN_TURBO;
if (ic->ic_sup_channels[ix].ich_freq == 0) {
ic->ic_sup_channels[ix].ich_freq = c->channel;
ic->ic_sup_channels[ix].ich_flags = flags;
} else {
ic->ic_sup_channels[ix].ich_flags |= flags;
}
if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
asc->asc_have11g = 1;
}
kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
return (0);
}
static void
ath_drainq(ath_t *asc, struct ath_txq *txq)
{
struct ath_buf *bf;
for (;;) {
mutex_enter(&txq->axq_lock);
bf = list_head(&txq->axq_list);
if (bf == NULL) {
txq->axq_link = NULL;
mutex_exit(&txq->axq_lock);
break;
}
list_remove(&txq->axq_list, bf);
mutex_exit(&txq->axq_lock);
bf->bf_in = NULL;
mutex_enter(&asc->asc_txbuflock);
list_insert_tail(&asc->asc_txbuf_list, bf);
mutex_exit(&asc->asc_txbuflock);
}
}
void
ath_draintxq(ath_t *asc)
{
struct ath_hal *ah = asc->asc_ah;
struct ath_txq *txq;
int i;
if (!asc->asc_invalid) {
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(asc, i)) {
txq = &asc->asc_txq[i];
(void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
}
}
}
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(asc, i)) {
ath_drainq(asc, &asc->asc_txq[i]);
}
}
}
int
ath_startrecv(ath_t *asc)
{
struct ath_buf *bf;
asc->asc_rxlink = NULL;
bf = list_head(&asc->asc_rxbuf_list);
while (bf != NULL) {
ath_setup_desc(asc, bf);
bf = list_next(&asc->asc_rxbuf_list, bf);
}
bf = list_head(&asc->asc_rxbuf_list);
ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
ATH_HAL_RXENA(asc->asc_ah);
ath_mode_init(asc);
ATH_HAL_STARTPCURECV(asc->asc_ah);
return (0);
}
void
ath_chan_change(ath_t *asc, struct ieee80211_channel *chan)
{
struct ieee80211com *ic = &asc->asc_isc;
enum ieee80211_phymode mode;
mode = ieee80211_chan2mode(ic, chan);
if (mode != asc->asc_curmode)
ath_setcurmode(asc, mode);
}
int
ath_chan_set(ath_t *asc, struct ieee80211_channel *chan)
{
struct ath_hal *ah = asc->asc_ah;
ieee80211com_t *ic = &asc->asc_isc;
if (chan != ic->ic_ibss_chan) {
HAL_STATUS status;
HAL_CHANNEL hchan;
ATH_HAL_INTRSET(ah, 0);
ath_draintxq(asc);
ath_stoprecv(asc);
hchan.channel = chan->ich_freq;
hchan.channelFlags = ath_chan2flags(ic, chan);
if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
&hchan, AH_TRUE, &status)) {
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
"unable to reset channel %u (%uMhz)\n"
"flags 0x%x: '%s' (HAL status %u)\n",
ieee80211_chan2ieee(ic, chan), hchan.channel,
hchan.channelFlags,
ath_get_hal_status_desc(status), status));
return (EIO);
}
asc->asc_curchan = hchan;
if (ath_startrecv(asc) != 0) {
ath_problem("ath: ath_chan_set(): "
"restarting receiving logic failed\n");
return (EIO);
}
ic->ic_ibss_chan = chan;
ath_chan_change(asc, chan);
ATH_HAL_INTRSET(ah, asc->asc_imask);
}
return (0);
}
void
ath_beacon_config(ath_t *asc)
{
struct ath_hal *ah = asc->asc_ah;
ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ieee80211_node *in = ic->ic_bss;
uint32_t nexttbtt;
nexttbtt = (ATH_LE_READ_4(in->in_tstamp.data + 4) << 22) |
(ATH_LE_READ_4(in->in_tstamp.data) >> 10);
nexttbtt += in->in_intval;
if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
HAL_BEACON_STATE bs;
bzero(&bs, sizeof (bs));
bs.bs_intval = in->in_intval;
bs.bs_nexttbtt = nexttbtt;
bs.bs_dtimperiod = bs.bs_intval;
bs.bs_nextdtim = nexttbtt;
bs.bs_bmissthreshold = ic->ic_bmissthreshold;
if (bs.bs_bmissthreshold > 10)
bs.bs_bmissthreshold = 10;
else if (bs.bs_bmissthreshold < 1)
bs.bs_bmissthreshold = 1;
bs.bs_sleepduration =
roundup((100 * 1000) / 1024, bs.bs_intval);
if (bs.bs_sleepduration > bs.bs_dtimperiod)
bs.bs_sleepduration =
roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
"intval %u nexttbtt %u dtim %u"
" nextdtim %u bmiss %u sleep %u\n",
bs.bs_intval,
bs.bs_nexttbtt,
bs.bs_dtimperiod,
bs.bs_nextdtim,
bs.bs_bmissthreshold,
bs.bs_sleepduration));
ATH_HAL_INTRSET(ah, 0);
ATH_HAL_RESETTSF(ah);
ATH_HAL_BEACONTIMERS(ah, &bs);
asc->asc_imask |= HAL_INT_BMISS;
ATH_HAL_INTRSET(ah, asc->asc_imask);
} else {
ATH_HAL_INTRSET(ah, 0);
ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
asc->asc_imask |= HAL_INT_SWBA;
ATH_HAL_INTRSET(ah, asc->asc_imask);
}
}
static uint16_t
key_alloc_pair(ath_t *asc, ieee80211_keyix *txkeyix,
ieee80211_keyix *rxkeyix)
{
uint16_t i, keyix;
ASSERT(!asc->asc_splitmic);
for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
uint8_t b = asc->asc_keymap[i];
if (b == 0xff)
continue;
for (keyix = i * NBBY; keyix < (i + 1) * NBBY;
keyix++, b >>= 1) {
if ((b & 1) || isset(asc->asc_keymap, keyix+64)) {
continue;
}
setbit(asc->asc_keymap, keyix);
setbit(asc->asc_keymap, keyix+64);
ATH_DEBUG((ATH_DBG_AUX,
"key_alloc_pair: key pair %u,%u\n",
keyix, keyix+64));
*txkeyix = *rxkeyix = keyix;
return (1);
}
}
ATH_DEBUG((ATH_DBG_AUX, "key_alloc_pair:"
" out of pair space\n"));
return (0);
}
static int
key_alloc_2pair(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
{
uint16_t i, keyix;
ASSERT(asc->asc_splitmic);
for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
uint8_t b = asc->asc_keymap[i];
if (b != 0xff) {
keyix = i*NBBY;
while (b & 1) {
again:
keyix++;
b >>= 1;
}
if (isset(asc->asc_keymap, keyix+32) ||
isset(asc->asc_keymap, keyix+64) ||
isset(asc->asc_keymap, keyix+32+64)) {
if (keyix == (i+1)*NBBY) {
continue;
}
goto again;
}
setbit(asc->asc_keymap, keyix);
setbit(asc->asc_keymap, keyix+64);
setbit(asc->asc_keymap, keyix+32);
setbit(asc->asc_keymap, keyix+32+64);
ATH_DEBUG((ATH_DBG_AUX,
"key_alloc_2pair: key pair %u,%u %u,%u\n",
keyix, keyix+64,
keyix+32, keyix+32+64));
*txkeyix = *rxkeyix = keyix;
return (1);
}
}
ATH_DEBUG((ATH_DBG_AUX, "key_alloc_2pair:"
" out of pair space\n"));
return (0);
}
static int
key_alloc_single(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
{
uint16_t i, keyix;
for (i = 0; i < ATH_N(asc->asc_keymap); i++) {
uint8_t b = asc->asc_keymap[i];
if (b != 0xff) {
keyix = i*NBBY;
while (b & 1)
keyix++, b >>= 1;
setbit(asc->asc_keymap, keyix);
ATH_DEBUG((ATH_DBG_AUX, "key_alloc_single:"
" key %u\n", keyix));
*txkeyix = *rxkeyix = keyix;
return (1);
}
}
return (0);
}
int
ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
ath_t *asc = (ath_t *)ic;
if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
return (key_alloc_single(asc, keyix, rxkeyix));
} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
if (asc->asc_splitmic)
return (key_alloc_2pair(asc, keyix, rxkeyix));
else
return (key_alloc_pair(asc, keyix, rxkeyix));
} else {
return (key_alloc_single(asc, keyix, rxkeyix));
}
}
int
ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
{
ath_t *asc = (ath_t *)ic;
struct ath_hal *ah = asc->asc_ah;
const struct ieee80211_cipher *cip = k->wk_cipher;
ieee80211_keyix keyix = k->wk_keyix;
ATH_DEBUG((ATH_DBG_AUX, "ath_key_delete:"
" delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
ATH_HAL_KEYRESET(ah, keyix);
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic)
ATH_HAL_KEYRESET(ah, keyix+32);
if (keyix >= IEEE80211_WEP_NKID) {
clrbit(asc->asc_keymap, keyix);
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
clrbit(asc->asc_keymap, keyix+64);
if (asc->asc_splitmic) {
clrbit(asc->asc_keymap, keyix+32);
clrbit(asc->asc_keymap, keyix+32+64);
}
}
}
return (1);
}
static void
ath_keyprint(ath_t *asc, const char *tag, uint_t ix,
const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
{
static const char *ciphers[] = {
"WEP",
"AES-OCB",
"AES-CCM",
"CKIP",
"TKIP",
"CLR",
};
int i, n;
char buf[MAX_IEEE80211STR], buft[32];
(void) snprintf(buf, sizeof (buf), "%s: [%02u] %s ",
tag, ix, ciphers[hk->kv_type]);
for (i = 0, n = hk->kv_len; i < n; i++) {
(void) snprintf(buft, sizeof (buft), "%02x", hk->kv_val[i]);
(void) strlcat(buf, buft, sizeof (buf));
}
(void) snprintf(buft, sizeof (buft), " mac %s",
ieee80211_macaddr_sprintf(mac));
(void) strlcat(buf, buft, sizeof (buf));
if (hk->kv_type == HAL_CIPHER_TKIP) {
(void) snprintf(buft, sizeof (buft), " %s ",
asc->asc_splitmic ? "mic" : "rxmic");
(void) strlcat(buf, buft, sizeof (buf));
for (i = 0; i < sizeof (hk->kv_mic); i++) {
(void) snprintf(buft, sizeof (buft), "%02x",
hk->kv_mic[i]);
(void) strlcat(buf, buft, sizeof (buf));
}
if (!asc->asc_splitmic) {
(void) snprintf(buft, sizeof (buft), " txmic ");
(void) strlcat(buf, buft, sizeof (buf));
for (i = 0; i < sizeof (hk->kv_txmic); i++) {
(void) snprintf(buft, sizeof (buft), "%02x",
hk->kv_txmic[i]);
(void) strlcat(buf, buft, sizeof (buf));
}
}
}
ATH_DEBUG((ATH_DBG_AUX, "%s", buf));
}
static int
ath_keyset_tkip(ath_t *asc, const struct ieee80211_key *k,
HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
{
#define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
static const uint8_t zerobssid[IEEE80211_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
struct ath_hal *ah = asc->asc_ah;
ASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP);
if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
if (asc->asc_splitmic) {
(void) memcpy(hk->kv_mic, k->wk_txmic,
sizeof (hk->kv_mic));
ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix, hk,
zerobssid);
if (!ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid))
return (0);
(void) memcpy(hk->kv_mic, k->wk_rxmic,
sizeof (hk->kv_mic));
ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix+32,
hk, mac);
return (ATH_HAL_KEYSET(ah, k->wk_keyix+32, hk, mac));
} else {
(void) memcpy(hk->kv_mic, k->wk_rxmic,
sizeof (hk->kv_mic));
(void) memcpy(hk->kv_txmic, k->wk_txmic,
sizeof (hk->kv_txmic));
ath_keyprint(asc, "ath_keyset_tkip", k->wk_keyix, hk,
mac);
return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, mac));
}
} else if (k->wk_flags & IEEE80211_KEY_XR) {
(void) memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
k->wk_txmic : k->wk_rxmic, sizeof (hk->kv_mic));
ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix, hk,
zerobssid);
return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid));
}
return (0);
#undef IEEE80211_KEY_XR
}
int
ath_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
static const uint8_t ciphermap[] = {
HAL_CIPHER_WEP,
HAL_CIPHER_TKIP,
HAL_CIPHER_AES_OCB,
HAL_CIPHER_AES_CCM,
HAL_CIPHER_CKIP,
HAL_CIPHER_CLR,
};
ath_t *asc = (ath_t *)ic;
struct ath_hal *ah = asc->asc_ah;
const struct ieee80211_cipher *cip = k->wk_cipher;
HAL_KEYVAL hk;
bzero(&hk, sizeof (hk));
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
ASSERT(cip->ic_cipher < ATH_N(ciphermap));
hk.kv_type = ciphermap[cip->ic_cipher];
hk.kv_len = k->wk_keylen;
bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
} else {
hk.kv_type = HAL_CIPHER_CLR;
}
if (hk.kv_type == HAL_CIPHER_TKIP &&
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
return (ath_keyset_tkip(asc, k, &hk, mac));
} else {
ath_keyprint(asc, "ath_keyset:", k->wk_keyix, &hk, mac);
return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
}
}
void
ath_set_shortslot(ieee80211com_t *ic, int onoff)
{
struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
if (onoff)
ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_9);
else
ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_20);
}
int
ath_reset(ieee80211com_t *ic)
{
ath_t *asc = (ath_t *)ic;
struct ath_hal *ah = asc->asc_ah;
struct ieee80211_channel *ch;
HAL_STATUS status;
ch = ic->ic_curchan;
asc->asc_curchan.channel = ch->ich_freq;
asc->asc_curchan.channelFlags = ath_chan2flags(ic, ch);
ATH_HAL_INTRSET(ah, 0);
ath_draintxq(asc);
if (ATH_IS_RUNNING(asc)) {
ath_stoprecv(asc);
if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
&asc->asc_curchan, AH_TRUE, &status)) {
ath_problem("ath: ath_reset(): "
"resetting hardware failed, '%s' (HAL status %u)\n",
ath_get_hal_status_desc(status), status);
}
ath_chan_change(asc, ch);
}
if (ATH_IS_RUNNING(asc)) {
if (ath_startrecv(asc) != 0)
ath_problem("ath: ath_reset(): "
"starting receiving logic failed\n");
if (ic->ic_state == IEEE80211_S_RUN) {
ath_beacon_config(asc);
}
ATH_HAL_INTRSET(ah, asc->asc_imask);
}
return (0);
}