ipw2200.c revision 3b608f655bd09bfb0ba6b14bb23ec70acc77814c
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2004, 2005
* Damien Bergamini <damien.bergamini@free.fr>. 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 unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/byteorder.h>
#include <sys/ethernet.h>
#include <sys/mac_wifi.h>
#include "ipw2200.h"
#include "ipw2200_impl.h"
#include <inet/wifi_ioctl.h>
/*
* minimal size reserved in tx-ring
*/
#define IPW2200_TX_RING_MIN (8)
#define IPW2200_TXBUF_SIZE (IEEE80211_MAX_LEN)
#define IPW2200_RXBUF_SIZE (4096)
static void *ipw2200_ssp = NULL;
/*
* PIO access attributor for registers
*/
static ddi_device_acc_attr_t ipw2200_csr_accattr = {
};
/*
* DMA access attributor for descriptors
*/
static ddi_device_acc_attr_t ipw2200_dma_accattr = {
};
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t ipw2200_dma_attr = {
DMA_ATTR_V0, /* version */
0x0000000000000000ULL, /* addr_lo */
0x00000000ffffffffULL, /* addr_hi */
0x00000000ffffffffULL, /* counter */
0x0000000000000004ULL, /* alignment */
0xfff, /* burst */
1, /* min xfer */
0x00000000ffffffffULL, /* max xfer */
0x00000000ffffffffULL, /* seg boud */
1, /* s/g list */
1, /* granularity */
0 /* flags */
};
static uint8_t ipw2200_broadcast_addr[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
{12, 18, 24, 36, 48, 72, 96, 108}
};
{2, 4, 11, 22}
};
{2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
};
/*
* Used by multi function thread
*/
extern pri_t minclsyspri;
/*
* ipw2200 specific hardware operations
*/
/*
* GLD specific operations
*/
static int ipw2200_m_start(void *arg);
static void ipw2200_m_stop(void *arg);
/*
* Interrupt and Data transferring operations
*/
struct ipw2200_frame *frame);
struct ipw2200_notif *notif);
/*
* WiFi specific operations
*/
/*
* IOCTL Handler
*/
/*
* Mac Call Back entries
*/
NULL,
};
/*
* DEBUG Facility
*/
#define MAX_MSG (128)
uint32_t ipw2200_debug = 0;
/*
* supported debug marks are:
* | IPW2200_DBG_CSR
* | IPW2200_DBG_TABLE
* | IPW2200_DBG_HWCAP
* | IPW2200_DBG_TX
* | IPW2200_DBG_INIT
* | IPW2200_DBG_FW
* | IPW2200_DBG_NOTIF
* | IPW2200_DBG_SCAN
* | IPW2200_DBG_IOCTL
* | IPW2200_DBG_RING
* | IPW2200_DBG_INT
* | IPW2200_DBG_RX
* | IPW2200_DBG_DMA
* | IPW2200_DBG_GLD
* | IPW2200_DBG_WIFI
* | IPW2200_DBG_SOFTINT
*/
/*
* Global tunning parameter to work around unknown hardware issues
*/
#define IEEE80211_IS_CHAN_2GHZ(_c) \
#define IEEE80211_IS_CHAN_5GHZ(_c) \
void
{
int instance;
if (dip) {
} else
}
/*
* Device operations
*/
int
{
struct ipw2200_softc *sc;
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
if (cmd != DDI_ATTACH) {
err = DDI_FAILURE;
goto fail1;
}
if (err != DDI_SUCCESS) {
"ipw2200_attach(): unable to allocate soft state\n"));
goto fail1;
}
/*
* Map config spaces register to read the vendor id, device id, sub
* vendor id, and sub device id.
*/
0, 0, &ipw2200_csr_accattr, &cfgh);
if (err != DDI_SUCCESS) {
"ipw2200_attach(): unable to map spaces regs\n"));
goto fail2;
}
"ipw2200_attach(): vendor = 0x%04x, devic = 0x%04x,"
"subversion = 0x%04x, subdev = 0x%04x",
/*
* Map operating registers
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): ddi_regs_map_setup() failed\n"));
goto fail2;
}
/*
* Reset the chip
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): ipw2200_chip_reset() failed\n"));
goto fail3;
}
/*
* Get the hardware configuration, including the MAC address
* Then, init all the rings needed.
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): ipw2200_ring_init() failed\n"));
goto fail3;
}
/*
* Initialize mutexs and condvars
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): ddi_get_iblock_cookie() failed\n"));
goto fail4;
}
/*
* interrupt lock
*/
/*
* command ring lock
*/
/*
* tx ring lock
*/
/*
* multi-function lock, may acquire this during interrupt
*/
sc->sc_mfthread_switch = 0;
/*
* Initialize the WiFi part, which will be used by generic layer
* Need support more features in the furture, such as
* IEEE80211_C_IBSS
*/
/*
* set mac addr
*/
/*
* set supported .11a rates and channel - (2915ABG only)
*/
if (device >= 0x4223) {
/* .11a rates */
/* .11a channels */
for (i = 36; i <= 64; i += 4) {
}
for (i = 149; i <= 165; i += 4) {
}
}
/*
* set supported .11b and .11g rates
*/
/*
* set supported .11b and .11g channels(1 through 14)
*/
for (i = 1; i < 14; i++) {
}
/*
* IBSS channal undefined for now
*/
/*
* init generic layer, then override state transition machine
*/
/*
* Override 80211 default routines
*/
ic->ic_def_txkey = 0;
/*
* Add the interrupt handler
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): ddi_add_intr() failed\n"));
goto fail5;
}
/*
* Initialize pointer to device specific functions
*/
if (err != 0) {
"ipw2200_attach(): mac_alloc() failed\n"));
goto fail6;
}
/*
* Register the macp to mac
*/
if (err != DDI_SUCCESS) {
"ipw2200_attach(): mac_register() failed\n"));
goto fail6;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
if (err != DDI_SUCCESS)
"ipw2200_attach(): ddi_create_minor_node() failed\n"));
/*
* Cache firmware will always be true
*/
(void) ipw2200_cache_firmware(sc);
/*
* Notify link is down now
*/
/*
* Create the mf thread to handle the link status,
* recovery fatal error, etc.
*/
return (DDI_SUCCESS);
return (err);
}
int
{
struct ipw2200_softc *sc =
int err;
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
/*
* Destroy the mf_thread
*/
sc->sc_mfthread_switch = 0;
break;
}
/*
* Unregister from the MAC layer subsystem
*/
if (err != DDI_SUCCESS)
return (err);
/*
* Detach ieee80211
*/
(void) ipw2200_free_firmware(sc);
return (DDI_SUCCESS);
}
static void
{
"ipw2200_stop(): enter\n"));
/*
* Reset ring
*/
"ipw2200_stop(): exit\n"));
}
static int
{
struct ipw2200_configuration cfg;
struct ipw2200_txpower pwr;
struct ipw2200_rateset rs;
struct ipw2200_wep_key wkey;
int err, i;
/*
* Set the IBSS mode channel: Tx power
*/
}
"ipw2200_config(): Setting .11b channels Tx power\n"));
if (err != DDI_SUCCESS)
return (err);
"ipw2200_config(): Setting .11g channels Tx power\n"));
if (err != DDI_SUCCESS)
return (err);
}
/*
* Set MAC address
*/
"ipw2200_config(): Setting MAC address to "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
IEEE80211_ADDR_LEN, 0);
if (err != DDI_SUCCESS)
return (err);
/*
* Set basic system config settings: configuration(capabilities)
*/
"ipw2200_config(): Configuring adapter\n"));
if (err != DDI_SUCCESS)
return (err);
/*
* Set power mode
*/
if (err != DDI_SUCCESS)
return (err);
/*
* Set supported rates
*/
if (err != DDI_SUCCESS)
return (err);
if (err != DDI_SUCCESS)
return (err);
/*
* Set RTS(request-to-send) threshold
*/
sizeof (data), 0);
if (err != DDI_SUCCESS)
return (err);
/*
* Set fragmentation threshold
*/
"ipw2200_config(): Setting fragmentation threshold to %u\n",
sizeof (data), 0);
if (err != DDI_SUCCESS)
return (err);
/*
* Set desired ESSID if we have
*/
if (ic->ic_des_esslen != 0) {
"ipw2200_config(): Setting desired ESSID to "
"(%u),%c%c%c%c%c%c%c%c\n",
ic->ic_des_esslen, 0);
if (err != DDI_SUCCESS)
return (err);
}
/*
* Set WEP initial vector(random seed)
*/
"ipw2200_config(): Setting initialization vector to %u\n",
if (err != DDI_SUCCESS)
return (err);
/*
* Set WEP if any
*/
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
if (err != DDI_SUCCESS)
return (err);
}
}
"ipw2200_config(): Enabling adapter\n"));
}
static int
{
struct ipw2200_cmd_desc *cmd;
"ipw2200_cmd(): cmd-cur=%d\n", idx));
else {
}
/*
* DMA sync
*/
idx * sizeof (struct ipw2200_cmd_desc),
sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV);
sc->sc_cmd_free--;
if (async)
goto out;
/*
* Wait for command done
*/
/* pending */
< 0)
break;
}
return (DDI_FAILURE);
out:
return (DDI_SUCCESS);
}
/*
* If init failed, it will call stop internally. Therefore, it's unnecessary
* to call ipw2200_stop() when this subroutine is failed. Otherwise, it may
* be called twice.
*/
int
{
int err;
/*
* No firmware is available, failed
*/
"ipw2200_init(): no firmware is available\n"));
return (DDI_FAILURE); /* return directly */
}
if (err != DDI_SUCCESS) {
"ipw2200_init(): could not reset adapter\n"));
goto fail;
}
/*
* Load boot code
*/
if (err != DDI_SUCCESS) {
"ipw2200_init(): could not load boot code\n"));
goto fail;
}
/*
* Load boot microcode
*/
if (err != DDI_SUCCESS) {
"ipw2200_init(): could not load microcode\n"));
goto fail;
}
/*
* Load firmware
*/
if (err != DDI_SUCCESS) {
"ipw2200_init(): could not load firmware\n"));
goto fail;
}
/*
* Hardware will be enabled after configuration
*/
if (err != DDI_SUCCESS) {
"ipw2200_init(): device configuration failed\n"));
goto fail;
}
/*
* workround to prevent too many h/w error.
* delay for a while till h/w is stable.
*/
return (DDI_SUCCESS); /* return successfully */
fail:
return (err);
}
/*
* get hardware configurations from EEPROM embedded within PRO/2200
*/
static void
{
int i;
/*
* Get mac address
*/
i = 0;
/*
* formatted MAC address string
*/
"%02x:%02x:%02x:%02x:%02x:%02x",
}
/*
* all ipw2200 interrupts will be masked by this routine
*/
static void
{
int ntries;
/*
* disable interrupts
*/
/*
* wait long enough to ensure hardware stop successfully.
*/
break;
/* wait for a while */
drv_usecwait(100);
}
if (ntries == 500)
"ipw2200_master_stop(): timeout\n"));
}
/*
* all ipw2200 interrupts will be masked by this routine
*/
static int
{
int ntries, i;
/*
* Move adapter to DO state
*/
/*
* Initialize Phase-Locked Level (PLL)
*/
/*
* Wait for clock stabilization
*/
break;
drv_usecwait(200);
}
if (ntries == 1000) {
"ipw2200_chip_reset(): timeout\n"));
return (DDI_FAILURE);
}
drv_usecwait(10);
/*
* clear NIC memory
*/
for (i = 0; i < 0xc000; i++)
return (DDI_SUCCESS);
}
/*
* This function is used by wificonfig/dladm to get the current
*/
int
{
int val;
IPW2200_IO_RADIO_ENABLED) ? 1 : 0;
return (val);
}
/*
* This function is used to get the statistic
*/
void
{
"ipw2200_get_statistic(): fw doesn't download yet."));
return;
}
/*
* To retrieve the statistic information into proper places. There are
* lot of information. These table will be read once a second.
* Hopefully, it will not effect the performance.
*/
/*
*/
/* WIFI_STAT_TX_FRAGS */
/* WIFI_STAT_MCAST_TX */
/* WIFI_STAT_TX_RETRANS */
/* WIFI_STAT_TX_FAILED */
/* MAC_STAT_OBYTES */
}
/*
* DMA region alloc subroutine
*/
int
{
int err;
"ipw2200_dma_region_alloc(): size =%u\n", size));
if (err != DDI_SUCCESS) {
"ipw2200_dma_region_alloc(): "
"ddi_dma_alloc_handle() failed\n"));
goto fail0;
}
if (err != DDI_SUCCESS) {
"ipw2200_dma_region_alloc(): "
"ddi_dma_mem_alloc() failed\n"));
goto fail1;
}
if (err != DDI_DMA_MAPPED) {
"ipw2200_dma_region_alloc(): "
"ddi_dma_addr_bind_handle() failed\n"));
goto fail2;
}
err = DDI_FAILURE;
goto fail3;
}
"ipw2200_dma_region_alloc(): get physical-base=0x%08x\n",
return (DDI_SUCCESS);
return (err);
}
void
{
}
static int
{
int err, i;
/*
* tx desc ring
*/
IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc),
if (err != DDI_SUCCESS)
goto fail0;
/*
* tx buffer array
*/
for (i = 0; i < IPW2200_TX_RING_SIZE; i++) {
if (err != DDI_SUCCESS) {
while (i >= 0) {
i--;
}
goto fail1;
}
}
/*
* rx buffer array
*/
for (i = 0; i < IPW2200_RX_RING_SIZE; i++) {
if (err != DDI_SUCCESS) {
while (i >= 0) {
i--;
}
goto fail2;
}
}
/*
* cmd desc ring
*/
IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc),
if (err != DDI_SUCCESS)
goto fail3;
return (DDI_SUCCESS);
for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
return (err);
}
static void
{
int i;
/*
* tx ring desc
*/
/*
* tx buf
*/
for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
/*
* rx buf
*/
for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
/*
* command ring desc
*/
}
static void
{
int i;
/*
* tx desc ring & buffer array
*/
for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
/*
* rx buffer array
*/
for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
/*
* command desc ring
*/
sc->sc_cmd_cur = 0;
}
/*
* tx, rx rings and command initialization
*/
static int
{
int err;
if (err != DDI_SUCCESS)
return (err);
return (DDI_SUCCESS);
}
static void
{
int i;
/*
* command desc ring
*/
/*
* tx desc ring. only tx1 is used, tx2, tx3, and tx4 are unused
*/
/*
* tx2, tx3, tx4 is not used
*/
/*
* rx buffer ring
*/
for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
/*
* all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
*/
}
int
{
struct ipw2200_scan scan;
int cnt, i;
"ipw2200_start_scan(): start scanning \n"));
/*
* start scanning
*/
/*
* Compact supported channel number(5G) into a single buffer
*/
cnt = 0;
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
cnt++;
}
}
/*
* Compact supported channel number(2G) into a single buffer
*/
cnt = 0;
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
cnt++;
}
}
}
int
{
struct ipw2200_configuration cfg;
struct ipw2200_rateset rs;
struct ipw2200_associate assoc;
int err;
/*
* set the confiuration
*/
/* enable b/g auto-detection */
if (err != DDI_SUCCESS)
return (err);
}
/*
*/
"ipw2200_auth_and_assoc(): "
"setting ESSID to(%u),%c%c%c%c%c%c%c%c\n",
if (err != DDI_SUCCESS)
return (err);
/*
* set the rate: the rate set has already been ''negocitated''
*/
"ipw2200_auth_and_assoc(): "
if (err != DDI_SUCCESS)
return (err);
/*
* set the sensitive
*/
"ipw2200_auth_and_assoc(): "
if (err != DDI_SUCCESS)
return (err);
/*
* invoke command associate
*/
/*
* use the value set to ic_bss to retraive current sharedmode
*/
"ipw2200_auth_and_assoc(): "
"associate to shared key mode, set thru. ioctl"));
}
else
"ipw2200_auth_and_assoc(): "
"associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), "
"chan(%u), auth(%u)\n",
}
/* ARGSUSED */
static int
{
wifi_data_t wd = { 0 };
switch (state) {
case IEEE80211_S_SCAN:
(void) ipw2200_start_scan(sc);
}
break;
case IEEE80211_S_AUTH:
(void) ipw2200_auth_and_assoc(sc);
break;
case IEEE80211_S_RUN:
/*
* We can send data now; update the fastpath with our
* current associated BSSID and other relevant settings.
*
* Hardware to ahndle the wep encryption. Set the encryption
* as false.
* wd.wd_wep = ic->ic_flags & IEEE80211_F_WEPON;
*/
break;
case IEEE80211_S_ASSOC:
case IEEE80211_S_INIT:
break;
}
/*
* notify to update the link
*/
(state != IEEE80211_S_RUN)) {
}
"ipw2200_newstat(): %s -> %s\n",
return (DDI_SUCCESS);
}
/*
* GLD operations
*/
/* ARGSUSED */
static int
{
"ipw2200_m_stat(): enter\n"));
/*
* Some of below statistic data are from hardware, some from net80211
*/
switch (stat) {
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
/*
* Get below from hardware statistic, retraive net80211 value once 1s
*/
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_TX_FAILED:
case WIFI_STAT_TX_RETRANS:
/*
* Get blow information from net80211
*/
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:
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
/*
* Need be supported later
*/
case MAC_STAT_IFSPEED:
case MAC_STAT_NOXMTBUF:
case MAC_STAT_IERRORS:
case MAC_STAT_OERRORS:
default:
return (ENOTSUP);
}
return (0);
}
/* ARGSUSED */
static int
{
/* not supported */
"ipw2200_m_multicst(): enter\n"));
return (DDI_SUCCESS);
}
/*
* Multithread handler for linkstatus, fatal error recovery, get statistic
*/
static void
{
int stat_cnt = 0;
while (sc->sc_mfthread_switch) {
/*
* notify the link state
*/
"ipw2200_thread(): link status --> %d\n",
sc->sc_linkstate));
}
/*
* recovery fatal error
*/
"ipw2200_thread(): "
"try to recover fatal hw error\n"));
/*
* workround. Delay for a while after init especially
* when something wrong happened already.
*/
}
/*
* get statistic, the value will be retrieved by m_stat
*/
if (stat_cnt == 10) {
stat_cnt = 0; /* re-start */
} else
stat_cnt++; /* until 1s */
}
}
static int
ipw2200_m_start(void *arg)
{
"ipw2200_m_start(): enter\n"));
/*
* initialize ipw2200 hardware, everything ok will start scan
*/
(void) ipw2200_init(sc);
return (DDI_SUCCESS);
}
static void
ipw2200_m_stop(void *arg)
{
"ipw2200_m_stop(): enter\n"));
}
static int
{
int err;
"ipw2200_m_unicst(): enter\n"));
"ipw2200_m_unicst(): GLD setting MAC address to "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
if (err != DDI_SUCCESS) {
"ipw2200_m_unicst(): "
"device configuration failed\n"));
goto fail;
}
}
}
return (DDI_SUCCESS);
fail:
return (err);
}
static int
{
/* not supported */
"ipw2200_m_promisc(): enter. "
"GLD setting promiscuous mode - %d\n", on));
return (DDI_SUCCESS);
}
static mblk_t *
{
/*
* 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.
*/
"ipw2200_m_tx(): discard msg, ic_state = %u\n",
return (NULL);
}
DDI_FAILURE) {
break;
}
}
return (mp);
}
/* ARGSUSED */
static int
{
struct ieee80211_node *in;
struct ieee80211_frame *wh;
struct ipw2200_tx_desc *txdsc;
struct dma_region *dr;
int err;
/* tmp pointer, used to pack header and payload */
uint8_t *p;
"ipw2200_send(): enter\n"));
err = DDI_SUCCESS;
/*
* skip all management frames since ipw2200 won't generate any
* management frames. Therefore, drop this package.
*/
err = DDI_SUCCESS;
goto fail0;
}
/*
* need 1 empty descriptor
*/
"ipw2200_send(): no enough descriptors(%d)\n",
sc->sc_tx_free));
err = DDI_FAILURE;
goto fail1;
}
"ipw2200_send(): tx-free=%d,tx-curr=%d\n",
err = DDI_SUCCESS;
goto fail1;
}
/*
* get txdsc and wh
*/
/*
* extract header from message
*/
off = 0;
while (off < sizeof (struct ieee80211_frame)) {
if (cnt) {
} else
}
/*
* extract payload from message
*/
off = 0;
while (m0) {
if (cnt)
}
}
else
/*
* DMA sync: buffer and desc
*/
idx * sizeof (struct ipw2200_tx_desc),
sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV);
sc->sc_tx_free--;
/*
* update txcur
*/
/*
* success, free the original message
*/
if (mp)
"ipw2200_send(): exit - err=%d\n", err));
return (err);
}
/*
* IOCTL handlers
*/
#define IEEE80211_IOCTL_REQUIRED (1)
#define IEEE80211_IOCTL_NOT_REQUIRED (0)
static void
{
"ipw2200_m_ioctl(): enter\n"));
/*
* Check whether or not need to handle this in net80211
*
*/
return;
(void) ipw2200_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
(void) ipw2200_chip_reset(sc);
}
}
static int
{
"ipw2200_ioctl(): ioctl buffer too short, %u\n",
MBLKL(m)));
/*
* Buf not enough, do not need net80211 either
*/
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* Validate the command
*/
switch (cmd) {
case WLAN_SET_PARAM:
case WLAN_COMMAND:
break;
case WLAN_GET_PARAM:
break;
default:
"ipw2200_ioctl(): unknown cmd 0x%x", cmd));
/*
* Unknown cmd, do not need net80211 either
*/
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
if (need_privilege) {
/*
* Check for specific net_config privilege on Solaris 10+.
* Otherwise just check for root access ...
*/
if (secpolicy_net_config != NULL)
else
if (ret != 0) {
/*
* privilege check fail, do not need net80211 either
*/
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
}
/*
* sanity check
*/
/*
* invalid format, do not need net80211 either
*/
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* assuming single data block
*/
}
if (!need_net80211) {
"ipw2200_ioctl(): go to call miocack with "
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* IEEE80211_IOCTL - need net80211 handle
*/
return (IEEE80211_IOCTL_REQUIRED);
}
static int
{
int ret;
"ipw2200_getset(): id = 0x%x\n", id));
switch (id) {
case WL_RADIO: /* which is not supported by net80211 */
break;
case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */
break;
default:
/*
* The wifi IOCTL net80211 supported:
* case WL_ESSID:
* case WL_BSSID:
* case WL_WEP_KEY_TAB:
* case WL_WEP_KEY_ID:
* case WL_AUTH_MODE:
* case WL_ENCRYPTION:
* case WL_BSS_TYPE:
* case WL_ESS_LIST:
* case WL_LINKSTATUS:
* case WL_RSSI:
* case WL_SCAN:
* case WL_LOAD_DEFAULTS:
* case WL_DISASSOCIATE:
*/
return (0);
}
/*
* we will overwrite everything
*/
return (ret);
}
static int
{
switch (cmd) {
case WLAN_GET_PARAM:
ret = 0; /* command success */
break;
case WLAN_SET_PARAM:
default:
break;
}
return (ret);
}
static int
{
/* return success, but with result NOTSUPPORTED */
return (0);
}
/* End of IOCTL Handlers */
void
{
struct ieee80211_frame *wh;
return;
if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
return;
/*
* assume the message contains only 1 block
*/
if (*frm == IEEE80211_ELEMID_DSPARMS)
#if IEEE80211_CHAN_MAX < 255
#endif
}
}
static void
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
mblk_t *m;
int i;
/*
* Skip the frame header, get the real data from the input
*/
data += sizeof (struct ipw2200_frame);
if ((len < sizeof (struct ieee80211_frame_min)) ||
(len > IPW2200_RXBUF_SIZE)) {
"ipw2200_rcv_frame(): bad frame length=%u\n",
return;
}
if (m) {
/*
* h/w decyption leaves the WEP bit, iv and CRC fields
*/
for (i = sizeof (struct ieee80211_frame) - 1;
i >= 0; i--)
*(m->b_rptr + IEEE80211_WEP_IVLEN +
IEEE80211_WEP_KIDLEN + i) =
*(m->b_rptr + i);
m->b_wptr -= IEEE80211_WEP_CRCLEN;
}
ipw2200_fix_channel(ic, m);
}
"ipw2200_rcv_frame(): "
"ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n",
}
else
"ipw2200_rcv_frame(): "
"cannot allocate receive message(%u)\n",
}
static void
{
struct ipw2200_notif_association *assoc;
struct ipw2200_notif_authentication *auth;
ndata += sizeof (struct ipw2200_notif);
"ipw2200_rcv_notif(): association=%u,%u\n",
case IPW2200_ASSOC_SUCCESS:
break;
case IPW2200_ASSOC_FAIL:
break;
default:
break;
}
break;
case IPW2200_AUTH_SUCCESS:
break;
case IPW2200_AUTH_FAIL:
break;
default:
"ipw2200_rcv_notif(): "
break;
}
break;
"ipw2200_rcv_notif(): scan-channel=%u\n",
break;
"ipw2200_rcv_notif():scan-completed,(%u,%u)\n",
/*
* scan complete
*/
break;
case IPW2200_NOTIF_TYPE_NOISE:
/*
* just ignore
*/
break;
default:
"ipw2200_rcv_notif(): unknown notification type(%u)\n",
break;
}
}
static uint_t
{
struct dma_region *dr;
struct ipw2200_hdr *hdr;
int need_sched;
if (ireg == 0xffffffff)
return (DDI_INTR_UNCLAIMED);
if (!(ireg & IPW2200_INTR_MASK_ALL))
return (DDI_INTR_UNCLAIMED);
/*
* mask all interrupts
*/
/*
* acknowledge all fired interrupts
*/
"ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg));
if (ireg & IPW2200_INTR_MASK_ERR) {
"ipw2200 interrupt(): int= 0x%08x\n", ireg));
/*
* inform mfthread to recover hw error by stopping it
*/
} else {
if (ireg & IPW2200_INTR_FW_INITED) {
}
if (ireg & IPW2200_INTR_RADIO_OFF) {
"ipw2200_intr(): radio is OFF\n"));
/*
* Stop hardware, will notify LINK is down
*/
}
if (ireg & IPW2200_INTR_CMD_TRANSFER) {
"ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n",
if (len > 0) {
}
for (; i != ridx;
}
if (ireg & IPW2200_INTR_RX_TRANSFER) {
"ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n",
/*
* DMA sync
*/
/*
* Get rx header(hdr) and rx data(p) from rxbuf
*/
p = rxbuf;
hdr = (struct ipw2200_hdr *)p;
p += sizeof (struct ipw2200_hdr);
"ipw2200_intr(): Rx hdr type %u\n",
case IPW2200_HDR_TYPE_FRAME:
(struct ipw2200_frame *)p);
break;
case IPW2200_HDR_TYPE_NOTIF:
(struct ipw2200_notif *)p);
break;
default:
"ipw2200_intr(): "
"unknown Rx hdr type %u\n",
break;
}
}
/*
* write sc_rx_cur backward 1 step into RX_WRITE_INDEX
*/
}
if (ireg & IPW2200_INTR_TX1_TRANSFER) {
"ipw2200_intr(): tx-ring,ridx=%u,len=%u\n",
need_sched = 0;
"ipw2200_intr(): Need Reschedule!"));
need_sched = 1;
}
if (need_sched)
}
}
/*
* enable all interrupts
*/
return (DDI_INTR_CLAIMED);
}
/*
* Module Loading Data & Entry Points
*/
static struct modldrv ipw2200_modldrv = {
};
static struct modlinkage ipw2200_modlinkage = {
};
int
_init(void)
{
int status;
sizeof (struct ipw2200_softc), 1);
if (status != DDI_SUCCESS)
return (status);
if (status != DDI_SUCCESS) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
int
{
}