mwl.c revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
* Copyright (c) 2007-2008 Marvell Semiconductor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
/*
* Driver for the Marvell 88W8363 Wireless LAN controller.
*/
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#include <inet/wifi_ioctl.h>
#include "mwl_var.h"
static struct modldrv mwl_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"Marvell 88W8363 WiFi driver v1.1", /* short description */
&mwl_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
};
static void *mwl_soft_state_p = NULL;
static int mwl_m_start(void *);
static void mwl_m_stop(void *);
static int mwl_m_promisc(void *, boolean_t);
static int mwl_m_unicst(void *, const uint8_t *);
void *wldp_buf);
static void mwl_m_propinfo(void *, const char *, mac_prop_id_t,
static mac_callbacks_t mwl_m_callbacks = {
NULL,
NULL,
NULL,
NULL,
};
#define MWL_DBG_ATTACH (1 << 0)
#ifdef DEBUG
#define MWL_DBG \
#else
#define MWL_DBG
#endif
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t mwl_reg_accattr = {
};
static ddi_device_acc_attr_t mwl_cmdbuf_accattr = {
};
/*
* DMA access attributes for descriptors and bufs: NOT to be byte swapped.
*/
static ddi_device_acc_attr_t mwl_desc_accattr = {
};
static ddi_device_acc_attr_t mwl_buf_accattr = {
};
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t mwl_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0xFFFFFFFF, /* dma_attr_addr_hi */
0x00000000FFFFFFFFull, /* dma_attr_count_max */
0x0000000000000001ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x000000000000FFFFull, /* dma_attr_maxxfer */
0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x00000001, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* Supported rates for 802.11a/b/g modes (in 500Kbps unit).
*/
static const struct ieee80211_rateset mwl_rateset_11b =
{ 4, { 2, 4, 11, 22 } };
static const struct ieee80211_rateset mwl_rateset_11g =
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
struct dma_area *);
static void mwl_free_dma_mem(struct dma_area *);
static int mwl_alloc_cmdbuf(struct mwl_softc *);
static void mwl_free_cmdbuf(struct mwl_softc *);
static int mwl_alloc_rx_ring(struct mwl_softc *, int);
static void mwl_free_rx_ring(struct mwl_softc *);
int);
static int mwl_setupdma(struct mwl_softc *);
static int mwl_tx_setup(struct mwl_softc *, int, int);
static int mwl_setup_txq(struct mwl_softc *);
static int mwl_fwload(struct mwl_softc *, void *);
static void mwlFwReset(struct mwl_softc *);
static void mwlPokeSdramController(struct mwl_softc *, int);
static void mwlTriggerPciCmd(struct mwl_softc *);
static void mwlSendCmd(struct mwl_softc *);
static int mwlExecuteCmd(struct mwl_softc *, unsigned short);
static void dumpresult(struct mwl_softc *, int);
static int mwlResetHalState(struct mwl_softc *);
static int mwlGetPwrCalTable(struct mwl_softc *);
static int mwlGetPwrCalTable(struct mwl_softc *);
static void dumpcaldata(const char *, const uint8_t *, int);
static void setmaxtxpow(struct mwl_hal_channel *, int, int);
static const char *
mwlcmdname(int);
static int mwl_gethwspecs(struct mwl_softc *);
static int mwl_getchannels(struct mwl_softc *);
static void getchannels(struct mwl_softc *, int, int *,
struct mwl_channel *);
static void addchannels(struct mwl_channel *, int, int *,
const MWL_HAL_CHANNELINFO *, int);
static void addht40channels(struct mwl_channel *, int, int *,
const MWL_HAL_CHANNELINFO *, int);
static const struct mwl_channel *
findchannel(const struct mwl_channel *, int,
int, int);
static void addchan(struct mwl_channel *, int, int, int, int);
static int mwl_setcurchanrates(struct mwl_softc *);
const struct ieee80211_rateset *
mwl_get_suprates(struct ieee80211com *,
const struct mwl_channel *);
static const struct mwl_hal_channel *
findhalchannel(const struct mwl_softc *,
const MWL_HAL_CHANNEL *);
enum ieee80211_phymode
mwl_chan2mode(const struct mwl_channel *);
static int mwl_map2regioncode(const struct mwl_regdomain *);
static int mwl_startrecv(struct mwl_softc *);
static int mwl_mode_init(struct mwl_softc *);
static int mwl_hal_sethwdma(struct mwl_softc *,
const struct mwl_hal_txrxdma *);
static int mwl_hal_getchannelinfo(struct mwl_softc *, int, int,
const MWL_HAL_CHANNELINFO **);
static int mwl_hal_setwmm(struct mwl_softc *, int);
uint8_t);
const MWL_HAL_TXRATE *);
static int mwl_hal_settxrate_auto(struct mwl_softc *,
const MWL_HAL_TXRATE *);
static int mwl_hal_setoptimizationlevel(struct mwl_softc *, int);
static int mwl_hal_setregioncode(struct mwl_softc *, int);
uint16_t);
static int mwl_setrates(struct ieee80211com *);
static int mwl_hal_setrtsthreshold(struct mwl_softc *, int);
static int mwl_hal_setpromisc(struct mwl_softc *, int);
static int mwl_hal_start(struct mwl_softc *);
static int mwl_hal_setinframode(struct mwl_softc *);
static int mwl_hal_stop(struct mwl_softc *);
static struct ieee80211_node *
mwl_node_alloc(struct ieee80211com *);
static void mwl_node_free(struct ieee80211_node *);
static int mwl_key_alloc(struct ieee80211com *,
const struct ieee80211_key *,
ieee80211_keyix *, ieee80211_keyix *);
static int mwl_key_delete(struct ieee80211com *,
const struct ieee80211_key *);
static void mwl_setglobalkeys(struct ieee80211com *c);
static void mwl_hal_txstart(struct mwl_softc *, int);
static void mwl_next_scan(void *);
static MWL_HAL_PEERINFO *
static void mwl_tx_intr(struct mwl_softc *);
static void mwl_rx_intr(struct mwl_softc *);
static int mwl_resume(struct mwl_softc *);
#ifdef DEBUG
static void
{
if (dbg_flags & mwl_dbg_flags) {
}
}
#endif
/*
* Allocate an DMA memory and a DMA handle for accessing it
*/
static int
{
int err;
/*
* Allocate handle
*/
if (err != DDI_SUCCESS) {
"failed to alloc handle\n");
goto fail1;
}
/*
* Allocate memory
*/
if (err != DDI_SUCCESS) {
"failed to alloc mem\n");
goto fail2;
}
/*
* Bind the two together
*/
if (err != DDI_DMA_MAPPED) {
"failed to bind handle\n");
goto fail3;
}
"failed to alloc cookies\n");
goto fail4;
}
return (DDI_SUCCESS);
return (err);
}
static void
{
}
}
}
static int
{
int err;
&sc->sc_cmd_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
}
static int
{
struct mwl_rx_ring *ring;
struct mwl_rxdesc *ds;
count * sizeof (struct mwl_rxdesc),
&ring->rxdesc_dma);
if (err) {
"alloc tx ring failed, size %d\n",
return (DDI_FAILURE);
}
"could not alloc rx ring data buffer\n");
return (DDI_FAILURE);
}
/*
* Pre-allocate Rx buffers and populate Rx ring.
*/
for (i = 0; i < count; i++) {
/* alloc DMA memory */
}
0,
return (0);
}
static void
{
struct mwl_rx_ring *ring;
int i;
}
}
}
}
static int
int count)
{
struct mwl_txdesc *ds;
&ring->txdesc_dma);
if (err) {
"alloc tx ring failed, size %d\n",
return (DDI_FAILURE);
}
"could not alloc tx ring data buffer\n");
return (DDI_FAILURE);
}
for (i = 0; i < count; i++) {
/* alloc DMA memory */
}
0,
return (0);
}
/* ARGSUSED */
static void
{
int i;
}
}
}
}
/*
* and related state. This cmd must be done immediately after a
* mwl_hal_gethwspecs call or the f/w will lockup.
*/
static int
{
int retval;
/*
* pCmd->Flags = LE_32(SET_HW_SPEC_HOSTFORM_BEACON
* #ifdef MWL_HOST_PS_SUPPORT
* | SET_HW_SPEC_HOST_POWERSAVE
* #endif
* | SET_HW_SPEC_HOSTFORM_PROBERESP);
*/
/* disable multi-bss operation for A1-A4 parts */
if (retval == 0) {
else
}
return (retval);
}
/*
* writes below are for compatibility with older firmware.
* For current firmware we send this information with a
* cmd block via mwl_hal_sethwdma.
*/
static int
{
int i, err;
for (i = 0; i < MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES; i++) {
}
if (err != 0) {
/* XXX */
}
return (err);
}
/* ARGSUSED */
static void
{
struct mwl_txdesc *ds;
int i;
for (i = 0; i < MWL_TX_RING_COUNT - 1; i++) {
bf++;
ds++;
}
}
/*
* Setup a hardware data transmit queue for the specified
* access control. We record the mapping from ac's
* to h/w queues for use by mwl_tx_start.
*/
static int
{
#define N(a) (sizeof (a)/sizeof (a[0]))
struct mwl_tx_ring *txring;
"AC %u out of range, max %u!\n",
return (0);
}
if (mvtype >= MWL_NUM_TX_QUEUES) {
"mvtype %u out of range, max %u!\n",
return (0);
}
return (1);
#undef N
}
static int
{
int err = 0;
/* NB: insure BK queue is the lowest priority h/w queue */
"unable to setup xmit queue for %s traffic!\n",
return (err);
}
/*
* Not enough hardware tx queues to properly do WME;
* just punt and assign them all to the same h/w queue.
* We could do a better job of this if, for example,
* we allocate queues when we switch from station to
* AP mode.
*/
}
return (err);
}
/*
* find mwl firmware module's "_start" "_end" symbols
* and get its size.
*/
static int
{
char start_sym[64];
char end_sym[64];
char *p, *end;
int rv;
size_t n;
return (-1);
}
return (-1);
}
*start = p;
*len = n;
return (0);
}
static void
{
"device not present!\n");
return;
}
}
static void
{
/* Set up sdram controller for superflyv2 */
}
static void
{
0,
}
static int
{
int i;
for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
return (1);
}
return (0);
}
/*
* Firmware block xmit when talking to the boot-rom.
*/
static int
{
/* XXX 2000 vs 200 */
return (1);
}
"timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
return (0);
}
/*
* Firmware block xmit when talking to the 1st-stage loader.
*/
static int
{
return (1);
}
"timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
return (0);
}
/* ARGSUSED */
static int
{
char *fwname = "mwlfw";
char *fwbootname = "mwlboot";
char *fwbinname = "mw88W8363fw";
char *fwboot_index, *fw_index;
/* XXX get from firmware header */
"module %s not found\n", fwname);
err = -1;
goto bad2;
}
if (err != 0) {
"could not get boot firmware\n");
err = -1;
goto bad2;
}
if (err != 0) {
"could not get firmware\n");
err = -1;
goto bad2;
}
"failed to alloc boot firmware memory\n");
err = -1;
goto bad2;
}
"failed to alloc firmware memory\n");
err = -1;
goto bad2;
}
(void) ddi_modclose(modfw);
if (fw_size < 4) {
"could not load firmware image %s\n",
fwname);
goto bad2;
}
/*
* 2-stage load, get the boot firmware.
*/
"could not load firmware image %s\n",
goto bad2;
}
} else
mwlFwReset(sc);
if (sc->sc_SDRAMSIZE_Addr != 0) {
/* Set up sdram controller for superflyv2 */
}
"load %s firmware image (%u bytes)\n",
/*
* Do 2-stage load. The 1st stage loader is setup
* with the bootrom loader then we load the real
* image using a different handshake. With this
* mechanism the firmware is segmented into chunks
* that have a CRC. If a chunk is incorrect we'll
* be told to retransmit.
*/
/* XXX assumes hlpimage fits in a block */
/* NB: zero size block indicates download is finished */
goto bad;
}
if (sc->sc_SDRAMSIZE_Addr != 0) {
/* Set up sdram controller for superflyv2 */
}
if (blocksize == 0) /* download complete */
break;
if (blocksize > 0x00000c00) {
goto bad;
}
if ((blocksize & 0x1) == 0) {
/* block successfully downloaded, advance */
ntries = 0;
} else {
if (++ntries > 2) {
/*
* Guard against f/w telling us to
* retry infinitely.
*/
goto bad;
}
blocksize &= ~0x1;
}
/* XXX this should not happen, what to do? */
}
goto bad;
}
}
} else {
if (nbytes > FW_DOWNLOAD_BLOCK_SIZE)
nbytes)) {
goto bad;
}
}
}
/*
* Wait for firmware to startup; we monitor the
* INT_CODE register waiting for a signature to
* written back indicating it's ready to go.
*/
/*
* XXX WAR for mfg fw download
*/
if (OpMode != HostCmd_STA_MODE)
for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
return (mwlResetHalState(sc));
}
}
"firmware download timeout\n");
return (ETIMEDOUT);
bad:
mwlFwReset(sc);
bad2:
(void) ddi_modclose(modfw);
return (err);
}
/*
* Low level firmware cmd block handshake support.
*/
static void
{
0,
}
static int
{
"device not present!\n");
return (EIO);
}
mwlSendCmd(sc);
return (ETIMEDOUT);
}
0,
if (mwl_dbg_flags & MWL_DBG_CMD)
return (0);
}
static int
{
#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000
int i;
for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) {
return (1);
}
return (0);
}
static const char *
mwlcmdname(int cmd)
{
static char buf[12];
#define CMD(x) case HostCmd_CMD_##x: return #x
switch (cmd) {
}
return (buf);
}
static void
{
int len;
#ifdef MWL_MBSS_SUPPORT
"Cmd %s Length %d SeqNum %d MacId %d",
#else
"Cmd %s Length %d SeqNum %d",
#endif
if (showresult) {
const char *results[] =
{ "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY",
"PARTIAL_DATA" };
if (result <= HostCmd_RESULT_PARTIAL_DATA)
else
"Result %d", result);
}
}
static int
{
int retval;
if (retval == 0 &&
return (retval);
}
/*
* Construct channel info for 2.4GHz channels from cal data.
*/
static void
{
int i, j;
j = 0;
for (i = 0; i < len; i += 4) {
j++;
}
}
/*
* Construct channel info for 5GHz channels from cal data.
*/
static void
{
int i, j, f, l, h;
l = 32000;
h = 0;
j = 0;
for (i = 0; i < len; i += 4) {
struct mwl_hal_channel *hc;
if (table[i] == 0)
continue;
if (f < l)
l = f;
if (f > h)
h = f;
j++;
}
}
/*
* Calculate the max tx power from the channel's cal data.
*/
static void
{
for (i++; i < maxix; i++)
}
static uint16_t
{
if (chan == 14)
return (2484);
if (chan < 14)
}
static void
{
int i;
for (i = 0; i < n; i += 4)
}
static int
{
int len;
/* NB: we hold the lock so it's ok to use cmdbuf */
if (len > PWTAGETRATETABLE20M)
}
if (len > PWTAGETRATETABLE40M)
}
if (len > PWTAGETRATETABLE20M_5G)
}
if (len > PWTAGETRATETABLE40M_5G)
}
return (0);
}
/*
* Reset internal state after a firmware download.
*/
static int
{
int err = 0;
/*
* Fetch cal data for later use.
* XXX may want to fetch other stuff too.
*/
/* XXX check return */
return (err);
}
static void
{
c->ic_minpower = 0;
}
static const struct mwl_channel *
{
const struct mwl_channel *c;
int i;
for (i = 0; i < nchans; i++) {
c = &chans[i];
return (c);
}
return (NULL);
}
static void
{
struct mwl_channel *c;
const struct mwl_channel *extc;
const struct mwl_hal_channel *hc;
int i;
flags &= ~IEEE80211_CHAN_HT;
/*
* Each entry defines an HT40 channel pair; find the
* extension channel above and the insert the pair.
*/
break;
c++, (*nchans)++;
break;
c++, (*nchans)++;
}
}
}
static void
{
struct mwl_channel *c;
int i;
const struct mwl_hal_channel *hc;
break;
c++, (*nchans)++;
/* g channel have a separate b-only entry */
break;
c[0] = c[-1];
c++, (*nchans)++;
}
if (flags == IEEE80211_CHAN_HTG) {
/* HT g channel have a separate g-only entry */
break;
c[0] = c[-1];
c[0].ic_flags &= ~IEEE80211_CHAN_HT;
c++, (*nchans)++;
}
if (flags == IEEE80211_CHAN_HTA) {
/* HT a channel have a separate a-only entry */
break;
c[0] = c[-1];
c[0].ic_flags &= ~IEEE80211_CHAN_HT;
c++, (*nchans)++;
}
}
}
static int
const MWL_HAL_CHANNELINFO **ci)
{
switch (band) {
case MWL_FREQ_BAND_2DOT4GHZ:
break;
case MWL_FREQ_BAND_5GHZ:
break;
default:
return (EINVAL);
}
}
static void
struct mwl_channel chans[])
{
const MWL_HAL_CHANNELINFO *ci;
/*
* Use the channel info from the hal to craft the
* channel list. Note that we pass back an unsorted
* list; the caller is required to sort it for us
* (if desired).
*/
*nchans = 0;
if (mwl_hal_getchannelinfo(sc,
if (mwl_hal_getchannelinfo(sc,
if (mwl_hal_getchannelinfo(sc,
if (mwl_hal_getchannelinfo(sc,
}
static int
{
/*
* Use the channel info from the hal to craft the
* channel list for net80211. Note that we pass up
* an unsorted list; net80211 will sort it for us.
*/
}
/*
* Return "hw specs". Note this must be the first
* cmd MUST be done after a firmware download or the
* f/w will lockup.
* XXX move into the hal so driver doesn't need to be responsible
*/
static int
{
struct mwl_hal_hwspec *hw;
int retval;
if (retval == 0) {
}
return (retval);
}
static int
{
#ifdef MWL_MBSS_SUPPORT
/* NB: already byte swapped */
#endif
}
static void
{
}
/* XXX station id must be in [0..63] */
static int
{
int retval;
/* NB: must fix up byte order */
}
"LegacyRateBitMap %x, CapInfo %x\n",
return (retval);
}
/*
* Configure antenna use.
* Takes effect immediately.
* XXX tx antenna setting ignored
* XXX rx antenna setting should always be 3 (for now)
*/
static int
{
int retval;
return (EINVAL);
ant = 3;
return (retval);
}
/*
* Configure radio.
* Takes effect immediately.
* XXX preamble installed after set fixed rate cmd
*/
static int
{
int retval;
if (onoff == 0)
else
return (retval);
}
static int
{
int retval;
return (retval);
}
/*
* Convert public channel flags definition to a
* value suitable for feeding to the firmware.
* Note this includes byte swapping.
*/
static uint32_t
{
uint32_t w;
/*
* NB: f/w only understands FREQ_BAND_5GHZ, supplying the more
* precise band info causes it to lockup (sometimes).
*/
case MWL_CH_10_MHz_WIDTH:
w |= CH_10_MHz_WIDTH;
break;
case MWL_CH_20_MHz_WIDTH:
w |= CH_20_MHz_WIDTH;
break;
case MWL_CH_40_MHz_WIDTH:
default:
w |= CH_40_MHz_WIDTH;
break;
}
case MWL_EXT_CH_NONE:
w |= EXT_CH_NONE;
break;
case MWL_EXT_CH_ABOVE_CTRL_CH:
w |= EXT_CH_ABOVE_CTRL_CH;
break;
case MWL_EXT_CH_BELOW_CTRL_CH:
w |= EXT_CH_BELOW_CTRL_CH;
break;
}
return (LE_32(w));
}
static int
{
int retval;
return (retval);
}
static int
{
const struct mwl_hal_channel *hc;
int i = 0, retval;
/* XXX temp while testing */
"no cal data for channel %u band %u width %u ext %u\n",
return (EINVAL);
}
/* NB: 5Ghz cal data have the channel # in [0]; don't truncate */
for (; i < 4; i++) {
}
return (retval);
}
static int
const MWL_HAL_TXRATE *rate)
{
int retval, i, n;
/* NB: no rate type field */
if (handling == RATE_FIXED) {
} else if (handling == RATE_FIXED_DROP) {
n = 0;
for (i = 0; i < 4; i++) {
break;
fp->RetryCount =
n++;
}
} else
return (retval);
}
static int
{
int retval;
/* NB: no rate type field */
return (retval);
}
/* XXX 0 = indoor, 1 = outdoor */
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
/*
* Set the region code that selects the radar bin'ing agorithm.
*/
static int
{
int retval;
/* XXX map pseudo-codes to fw codes */
switch (regionCode) {
case DOMAIN_CODE_ETSI_131:
break;
default:
break;
}
return (retval);
}
static int
{
int retval;
return (retval);
}
/*
* Inform firmware of tx rate parameters. Called whenever
* user-settable params change and after a channel change.
*/
static int
{
const struct ieee80211_rateset *rs;
/*
* Update the h/w rate map.
* NB: 0x80 for MCS is passed through unchanged
*/
/* rate used to send management frames */
/* rate used to send multicast frames */
}
/*
* Set packet size threshold for implicit use of RTS.
* Takes effect immediately.
* XXX packet length > threshold =>'s RTS
*/
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
static int
{
uint32_t v;
return (0);
}
static int
{
int retval;
return (retval);
}
/*
* Enable sta-mode operation (disables beacon frame xmit).
*/
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
static int
{
int retval;
else
/* NB: includes TKIP MIC keys */
case KEY_TYPE_ID_WEP:
break;
case KEY_TYPE_ID_TKIP:
break;
case KEY_TYPE_ID_AES:
break;
}
#ifdef MWL_MBSS_SUPPORT
#else
#endif
return (retval);
}
static int
{
int retval;
#ifdef MWL_MBSS_SUPPORT
#else
#endif
return (retval);
}
/* ARGSUSED */
static struct ieee80211_node *
{
/* XXX stat+msg */
"alloc node failed\n");
return (NULL);
}
}
static void
{
// mwl_hal_delstation(mn->mn_hvap, vap->iv_myaddr);
// delstaid(sc, mn->mn_staid);
}
}
/*
* Allocate a key cache slot for a unicast key. The
* firmware handles key allocation and every station is
* guaranteed key space so we are always successful.
*/
static int
{
if (k->wk_keyix != IEEE80211_KEYIX_NONE ||
(k->wk_flags & IEEE80211_KEY_GROUP)) {
if (!(&ic->ic_nw_keys[0] <= k &&
/* should not happen */
"bogus group key\n");
return (0);
}
/* give the caller what they requested */
"alloc GROUP key keyix %x, rxkeyix %x\n",
} else {
/*
* Firmware handles key allocation.
*/
"reset key index in key allocation\n");
}
return (1);
}
/*
* Delete a key entry allocated by mwl_key_alloc.
*/
static int
{
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
case IEEE80211_CIPHER_WEP:
break;
case IEEE80211_CIPHER_TKIP:
break;
case IEEE80211_CIPHER_AES_CCM:
break;
default:
/* XXX should not happen */
return (0);
}
}
/*
* Set the key cache contents for the specified key. Key cache
* slot(s) must already have been allocated by mwl_key_alloc.
*/
/* ARGSUSED */
static int
{
#define IEEE80211_IS_STATICKEY(k) \
case IEEE80211_CIPHER_WEP:
if (!IEEE80211_IS_STATICKEY(k)) {
/* NB: WEP is never used for the PTK */
(void) addgroupflags(&hk, k);
}
break;
case IEEE80211_CIPHER_TKIP:
if (!addgroupflags(&hk, k))
break;
case IEEE80211_CIPHER_AES_CCM:
if (!addgroupflags(&hk, k))
break;
default:
/* XXX should not happen */
"unknown cipher %d\n",
return (0);
}
/*
* NB: tkip mic keys get copied here too; the layout
* just happens to match that in ieee80211_key.
*/
/*
* Locate address of sta db entry for writing key;
* the convention unfortunately is somewhat different
* than how net80211, hostapd, and wpa_supplicant think.
*/
/*
* NB: keys plumbed before the sta reaches AUTH state
* will be discarded or written to the wrong sta db
* entry because iv_bss is meaningless. This is ok
* (right now) because we handle deferred plumbing of
* WEP keys when the sta reaches AUTH state.
*/
if (k->wk_flags & IEEE80211_KEY_XMIT) {
/* XXX plumb to local sta db too for static key wep */
}
}
/*
* Plumb any static WEP key for the station. This is
* necessary as we must propagate the key from the
* global key table of the vap to each sta db entry.
*/
static void
{
}
static void
{
struct ieee80211_key *wk;
}
static int
{
if (k->wk_flags & IEEE80211_KEY_GROUP) {
if (k->wk_flags & IEEE80211_KEY_XMIT)
if (k->wk_flags & IEEE80211_KEY_RECV)
return (1);
} else
return (0);
}
/*
*/
static int
{
int maxtxpow;
/*
* Convert to a HAL channel description with
* the flags constrained to reflect the current
* operating mode.
*/
/*
* Tx power is cap'd by the regulatory setting and
* possibly a user-set limit. We pass the min of
* these to the hal to apply them to the cal data
* for this channel.
* XXX min bound?
*/
if (maxtxpow > 100)
maxtxpow = 100;
(void) mwl_setcurchanrates(sc);
return (0);
}
/*
* Convert net80211 channel to a HAL channel.
*/
static void
{
else
} else
/* XXX 10MHz channels */
}
/*
* Return the phy mode for with the specified channel.
*/
enum ieee80211_phymode
{
if (IEEE80211_IS_CHAN_HTA(chan))
return (IEEE80211_MODE_11NA);
else if (IEEE80211_IS_CHAN_HTG(chan))
return (IEEE80211_MODE_11NG);
else if (IEEE80211_IS_CHAN_108G(chan))
return (IEEE80211_MODE_TURBO_G);
else if (IEEE80211_IS_CHAN_ST(chan))
return (IEEE80211_MODE_STURBO_A);
else if (IEEE80211_IS_CHAN_TURBO(chan))
return (IEEE80211_MODE_TURBO_A);
else if (IEEE80211_IS_CHAN_HALF(chan))
return (IEEE80211_MODE_HALF);
else if (IEEE80211_IS_CHAN_QUARTER(chan))
return (IEEE80211_MODE_QUARTER);
else if (IEEE80211_IS_CHAN_A(chan))
return (IEEE80211_MODE_11A);
else if (IEEE80211_IS_CHAN_ANYG(chan))
return (IEEE80211_MODE_11G);
else if (IEEE80211_IS_CHAN_B(chan))
return (IEEE80211_MODE_11B);
else if (IEEE80211_IS_CHAN_FHSS(chan))
return (IEEE80211_MODE_FH);
/* NB: should not get here */
"cannot map channel to mode; freq %u flags 0x%x\n",
return (IEEE80211_MODE_11B);
}
/* XXX inline or eliminate? */
const struct ieee80211_rateset *
{
/* XXX does this work for 11ng basic rates? */
}
/*
* Inform firmware of tx rate parameters.
* Called after a channel change.
*/
static int
{
const struct ieee80211_rateset *rs;
/* rate used to send management frames */
/* rate used to send multicast frames */
}
static const struct mwl_hal_channel *
{
const struct mwl_hal_channel *hc;
const MWL_HAL_CHANNELINFO *ci;
i = chan - 1;
if (c->channelFlags.ExtChnlOffset ==
i -= 4;
} else
/* 2.4G channel table is directly indexed */
if (c->channelFlags.ExtChnlOffset ==
chan -= 4;
} else
/* 5GHz channel table is sparse and must be searched */
break;
} else
return (hc);
}
/*
* Map SKU+country code to region code for radar bin'ing.
*/
static int
{
case SKU_FCC:
case SKU_FCC3:
return (DOMAIN_CODE_FCC);
case SKU_CA:
return (DOMAIN_CODE_IC);
case SKU_ETSI:
case SKU_ETSI2:
case SKU_ETSI3:
return (DOMAIN_CODE_SPAIN);
return (DOMAIN_CODE_FRANCE);
/* XXX force 1.3.1 radar type */
return (DOMAIN_CODE_ETSI_131);
case SKU_JAPAN:
return (DOMAIN_CODE_MKK);
case SKU_ROW:
return (DOMAIN_CODE_DGT); /* Taiwan */
case SKU_APAC:
case SKU_APAC2:
case SKU_APAC3:
return (DOMAIN_CODE_AUS); /* Australia */
}
/* XXX KOREA? */
return (DOMAIN_CODE_FCC); /* XXX? */
}
/*
* Setup the rx data structures. This should only be
* done once or we may get out of sync with the firmware.
*/
static int
{
struct mwl_rx_ring *ring;
struct mwl_rxdesc *ds;
int i;
for (i = 0; i < MWL_RX_RING_COUNT; i++, bf++) {
/*
* NB: DMA buffer contents is known to be unmodified
* so there's no need to flush the data cache.
*/
/*
* Setup descriptor.
*/
/* NB: don't touch pPhysNext, set once */
i * sizeof (struct mwl_rxdesc),
sizeof (struct mwl_rxdesc),
}
}
}
/* set filters, etc. */
(void) mwl_mode_init(sc);
return (0);
}
static int
{
/*
* NB: Ignore promisc in hostap mode; it's set by the
* bridge. This is wrong but we have no way to
* identify internal requests (from the bridge)
* versus external requests such as for tcpdump.
*/
/* mwl_setmcastfilter - not support now */
(void) mwl_hal_setpromisc(sc, 0);
return (0);
}
/*
* Kick the firmware to tell it there are new tx descriptors
* for processing. The driver says what h/w q has work in
* case the f/w ever gets smarter.
*/
/* ARGSUSED */
static void
{
}
static int
{
struct mwl_tx_ring *ring;
struct mwl_txdesc *ds;
MWL_TXLOCK(sc);
err = DDI_SUCCESS;
goto fail1;
}
sc->sc_tx_nobuf++;
goto fail1;
}
if (m == NULL) {
"can't alloc mblk.\n");
err = DDI_FAILURE;
goto fail1;
}
}
err = DDI_FAILURE;
goto fail2;
}
const struct ieee80211_cipher *cip;
struct ieee80211_key *k;
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
err = DDI_FAILURE;
goto fail3;
}
/*
* Adjust the packet length for the crypto additions
* done during encap and any other bits that the f/w
* will add later on.
*/
/* packet header may have moved, reset our local pointer */
}
/*
* inject FW specific fields into the 802.11 frame
*
* 2 bytes FW len (inject)
* 24 bytes 802.11 frame header
* 6 bytes addr4 (inject)
* n bytes 802.11 frame body
*/
pktlen += 8;
0,
ds->ack_wcb_addr = 0;
"tx desc Status %x, DataRate %x, TxPriority %x, QosCtrl %x, "
"PktLen %x, SapPktInfo %x, Format %x, Pad %x, ack_wcb_addr %x\n",
sizeof (struct mwl_txdesc),
"pktlen = %u, slot = %u, queued = %x\n",
/*
* NB: We don't need to lock against tx done because
* this just prods the firmware to check the transmit
* descriptors. The firmware will also start fetching
* descriptors by itself if it notices new ones are
* present when it goes to deliver a tx done interrupt
* to the host. So if we race with tx done processing
* it's ok. Delivering the kick here rather than in
* mwl_tx_start is an optimization to avoid poking the
* firmware for each packet.
*
* NB: the queue id isn't used so 0 is ok.
*/
mwl_hal_txstart(sc, 0);
freemsg(m);
err == DDI_SUCCESS)
return (err);
}
/*
* This function is called periodically (every 200ms) during scanning to
* switch from one channel to another.
*/
static void
mwl_next_scan(void *arg)
{
(void) ieee80211_next_scan(ic);
sc->sc_scan_id = 0;
}
/*
* Convert a legacy rate set to a firmware bitmask.
*/
static uint32_t
{
int i;
rates = 0;
}
return (rates);
}
/*
* Craft station database entry for station.
* NB: use host byte order here, the hal handles byte swapping.
*/
static MWL_HAL_PEERINFO *
{
return (pi);
}
static int
{
enum ieee80211_state ostate;
struct ieee80211_channel *ic_chan;
if (sc->sc_scan_id != 0) {
sc->sc_scan_id = 0;
}
"ostate %x -> nstate %x\n",
switch (nstate) {
case IEEE80211_S_INIT:
break;
case IEEE80211_S_SCAN:
if (ostate != IEEE80211_S_INIT) {
sc->sc_cur_chan =
"chan num is %u, sc chan is %u\n",
}
}
drv_usectohz(250000));
break;
case IEEE80211_S_AUTH:
"chan num is %u, sc chan is %u\n",
break;
case IEEE80211_S_ASSOC:
break;
case IEEE80211_S_RUN:
(void) mwl_hal_newstation(sc,
(void) mwl_hal_setassocid(sc,
(void) mwl_setrates(ic);
break;
default:
break;
}
}
/*
* Set the interrupt mask.
*/
static void
{
}
/*
* Return the current ISR setting and clear the cause.
*/
static void
{
cause = 0;
} else if (cause != 0) {
/* clear cause bits */
}
}
static void
{
struct mwl_tx_ring *ring;
struct mwl_txdesc *ds;
MWL_TXLOCK(sc);
return;
}
0,
for (;;) {
break;
}
break;
}
"recv tx desc status %x, datarate %x, txpriority %x, "
"QosCtrl %x, pktLen %x, SapPktInfo %x, Format %x, "
"pad %x, ack_wcb_addr %x\n",
/* descriptor is no longer valid */
sizeof (struct mwl_txdesc),
" tx done idx=%u, queued= %d\n",
if (sc->sc_need_sched &&
sc->sc_need_sched = 0;
}
}
}
/*
* Convert hardware signal strength to rssi. The value
* provided by the device has the noise floor added in;
* we need to compensate for this but we don't have that
* so we use a fixed value.
*
* The offset of 8 is good for both 2.4 and 5GHz. The LNA
* offset is already set as part of the initial gain. This
* will give at least +/- 3dB for 2.4GHz and +/- 5dB for 5GHz.
*/
static int
{
/* XXX hack guess until we have a real noise floor */
}
static void
{
struct mwl_rx_ring *ring;
struct ieee80211_node *ni;
struct ieee80211_frame *wh;
struct mwl_rxdesc *ds;
MWL_RXLOCK(sc);
sizeof (struct mwl_rxdesc),
break;
if (status & EAGLE_RXD_STATUS_DECRYPT_ERR_MASK) {
"rx decrypt error\n");
}
/*
* Sync the data buffer.
*/
0,
"packet len error %d\n", len);
goto rxnext;
}
"alloc mblk error\n");
sc->sc_rx_nobuf++;
goto rxnext;
}
goto rxnext;
}
/*
* The f/w strips WEP header but doesn't clear
* the WEP bit; mark the packet with M_WEP so
* net80211 will treat the data as decrypted.
* While here also clear the PWR_MGT bit since
* power save is handled by the firmware and
* passing this up will potentially cause the
* upper layer to put a station in power save
* (except when configured with MWL_HOST_PS_SUPPORT).
*/
#ifdef MWL_HOST_PS_SUPPORT
#else
#endif
/* calculate rssi early so we can re-use for each aggregate */
/* send the frame to the 802.11 layer */
/*
* Setup descriptor.
*/
/* NB: don't touch pPhysNext, set once */
sizeof (struct mwl_rxdesc),
/* NB: ignore ENOMEM so we process more descriptors */
}
}
/*ARGSUSED*/
static uint_t
{
/*
* Check if the soft interrupt is triggered by another
* driver at the same level.
*/
if (sc->sc_rx_pend) {
sc->sc_rx_pend = 0;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/*ARGSUSED*/
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
/*
* Figure out the reason(s) for the interrupt.
*/
if (status == 0) {
return (DDI_INTR_UNCLAIMED);
}
if (status & MACREG_A2HRIC_BIT_RX_RDY) {
}
if (status & MACREG_A2HRIC_BIT_TX_DONE) {
}
if (status & MACREG_A2HRIC_BIT_BA_WATCHDOG) {
"ba watchdog\n");
}
if (status & MACREG_A2HRIC_BIT_OPC_DONE) {
"opc done\n");
}
if (status & MACREG_A2HRIC_BIT_MAC_EVENT) {
"mac event\n");
}
if (status & MACREG_A2HRIC_BIT_ICV_ERROR) {
"ICV error\n");
}
if (status & MACREG_A2HRIC_BIT_QUEUE_EMPTY) {
"queue empty\n");
}
if (status & MACREG_A2HRIC_BIT_QUEUE_FULL) {
"queue full\n");
}
if (status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
"radar detect\n");
}
if (status & MACREG_A2HRIC_BIT_CHAN_SWITCH) {
"chan switch\n");
}
return (DDI_INTR_CLAIMED);
}
static int
{
int err = 0;
mwl_hal_intrset(sc, 0);
if (err != 0) {
"could not set rx antenna\n");
goto fail;
}
if (err != 0) {
"could not set tx antenna\n");
goto fail;
}
if (err != 0) {
"could not set radio\n");
goto fail;
}
if (err != 0) {
"could not set wme\n");
goto fail;
}
/* select default channel */
if (err != 0) {
"could not set wme\n");
goto fail;
}
if (err != 0) {
"could not set rate adapt mode\n");
goto fail;
}
if (err != 0) {
"could not set optimization level\n");
goto fail;
}
if (err != 0) {
"could not set regioncode\n");
goto fail;
}
if (err != 0) {
"could not set start recv logic\n");
goto fail;
}
/*
* Enable interrupts.
*/
if (err != 0) {
"could not get hal start\n");
goto fail;
}
if (err != 0) {
"could not set infra mode\n");
goto fail;
}
fail:
return (err);
}
static int
{
if (err != 0) {
"failed to load fw\n");
goto fail;
}
if (err != 0) {
"failed to get hw spec\n");
goto fail;
}
if (err != 0) {
"could not alloc cmd dma buffer\n");
goto fail;
}
if (err != 0) {
"could not alloc tx ring %d\n", qid);
goto fail;
}
}
if (err != 0) {
"could not setup dma\n");
goto fail;
}
if (err != 0) {
"could not setup txq\n");
goto fail;
}
fail:
return (err);
}
static void
{
int err;
/* by pass if it's quiesced */
if (!MWL_IS_QUIESCE(sc))
if (err != 0) {
"could not stop hw\n");
}
/* by pass if it's quiesced */
if (!MWL_IS_QUIESCE(sc))
}
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_NORCVBUF:
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:
case WIFI_STAT_WEP_ERRORS:
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:
default:
return (ENOTSUP);
}
return (0);
}
static int
mwl_m_start(void *arg)
{
int err;
if (err != DDI_SUCCESS) {
"Hardware initialization failed\n");
goto fail1;
}
return (0);
return (err);
}
static void
mwl_m_stop(void *arg)
{
}
/*ARGSUSED*/
static int
{
int err;
return (err);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
static mblk_t *
{
if (MWL_IS_SUSPEND(sc)) {
return (NULL);
}
/*
* 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);
}
DDI_SUCCESS) {
break;
}
}
return (mp);
}
static void
{
int err;
if (ic->ic_des_esslen) {
if (MWL_IS_RUNNING(sc)) {
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
}
}
/*
*/
static int
{
int err = 0;
return (err);
}
static void
{
}
static int
{
int err;
wldp_buf);
if (ic->ic_des_esslen) {
if (MWL_IS_RUNNING(sc)) {
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
err = 0;
}
return (err);
}
static int
{
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if (mwl_resume(sc) != 0) {
"failed to resume\n");
return (DDI_FAILURE);
}
if (MWL_IS_RUNNING(sc)) {
}
"resume now\n");
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
"Unable to alloc soft state\n");
return (DDI_FAILURE);
}
/* PCI configuration space */
if (err != DDI_SUCCESS) {
"ddi_regs_map_setup() failed");
goto attach_fail0;
}
if (!csz)
csz = 16;
"vendor 0x%x, device id 0x%x, cache size %d\n",
/*
* Enable response to memory space accesses,
* and enabe bus master.
*/
command);
/* BAR0 */
if (err != DDI_SUCCESS) {
"i/o space failed");
goto attach_fail1;
}
/* BAR1 */
if (err != DDI_SUCCESS) {
"memory space failed");
goto attach_fail2;
}
"PCI configuration is done successfully\n");
/*
* Alloc cmd DMA buffer for firmware download
*/
if (err != 0) {
"could not alloc cmd dma buffer\n");
goto attach_fail3;
}
sc->sc_hw_flags = 0;
/*
* Some cards have SDRAM. When loading firmware we need
* to reset the SDRAM controller prior to doing this.
* When the SDRAMSIZE is non-zero we do that work in
* mwl_hal_fwload.
*/
switch (device_id) {
case 0x2a02: /* CB82 */
case 0x2a03: /* CB85 */
case 0x2a08: /* MC85_B1 */
case 0x2a0b: /* CB85AP */
case 0x2a24:
break;
case 0x2a04: /* MC85 */
break;
default:
break;
}
if (err != 0) {
"firmware download failed\n");
goto attach_fail4;
}
"firmware download successfully\n");
if (err != 0) {
"failed to get hw spec\n");
goto attach_fail4;
}
if (err != 0) {
"failed to get channels\n");
goto attach_fail4;
}
/*
* Alloc rx DMA buffer
*/
if (err != 0) {
"could not alloc cmd dma buffer\n");
goto attach_fail5;
}
/*
* Alloc rx DMA buffer
*/
if (err != 0) {
"could not alloc tx ring %d\n", qid);
goto attach_fail6;
}
}
if (err != 0) {
"could not setup dma\n");
goto attach_fail6;
}
if (err != 0) {
"could not setup txq\n");
goto attach_fail6;
}
"mwl MAC:%2x:%2x:%2x:%2x:%2x:%2x\n",
ic->ic_macaddr[0],
if (err != 0) { /* NB: mwl_setupdma prints msg */
"could not set mac\n");
goto attach_fail6;
}
/* set supported rates */
/* set supported .11b and .11g channels (1 through 14) */
for (i = 1; i <= 14; i++) {
}
/* set device capabilities */
IEEE80211_C_TXPMGT | /* tx power management */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT; /* short slot time supported */
/* Enable hardware encryption */
/* register WPA door */
/* override state transition machine */
ic->ic_def_txkey = 0;
if (err != 0) {
"could not create new station\n");
goto attach_fail7;
}
// mwl_setglobalkeys(ic);
"fixed type interrupt is not supported\n");
goto attach_fail7;
}
"no fixed interrupts\n");
goto attach_fail7;
}
"ddi_intr_alloc() failed 0x%x\n", err);
goto attach_fail8;
}
if (err != DDI_SUCCESS) {
"ddi_intr_get_pri() failed 0x%x\n", err);
goto attach_fail9;
}
if (err != DDI_SUCCESS) {
"ddi_add_softintr() failed");
goto attach_fail9;
}
if (err != DDI_SUCCESS) {
"ddi_intr_addr_handle() failed\n");
goto attach_fail10;
}
if (err != DDI_SUCCESS) {
"ddi_intr_enable() failed\n");
goto attach_fail11;
}
/*
* Provide initial settings for the WiFi plugin; whenever this
* information changes, we need to call mac_plugindata_update()
*/
"MAC version mismatch\n");
goto attach_fail12;
}
if (err != 0) {
"mac_register err %x\n", err);
goto attach_fail12;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
"mwl", instance);
if (err != 0) {
"create minor node error\n");
goto attach_fail13;
}
/*
* Notify link is down now
*/
"driver attach successfully\n");
return (DDI_SUCCESS);
while (--qid >= 0)
return (DDI_FAILURE);
}
static int32_t
{
int qid;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (MWL_IS_RUNNING(sc))
"suspend now\n");
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/*
* Unregister from the MAC layer subsystem
*/
/*
* detach ieee80211 layer
*/
"detach successfully\n");
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.
*/
int
{
return (DDI_FAILURE);
#ifdef DEBUG
mwl_dbg_flags = 0;
#endif
/*
* No more blocking is allowed while we are in quiesce(9E) entry point
*/
/*
* Disable all interrupts
*/
return (DDI_SUCCESS);
}
int
_init(void)
{
int status;
sizeof (struct mwl_softc), 1);
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
{
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}