/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2008 Atheros Communications Inc.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/sysmacros.h>
#include <sys/mac_provider.h>
#include <sys/ethernet.h>
#include <sys/byteorder.h>
#include <inet/wifi_ioctl.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#include <sys/net80211_proto.h>
#include <sys/net80211_ht.h>
#include "arn_ath9k.h"
#include "arn_core.h"
#include "arn_reg.h"
#include "arn_hw.h"
/*
* Default 11n reates supported by this station.
*/
extern struct ieee80211_htrateset ieee80211_rateset_11n;
/*
* PIO access attributes for registers
*/
};
/*
* DMA access attributes for descriptors: NOT to be byte swapped.
*/
};
/*
* Describes the chip's DMA engine
*/
DMA_ATTR_V0, /* version number */
0, /* low address */
0xffffffffU, /* high address */
0x3ffffU, /* counter register max */
1, /* alignment */
0xFFF, /* burst sizes */
1, /* minimum transfer size */
0x3ffffU, /* max transfer size */
0xffffffffU, /* address register max */
1, /* no scatter-gather */
1, /* granularity of device */
0, /* DMA flags */
};
DMA_ATTR_V0, /* version number */
0, /* low address */
0xffffffffU, /* high address */
0xffffffffU, /* counter register max */
0x1000, /* alignment */
0xFFF, /* burst sizes */
1, /* minimum transfer size */
0xffffffffU, /* max transfer size */
0xffffffffU, /* address register max */
1, /* no scatter-gather */
1, /* granularity of device */
0, /* DMA flags */
};
static int arn_m_start(void *);
static void arn_m_stop(void *);
static int arn_m_promisc(void *, boolean_t);
static int arn_m_unicst(void *, const uint8_t *);
static int arn_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static int arn_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static void arn_m_propinfo(void *, const char *, mac_prop_id_t,
/* MAC Callcack Functions */
NULL,
NULL,
NULL,
NULL,
};
/*
* ARN_DBG_HW
* ARN_DBG_REG_IO
* ARN_DBG_QUEUE
* ARN_DBG_EEPROM
* ARN_DBG_XMIT
* ARN_DBG_RECV
* ARN_DBG_CALIBRATE
* ARN_DBG_CHANNEL
* ARN_DBG_INTERRUPT
* ARN_DBG_REGULATORY
* ARN_DBG_ANI
* ARN_DBG_POWER_MGMT
* ARN_DBG_KEYCACHE
* ARN_DBG_BEACON
* ARN_DBG_RATE
* ARN_DBG_INIT
* ARN_DBG_ATTACH
* ARN_DBG_DEATCH
* ARN_DBG_AGGR
* ARN_DBG_RESET
* ARN_DBG_FATAL
* ARN_DBG_ANY
* ARN_DBG_ALL
*/
/*
*/
void
{
}
/*
* Normal log information independent of debug.
*/
void
{
}
void
{
if (dbg_flags & arn_dbg_mask) {
}
}
/*
* Read and write, they both share the same lock. We do this to serialize
* reads and writes on Atheros 802.11n PCI devices only. This is required
* as the FIFO on these devices can only accept sanely 2 requests. After
* from happening.
*/
void
{
} else {
}
}
unsigned int
{
} else {
}
return (val);
}
/*
* Allocate an area of memory and a DMA handle for accessing it
*/
static int
{
int err;
/*
* Allocate handle
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Allocate memory
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Bind the two together
*/
if (err != DDI_DMA_MAPPED)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Free one allocated area of DMAable memory
*/
static void
{
}
}
}
/*
* Initialize tx, rx. or beacon buffer list. Allocate DMA memory for
* each buffer.
*/
static int
int nbuf,
{
int i, err;
/* alloc DMA memory */
if (err != DDI_SUCCESS)
return (err);
}
return (DDI_SUCCESS);
}
/*
* Destroy tx, rx or beacon buffer list. Free DMA memory.
*/
static void
{
if (!buflist)
return;
}
/* Free DMA buffer */
}
}
}
static void
{
#ifdef ARN_IBSS
#endif
/* Free descriptor DMA buffer */
}
static int
{
int err;
#ifdef ARN_IBSS
#else
#endif
/* virtual address of the first descriptor */
"%p (%d) -> %p\n",
#ifdef ARN_IBSS
#else
#endif
#ifdef ARN_TX_AGGREGRATION
sc->tx_dmabuf_size =
#else
sc->tx_dmabuf_size =
#endif
sc->rx_dmabuf_size =
/* create RX buffer list */
if (err != DDI_SUCCESS) {
return (err);
}
/* create TX buffer list */
if (err != DDI_SUCCESS) {
return (err);
}
/* create beacon buffer list */
#ifdef ARN_IBSS
if (err != DDI_SUCCESS) {
return (err);
}
#endif
return (DDI_SUCCESS);
}
static void
{
int i;
for (i = 0; i < sizeof (sc->asc_rixmap); i++)
/*
* All protection frames are transmited at 2Mb/s for
* 11g, otherwise at 1Mb/s.
* XXX select protection rate index from rate table.
*/
}
static enum wireless_mode
{
return (ATH9K_MODE_11A);
return (ATH9K_MODE_11G);
return (ATH9K_MODE_11B);
return (ATH9K_MODE_11NA_HT20);
return (ATH9K_MODE_11NG_HT20);
return (ATH9K_MODE_11NA_HT40PLUS);
return (ATH9K_MODE_11NA_HT40MINUS);
return (ATH9K_MODE_11NG_HT40PLUS);
return (ATH9K_MODE_11NG_HT40MINUS);
return (ATH9K_MODE_11B);
}
static void
{
/* read back in case value is clamped */
}
}
{
/*
* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
* 0 for no restriction
* 1 for 1/4 us
* 2 for 1/2 us
* 3 for 1 us
* 4 for 2 us
* 5 for 4 us
* 6 for 8 us
* 7 for 16 us
*/
switch (mpdudensity) {
case 0:
return (0);
case 1:
case 2:
case 3:
/*
* Our lower layer calculations limit our
* precision to 1 microsecond
*/
return (1);
case 4:
return (2);
case 5:
return (4);
case 6:
return (8);
case 7:
return (16);
default:
return (0);
}
}
static void
{
int i, maxrates;
/* rate_table = arn_get_ratetable(sc, mode); */
switch (mode) {
case IEEE80211_MODE_11A:
break;
case IEEE80211_MODE_11B:
break;
case IEEE80211_MODE_11G:
break;
#ifdef ARN_11N
case IEEE80211_MODE_11NA_HT20:
break;
case IEEE80211_MODE_11NG_HT20:
break;
break;
break;
break;
break;
#endif
default:
"invalid mode %u\n", mode));
break;
}
if (rate_table == NULL)
return;
"rate table too small (%u > %u)\n",
} else
"maxrates is %d\n", maxrates));
for (i = 0; i < maxrates; i++) {
}
}
static int
{
struct ath9k_channel *c;
/* Fill in ah->ah_channels */
B_FALSE, 1)) {
"unable to collect channel list; "
"regdomain likely %u country code %u\n",
rd, CTRY_DEFAULT));
return (EINVAL);
}
"number of channel is %d\n", nchan));
for (i = 0; i < nchan; i++) {
c = &ah->ah_channels[i];
if (index > IEEE80211_CHAN_MAX) {
"arn: arn_setup_channels(): "
"bad hal channel %d (%u/%x) ignored\n",
continue;
}
/* NB: flags are known to be compatible */
if (index < 0) {
/*
* can't handle frequency <2400MHz (negative
* channels) right now
*/
"arn: arn_setup_channels(): "
"hal channel %d (%u/%x) "
"cannot be handled, ignored\n",
continue;
}
/*
* Calculate net80211 flags; most are compatible
* but some need massaging. Note the static turbo
* conversion can be removed once net80211 is updated
* to understand static vs. dynamic turbo.
*/
} else {
/* channels overlap; e.g. 11g and 11b */
}
IEEE80211_C_SHSLOT; /* short slot time */
}
}
return (0);
}
{
case IEEE80211_MODE_11NA:
else
break;
case IEEE80211_MODE_11NG:
else
break;
case IEEE80211_MODE_TURBO_G:
case IEEE80211_MODE_STURBO_A:
case IEEE80211_MODE_TURBO_A:
channel_mode = 0;
break;
case IEEE80211_MODE_11A:
break;
case IEEE80211_MODE_11G:
break;
case IEEE80211_MODE_11B:
break;
case IEEE80211_MODE_FH:
channel_mode = 0;
break;
default:
break;
}
return (channel_mode);
}
/*
* Update internal state after a channel change.
*/
void
{
/*
* Change channels and update the h/w rate map
* if we're switching; e.g. 11a to 11b/g.
*/
switch (mode) {
case IEEE80211_MODE_11A:
break;
case IEEE80211_MODE_11B:
break;
case IEEE80211_MODE_11G:
break;
default:
break;
}
}
/*
* by reseting the chip. To accomplish this we must first cleanup any pending
* DMA, then restart stuff.
*/
static int
{
return (EIO);
int status;
/*
* This is only performed if the channel settings have
* actually changed.
*
* To switch channels clear any pending DMA operations;
* wait long enough for the RX fifo to drain, reset the
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
/*
* XXX: do not flush receive queue here. We don't want
* to flush data frames already in queue because of
* changing channel.
*/
"(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
"unable to reset channel %u (%uMhz) "
"flags 0x%x hal status %u\n",
return (EIO);
}
if (arn_startrecv(sc) != 0) {
arn_problem("arn: arn_set_channel(): "
"unable to restart recv logic\n");
return (EIO);
}
/*
* Change channels and update the h/w rate map
* if we're switching; e.g. 11a to 11b/g.
*/
}
return (0);
}
/*
* This routine performs the periodic noise floor calibration function
* that is used to adjust and optimize the chip performance. This
* takes environmental changes (location, temperature) into account.
* When the task is complete, it reschedules itself depending on the
* appropriate interval that was calculated.
*/
static void
{
/*
* don't calibrate when we're scanning.
* we are most likely not on our home channel.
*/
goto settimer;
/* Long calibration runs independently of short calibration. */
}
/* Short calibration applies only while sc_caldone is FALSE */
"%s: shortcal @%lu\n",
__func__, drv_hztousec));
}
} else {
}
}
/* Verify whether we must check ANI */
}
/* Skip all processing if there's nothing to do. */
/* Call ANI routine if necessary */
if (aniflag)
ah->ah_curchan);
/* Perform calibration if necessary */
if (longcal)
ah->ah_curchan);
"%s: calibrate chan %u/%x nf: %d\n",
} else {
"%s: calibrate chan %u/%x failed\n",
}
}
}
/*
* Set timer interval based on previous results.
* The interval must be the shortest necessary to satisfy ANI,
* short calibration and long calibration.
*/
sc->sc_scan_timer = 0;
}
static void
{
}
sc->sc_cal_timer = 0;
}
static uint_t
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* touch anything. Note this can happen early
* on if the IRQ is shared.
*/
ARN_UNLOCK(sc);
return (DDI_INTR_UNCLAIMED);
}
ARN_UNLOCK(sc);
return (DDI_INTR_UNCLAIMED);
}
/*
* Figure out the reason(s) for the interrupt. Note
* that the hal returns a pseudo-ISR that may include
* bits we haven't explicitly enabled so we mask the
* value to insure we only process bits we requested.
*/
/*
* If there are no status bits set, then this interrupt was not
* for me (should have been caught above).
*/
if (!status) {
ARN_UNLOCK(sc);
return (DDI_INTR_UNCLAIMED);
}
if (status & ATH9K_INT_FATAL) {
/* need a chip reset */
"ATH9K_INT_FATAL\n"));
goto reset;
} else if (status & ATH9K_INT_RXORN) {
/* need a chip reset */
"ATH9K_INT_RXORN\n"));
goto reset;
} else {
if (status & ATH9K_INT_RXEOL) {
/*
* NB: the hardware should re-read the link when
* RXE bit is written, but it doesn't work
* at least on older hardware revs.
*/
"ATH9K_INT_RXEOL\n"));
}
if (status & ATH9K_INT_TXURN) {
/* bump tx trigger level */
"ATH9K_INT_TXURN\n"));
}
/* XXX: optimize this */
if (status & ATH9K_INT_RX) {
"ATH9K_INT_RX\n"));
}
if (status & ATH9K_INT_TX) {
"ATH9K_INT_TX\n"));
DDI_SUCCESS) {
arn_problem("arn: arn_isr(): "
"No memory for tx taskq\n");
}
}
#ifdef ARN_ATH9K_INT_MIB
if (status & ATH9K_INT_MIB) {
/*
* Disable interrupts until we service the MIB
* interrupt; otherwise it will continue to
* fire.
*/
(void) ath9k_hw_set_interrupts(ah, 0);
/*
* Let the hal handle the event. We assume
* it will clear whatever condition caused
* the interrupt.
*/
"ATH9K_INT_MIB\n"));
}
#endif
#ifdef ARN_ATH9K_INT_TIM_TIMER
if (status & ATH9K_INT_TIM_TIMER) {
"ATH9K_INT_TIM_TIMER\n"));
/*
* Clear RxAbort bit so that we can
* receive frames
*/
ath9k_hw_setrxabort(ah, 0);
goto reset;
}
}
#endif
if (status & ATH9K_INT_BMISS) {
"ATH9K_INT_BMISS\n"));
#ifdef ARN_HW_BEACON_MISS_HANDLE
"handle beacon mmiss by H/W mechanism\n"));
arn_problem("arn: arn_isr(): "
"No memory available for bmiss taskq\n");
}
#else
"handle beacon mmiss by S/W mechanism\n"));
#endif /* ARN_HW_BEACON_MISS_HANDLE */
}
ARN_UNLOCK(sc);
#ifdef ARN_ATH9K_INT_CST
/* carrier sense timeout */
if (status & ATH9K_INT_CST) {
"ATH9K_INT_CST\n"));
return (DDI_INTR_CLAIMED);
}
#endif
if (status & ATH9K_INT_SWBA) {
"ATH9K_INT_SWBA\n"));
/* This will occur only in Host-AP or Ad-Hoc mode */
return (DDI_INTR_CLAIMED);
}
}
return (DDI_INTR_CLAIMED);
ARN_UNLOCK(sc);
return (DDI_INTR_CLAIMED);
}
static int
{
int i;
return (i);
}
return (-1);
}
int
{
int status;
int error = 0;
(void) ath9k_hw_set_interrupts(ah, 0);
arn_draintxq(sc, 0);
(void) arn_stoprecv(sc);
"unable to reset hardware; hal status %u\n", status));
}
if (arn_startrecv(sc) != 0)
"unable to start recv logic\n"));
/*
* We may be doing a reset in response to a request
* that changes the channel so update any state that
* might change as a result.
*/
return (error);
}
int
{
int qnum;
switch (queue) {
case WME_AC_VO:
break;
case WME_AC_VI:
break;
case WME_AC_BE:
break;
case WME_AC_BK:
break;
default:
break;
}
return (qnum);
}
static struct {
const char *name;
} ath_mac_bb_names[] = {
{ AR_SREV_VERSION_5416_PCI, "5416" },
{ AR_SREV_VERSION_5416_PCIE, "5418" },
{ AR_SREV_VERSION_9100, "9100" },
{ AR_SREV_VERSION_9160, "9160" },
{ AR_SREV_VERSION_9280, "9280" },
{ AR_SREV_VERSION_9285, "9285" }
};
static struct {
const char *name;
} ath_rf_names[] = {
{ 0, "5133" },
{ AR_RAD5133_SREV_MAJOR, "5133" },
{ AR_RAD5122_SREV_MAJOR, "5122" },
{ AR_RAD2133_SREV_MAJOR, "2133" },
{ AR_RAD2122_SREV_MAJOR, "2122" }
};
/*
*/
static const char *
{
int i;
for (i = 0; i < ARRAY_SIZE(ath_mac_bb_names); i++) {
return (ath_mac_bb_names[i].name);
}
}
return ("????");
}
/*
* Return the RF name. "????" is returned if the RF is unknown.
*/
static const char *
{
int i;
for (i = 0; i < ARRAY_SIZE(ath_rf_names); i++) {
return (ath_rf_names[i].name);
}
}
return ("????");
}
static void
{
sc->sc_scan_timer = 0;
}
}
static void
{
}
sc->sc_scan_timer = 0;
}
static int32_t
{
int pos;
/* Should set up & init LED here */
return (0);
if (nstate != IEEE80211_S_SCAN)
if (nstate != IEEE80211_S_RUN)
/* Should set LED here */
if (nstate == IEEE80211_S_INIT) {
/*
* Disable interrupts.
*/
(void) ath9k_hw_set_interrupts
#ifdef ARN_IBSS
}
#endif
ARN_UNLOCK(sc);
goto done;
}
if (pos == -1) {
"%s: Invalid channel\n", __func__));
ARN_UNLOCK(sc);
goto bad;
}
} else
arn_problem("arn_newstate(): channel == NULL");
ARN_UNLOCK(sc);
goto bad;
}
if (error != 0) {
if (nstate != IEEE80211_S_SCAN) {
ARN_UNLOCK(sc);
goto bad;
}
}
/*
* Get the receive filter according to the
* operating mode and state
*/
if (nstate == IEEE80211_S_SCAN)
else
else
/* Check for WLAN_CAPABILITY_PRIVACY ? */
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
bssid);
}
}
if (nstate == IEEE80211_S_RUN) {
#ifdef ARN_IBSS
case IEEE80211_M_IBSS:
/*
* Allocate and setup the beacon frame.
* Stop any previous beacon DMA.
*/
if (error != 0) {
ARN_UNLOCK(sc);
goto bad;
}
/*
* If joining an adhoc network defer beacon timer
* configuration to the next beacon frame so we
* have a current TSF to use. Otherwise we're
*/
} else {
}
break;
#endif /* ARN_IBSS */
case IEEE80211_M_STA:
if (ostate != IEEE80211_S_RUN) {
/*
* Defer beacon timer configuration to the next
* beacon frame so we have a current TSF to use.
* Any TSF collected when scanning is likely old
*/
#ifdef ARN_IBSS
#else
/* Configure the beacon and sleep timers. */
/* Reset rssi stats */
/* end */
#endif /* ARN_IBSS */
}
break;
default:
break;
}
} else {
}
/*
* Reset the rate control state.
*/
ARN_UNLOCK(sc);
done:
/*
* Invoke the parent method to complete the work.
*/
/*
* Finally, start any timers.
*/
if (nstate == IEEE80211_S_RUN) {
/* ASSERT(sc->sc_scan_timer == 0); */
if (sc->sc_scan_timer != 0) {
sc->sc_scan_timer = 0;
}
}
bad:
return (error);
}
static void
{
int ntimer = 0;
ic->ic_watchdog_timer = 0;
ARN_UNLOCK(sc);
return;
}
/*
* Start the background rate control thread if we
* are not configured to use a fixed xmit rate.
*/
#ifdef ARN_LEGACY_RC
else
arn_rate_ctl, sc);
}
#endif /* ARN_LEGACY_RC */
#ifdef ARN_HW_BEACON_MISS_HANDLE
/* nothing to do here */
#else
/* currently set 10 seconds as beacon miss threshold */
"Beacon missed for 10 seconds, run"
"ieee80211_new_state(ic, IEEE80211_S_INIT, -1)\n"));
ARN_UNLOCK(sc);
return;
}
#endif /* ARN_HW_BEACON_MISS_HANDLE */
ntimer = 1;
}
ARN_UNLOCK(sc);
if (ntimer != 0)
}
/* ARGSUSED */
static struct ieee80211_node *
{
#ifdef ARN_TX_AGGREGATION
#endif
/* legacy rate control */
#ifdef ARN_LEGACY_RC
#endif
#ifdef ARN_TX_AGGREGATION
}
#endif /* ARN_TX_AGGREGATION */
}
static void
{
int32_t i;
#ifdef ARN_TX_AGGREGATION
#endif /* TX_AGGREGATION */
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i)) {
}
}
}
}
}
/*
*/
static uint16_t
{
if (b == 0xff)
continue;
keyix++, b >>= 1) {
/* full pair unavailable */
continue;
}
"arn_key_alloc_pair(): key pair %u,%u\n",
return (1);
}
}
" out of pair space\n"));
return (0);
}
/*
*/
static int
{
if (b != 0xff) {
/*
* One or more slots in this byte are free.
*/
while (b & 1) {
keyix++;
b >>= 1;
}
/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
/* full pair unavailable */
/* no slots were appropriate, advance */
continue;
}
goto again;
}
"arn_key_alloc_2pair(): key pair %u,%u %u,%u\n",
return (1);
}
}
" out of pair space\n"));
return (0);
}
/*
* Allocate a single key cache slot.
*/
static int
{
/* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
if (b != 0xff) {
/*
* One or more slots are free.
*/
while (b & 1)
keyix++, b >>= 1;
"key %u\n", keyix));
return (1);
}
}
return (0);
}
/*
* Allocate one or more key cache slots for a unicast key. The
* key itself is needed only to identify the cipher. For hardware
* TKIP with split cipher+MIC keys we allocate two key cache slot
* pairs so that we can setup separate TX and RX MIC keys. Note
* that the MIC key for a TKIP key at slot i is assumed by the
* hardware to be at slot i+64. This limits TKIP keys to the first
* 64 entries.
*/
/* ARGSUSED */
int
{
/*
* We allocate two pair for TKIP when using the h/w to do
* the MIC. For everything else, including software crypto,
* we allocate a single entry. Note that s/w crypto requires
* a pass-through slot on the 5211 and 5212. The 5210 does
* not support pass-through cache entries and we map all
* those requests to slot 0.
*/
if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
if (sc->sc_splitmic)
else
} else {
}
}
/*
* Delete an entry in the key cache allocated by ath_key_alloc.
*/
int
{
/*
*/
if (keyix >= IEEE80211_WEP_NKID) {
/*
* Don't touch keymap entries for global keys so
* they are never considered for dynamic allocation.
*/
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
/*
* If splitmic is true +64 is TX key MIC,
* else +64 is RX key + RX key MIC.
*/
if (sc->sc_splitmic) {
/* Rx key */
/* RX key MIC */
}
}
}
return (1);
}
/*
* Set a TKIP key into the hardware. This handles the
* potential distribution of key state to multiple key
* cache slots for TKIP.
*/
static int
{
/* Group key installation */
}
if (!sc->sc_splitmic) {
/*
* data key goes at first index,
* the hal handles the MIC keys at index+64.
*/
}
/*
* TX key goes at first index, RX key at +32.
* The hal handles the MIC keys at index+64.
*/
B_FALSE))) {
/* Txmic entry failed. No need to proceed further */
"%s Setting TX MIC Key Failed\n", __func__));
return (0);
}
/* XXX delete tx key on failure? */
}
int
{
/* cipher table */
ATH9K_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */
ATH9K_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */
ATH9K_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */
ATH9K_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */
ATH9K_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */
ATH9K_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */
};
/*
* Software crypto uses a "clear key" so non-crypto
* state kept in the key cache are maintainedd so that
* rx frames have an entry to match.
*/
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
} else {
}
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
} else {
}
}
/*
*/
void
{
if (onoff)
else
}
static int
{
if (pos == -1) {
"%s: Invalid channel\n", __func__));
goto error;
}
} else {
}
/* Reset SERDES registers */
/*
* The basic interface to setting the hardware in a good
* state is ``reset''. On return the hardware is known to
* be powered up and with interrupts disabled. This must
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
"%s: unable to reset hardware; hal status %u "
goto error;
}
/*
* This is needed only to setup initial state
* but it's best done after a reset.
*/
/*
* Setup the hardware after reset:
* The receive engine is set going.
* Frame transmit is handled entirely
* in the frame output path; there's nothing to do
* here except setup the interrupt mask.
*/
if (arn_startrecv(sc) != 0) {
"%s: unable to start recv logic\n", __func__));
goto error;
}
/* Setup our intr mask. */
#ifdef ARN_ATH9K_HW_CAP_GTT
#endif
#ifdef ARN_ATH9K_HW_CAP_GTT
#endif
/*
* Enable MIB interrupts when there are hardware phy counters.
* Note we only do this (at the moment) for station mode.
*/
#ifdef ARN_ATH9K_INT_MIB
#endif
/*
* Some hardware processes the TIM IE and fires an
* interrupt when the TIM bit is set. For hardware
* that does, if not overridden by configuration,
* enable the TIM interrupt when operating as station.
*/
#ifdef ARN_ATH9K_INT_TIM
#endif
"%s: current mode after arn_setcurmode is %d\n",
/* Disable BMISS interrupt when we're not associated */
return (0);
return (error);
}
static void
{
if (!sc->sc_isrunning)
return;
/*
* Shutdown the hardware and driver
* Note that some of this work is not possible if the
* hardware is gone (invalid).
*/
ARN_UNLOCK(sc);
/*
* make sure h/w will not generate any interrupt
* before setting the invalid flag.
*/
(void) ath9k_hw_set_interrupts(ah, 0);
arn_draintxq(sc, 0);
(void) arn_stoprecv(sc);
(void) ath9k_hw_phy_disable(ah);
} else {
}
sc->sc_isrunning = 0;
}
/*
* MAC callback functions
*/
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
1000000ull;
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OERRORS:
case WIFI_STAT_TX_FAILED:
break;
case WIFI_STAT_TX_RETRANS:
break;
case WIFI_STAT_FCS_ERRORS:
break;
case WIFI_STAT_WEP_ERRORS:
break;
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
case WIFI_STAT_RX_DUPS:
ARN_UNLOCK(sc);
default:
ARN_UNLOCK(sc);
return (ENOTSUP);
}
ARN_UNLOCK(sc);
return (0);
}
int
{
int err = 0;
/*
* Stop anything previously setup. This is safe
* whether this is the first time through or not.
*/
ARN_UNLOCK(sc);
return (err);
}
/* H/W is reday now */
ARN_UNLOCK(sc);
return (0);
}
static void
{
/* disable HAL and put h/w to sleep */
/* XXX: hardware will not be ready in suspend state */
ARN_UNLOCK(sc);
}
static int
{
if (on)
else
ARN_UNLOCK(sc);
return (0);
}
static int
{
/* calculate XOR of eight 6bit values */
pos &= 0x3f;
if (add) { /* enable multicast */
} else { /* disable multicast */
}
ARN_UNLOCK(sc);
return (0);
}
static int
{
"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
ARN_UNLOCK(sc);
return (0);
}
static mblk_t *
{
int error = 0;
/*
* No data frames go out unless we're associated; this
* should not happen as the 802.11 layer does not enable
* the xmit queue until we enter the RUN state.
*/
return (NULL);
}
if (error != 0) {
break;
} else {
return (NULL);
}
}
}
return (mp);
}
static void
{
ARN_UNLOCK(sc);
(void) arn_m_start(sc);
IEEE80211_S_SCAN, -1);
}
}
ARN_UNLOCK(sc);
}
static int
{
int err;
ARN_UNLOCK(sc);
(void) arn_m_start(sc);
IEEE80211_S_SCAN, -1);
}
err = 0;
}
ARN_UNLOCK(sc);
return (err);
}
/* ARGSUSED */
static int
{
int err = 0;
return (err);
}
static void
{
}
/* return bus cachesize in 4B word units */
static void
{
/*
* Cache line size is used to size and align various
* structures used to communicate with the hardware.
*/
if (csz == 0) {
/*
* We must have this setup properly for rx buffer
* DMA to work so force a reasonable value here if it
* comes up zero.
*/
csz);
}
}
static int
{
/*
* Enable memory mapping and bus mastering
*/
if ((command & PCI_COMM_MAE) == 0) {
arn_problem("arn: arn_pci_setup(): "
"failed to enable memory mapping\n");
return (EIO);
}
if ((command & PCI_COMM_ME) == 0) {
arn_problem("arn: arn_pci_setup(): "
"failed to enable bus mastering\n");
return (EIO);
}
"set command reg to 0x%x \n", command));
return (0);
}
static void
{
}
static void
{
/* Todo: IEEE80211_HTCAP_SMPS */
/* set up supported mcs set */
if (rx_streams >= 2)
}
/* xxx should be used for ht rate set negotiating ? */
static void
{
int i, j;
(void) memset(&ieee80211_rateset_11n, 0,
sizeof (ieee80211_rateset_11n));
for (i = 0; i < 10; i++) {
for (j = 0; j < 8; j++) {
if (ht_rs[i] & (1 << j)) {
mcs_idx = i * 8 + j;
if (mcs_idx >= IEEE80211_HTRATE_MAXSIZE) {
break;
}
mcs_count++;
}
}
}
"MCS rate set supported by this station is as follows:\n"));
for (i = 0; i < ieee80211_rateset_11n.rs_nrates; i++) {
i, ieee80211_rateset_11n.rs_rates[i]));
}
}
/*
* Update WME parameters for a transmit queue.
*/
static int
{
/*
* TXQ_FLAG_TXOKINT_ENABLE = 0x0001
* TXQ_FLAG_TXERRINT_ENABLE = 0x0001
* TXQ_FLAG_TXDESCINT_ENABLE = 0x0002
* TXQ_FLAG_TXEOLINT_ENABLE = 0x0004
* TXQ_FLAG_TXURNINT_ENABLE = 0x0008
* TXQ_FLAG_BACKOFF_DISABLE = 0x0010
* TXQ_FLAG_COMPRESSION_ENABLE = 0x0020
* TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040
* TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080
*/
/* xxx should update these flags here? */
#if 0
#endif
qi.tqi_readyTime = 0;
"%s:"
"Q%u"
"qflags 0x%x"
"aifs %u"
"cwmin %u"
"cwmax %u"
"burstTime %u\n",
qi.tqi_burstTime));
arn_problem("unable to update hardware queue "
"parameters for %s traffic!\n",
return (0);
} else {
/* push to H/W */
return (1);
}
}
/* Update WME parameters */
static int
{
/* updateing */
}
/*
* hard code chainmask to 1x1, for 11n association, use
* the chainmask configuration.
*/
void
{
if (is_ht) {
} else {
}
"tx_chainmask = %d, rx_chainmask = %d\n",
}
static int
{
"failed to get soft state\n"));
return (DDI_FAILURE);
}
/*
* Set up config space command register(s). Refuse
* to resume on failure.
*/
if (arn_pci_setup(sc) != 0) {
"ath_pci_setup() failed\n"));
ARN_UNLOCK(sc);
return (DDI_FAILURE);
}
ARN_UNLOCK(sc);
return (ret);
}
static int
{
int instance;
int status;
uint32_t i;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (arn_resume(devinfo));
default:
return (DDI_FAILURE);
}
"%s: Unable to alloc softstate\n", __func__));
return (DDI_FAILURE);
}
#ifdef ARN_IBSS
#endif
if (err != DDI_SUCCESS) {
"pci_config_setup() failed"));
goto attach_fail0;
}
if (arn_pci_setup(sc) != 0)
goto attach_fail1;
/* Cache line size set up */
"device id 0x%x, cache size %d\n",
if ((val & 0x0000ff00) != 0)
if (err != DDI_SUCCESS) {
"ddi_regs_map_setup() failed"));
goto attach_fail1;
}
"unable to attach hw: H/W status %u\n",
status));
goto attach_fail2;
}
/* Get the hardware key cache size. */
"Warning, using only %u entries in %u key cache\n",
}
/*
* Reset the key cache since some parts do not
* reset the contents on initial power up.
*/
/*
* Mark key cache slots associated with global keys
* as in use. If we knew TKIP was not to be used we
* could leave the +32, +64, and +32+64 slots free.
* XXX only for splitmic.
*/
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
}
/* Collect the channel list using the default country code */
"ERR:arn_setup_channels\n"));
goto attach_fail3;
}
/* default to STA mode */
/* Setup rate tables */
/* Setup current mode here */
/* 802.11g features */
if (sc->sc_have11g)
IEEE80211_C_SHSLOT; /* short slot time */
/* Temp workaround */
if (err != DDI_SUCCESS) {
"failed to allocate descriptors: %d\n", err));
goto attach_fail3;
}
TASKQ_DEFAULTPRI, 0)) == NULL) {
"ERR:ddi_taskq_create\n"));
goto attach_fail4;
}
/*
* Allocate hardware transmit queues: one queue for
* beacon frames and one data queue for each QoS
* priority. Note that the hal handles reseting
* these queues at the needed time.
*/
#ifdef ARN_IBSS
"unable to setup a beacon xmit queue\n"));
goto attach_fail4;
}
#endif
#ifdef ARN_HOSTAP
"unable to setup CAB xmit queue\n"));
goto attach_fail4;
}
#endif
/* Setup data queues */
/* NB: ensure BK queue is the lowest priority h/w queue */
"unable to setup xmit queue for BK traffic\n"));
goto attach_fail4;
}
"unable to setup xmit queue for BE traffic\n"));
goto attach_fail4;
}
"unable to setup xmit queue for VI traffic\n"));
goto attach_fail4;
}
"unable to setup xmit queue for VO traffic\n"));
goto attach_fail4;
}
/*
* Initializes the noise floor to a reasonable default value.
* Later on this will be updated during ANI processing.
*/
ATH9K_CIPHER_TKIP, NULL)) {
/*
* Whether we should enable h/w TKIP MIC.
* XXX: if we don't support WME TKIP MIC, then we wouldn't
* report WMM capable, so it's always safe to turn on
* TKIP MIC in this case.
*/
0, 1, NULL);
}
/* Get cipher releated capability information */
/*
* Check whether the separate key cache entries
* are required to handle both tx+rx MIC keys.
* With split mic keys the number of stations is limited
* to 27 otherwise 59.
*/
ATH9K_CIPHER_TKIP, NULL) &&
ATH9K_CIPHER_MIC, NULL) &&
0, NULL))
/* turn on mcast key search if possible */
1, NULL);
/* 11n Capabilities */
}
"tx_chainmask = %d, rx_chainmask = %d\n",
/* arn_update_chainmask(sc); */
}
/* set default value to short slot time */
/* initialize beacon slots */
/* Save MISC configurations */
#ifdef ARN_TX_AGGREGATION
#endif
}
/* Header padding requested by driver */
#if 0
#endif
/* different instance has different WPA door */
// sc->sc_send_action = ic->ic_send_action;
// ic->ic_send_action = arn_ampdu_send_action;
}
/* Override 80211 default routines */
#ifdef ARN_IBSS
#endif
/*
* initialize default tx key
*/
ic->ic_def_txkey = 0;
sc->sc_rx_pend = 0;
if (err != DDI_SUCCESS) {
"ddi_add_softintr() failed....\n"));
goto attach_fail5;
}
!= DDI_SUCCESS) {
"Can not get iblock cookie for INT\n"));
goto attach_fail6;
}
"Can not set intr for ARN driver\n"));
goto attach_fail6;
}
/*
* Provide initial settings for the WiFi plugin; whenever this
* information changes, we need to call mac_plugindata_update()
*/
"IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid)"
"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
"MAC version mismatch\n"));
goto attach_fail7;
}
if (err != 0) {
"mac_register err %x\n", err));
goto attach_fail7;
}
/* Create minor node of type DDI_NT_NET_WIFI */
if (err != DDI_SUCCESS)
"Create minor node failed - %d\n", err));
/* Notify link is down now */
"AR%s RF Rev:%x: mem=0x%lx\n",
/* XXX: hardware will not be ready until arn_open() being called */
sc->sc_isrunning = 0;
return (DDI_SUCCESS);
(void) ieee80211_detach(ic);
/* cleanup tx queues */
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i)) {
/* arn_tx_cleanupq(asc, &asc->sc_txq[i]); */
}
}
#ifdef ARN_IBSS
#endif
return (DDI_FAILURE);
}
/*
*/
static int
{
ARN_UNLOCK(sc);
return (DDI_SUCCESS);
}
static int32_t
{
int i;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (arn_suspend(sc));
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/* disable interrupts */
/*
* Unregister from the MAC layer subsystem
*/
/* free intterrupt resources */
/*
* NB: the order of these is important:
* o call the 802.11 layer before detaching the hal to
* insure callbacks into the driver to delete global
* key cache entries can be handled
* o reclaim the tx queue data structures after calling
* the 802.11 layer as we'll get called back to reclaim
* node state and potentially want to use them
* o to cleanup the tx queues the hal is called, so detach
* it last
*/
/* cleanup tx queues */
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i)) {
}
}
/* free io handle */
/* destroy locks */
#ifdef ARN_IBSS
#endif
return (DDI_SUCCESS);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int32_t
{
int i;
return (DDI_FAILURE);
/*
* Disable interrupts
*/
(void) ath9k_hw_set_interrupts(ah, 0);
/*
* Disable TX HW
*/
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i))
}
/*
* Disable RX HW
*/
ath9k_hw_setrxfilter(ah, 0);
(void) ath9k_hw_stopdmarecv(ah);
drv_usecwait(3000);
/*
* Power down HW
*/
(void) ath9k_hw_phy_disable(ah);
return (DDI_SUCCESS);
}
&mod_driverops, /* Type of module. This one is a driver */
"Atheros 9000 series driver", /* short description */
&arn_dev_ops /* driver specific ops */
};
};
int
{
}
int
_init(void)
{
int status;
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}