ipw2100.c revision 0778188f242b11e5d53f771c9e8a069354b3d5d4
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright(c) 2004
* 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.
*/
#include <sys/byteorder.h>
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include "ipw2100.h"
#include "ipw2100_impl.h"
#include <inet/wifi_ioctl.h>
/*
* kCF framework include files
*/
static void *ipw2100_ssp = NULL;
static char ipw2100_ident[] = IPW2100_DRV_DESC;
/*
* PIO access attribute for register
*/
static ddi_device_acc_attr_t ipw2100_csr_accattr = {
};
static ddi_device_acc_attr_t ipw2100_dma_accattr = {
};
static ddi_dma_attr_t ipw2100_dma_attr = {
0x0000000000000000ULL,
0x00000000ffffffffULL,
0x00000000ffffffffULL,
0x0000000000000004ULL,
0xfff,
1,
0x00000000ffffffffULL,
0x00000000ffffffffULL,
1,
1,
0
};
{2, 4, 11, 22}
};
/*
* For mfthread only
*/
extern pri_t minclsyspri;
/*
* ipw2100 specific hardware operations
*/
/*
* GLD specific operations
*/
static int ipw2100_m_start(void *arg);
static void ipw2100_m_stop(void *arg);
static void ipw2100_m_propinfo(void *, const char *, mac_prop_id_t,
/*
* Interrupt and Data transferring operations
*/
/*
* WiFi specific operations
*/
/*
* IOCTL Handler
*/
/*
* Suspend / Resume operations
*/
/*
* Mac Call Back entries
*/
NULL,
NULL,
NULL,
NULL,
};
/*
* DEBUG Facility
*/
#define MAX_MSG (128)
uint32_t ipw2100_debug = 0;
/*
* supported debug marsks:
* | IPW2100_DBG_INIT
* | IPW2100_DBG_GLD
* | IPW2100_DBG_TABLE
* | IPW2100_DBG_SOFTINT
* | IPW2100_DBG_CSR
* | IPW2100_DBG_INT
* | IPW2100_DBG_FW
* | IPW2100_DBG_IOCTL
* | IPW2100_DBG_HWCAP
* | IPW2100_DBG_STATISTIC
* | IPW2100_DBG_RING
* | IPW2100_DBG_WIFI
* | IPW2100_DBG_BRUSSELS
*/
/*
* global tuning parameters to work around unknown hardware issues
*/
void
{
int instance;
if (dip) {
} else
}
/*
* device operations
*/
int
{
struct ipw2100_softc *sc;
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
err = DDI_FAILURE;
goto fail1;
}
return (ipw2100_cpr_resume(sc));
default:
err = DDI_FAILURE;
goto fail1;
}
if (err != DDI_SUCCESS) {
"ipw2100_attach(): unable to allocate soft state\n"));
goto fail1;
}
/*
* Map config spaces register
*/
0, 0, &ipw2100_csr_accattr, &cfgh);
if (err != DDI_SUCCESS) {
"ipw2100_attach(): unable to map spaces regs\n"));
goto fail2;
}
/*
* Map operating registers
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): unable to map device regs\n"));
goto fail2;
}
/*
* Reset the chip
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): reset failed\n"));
goto fail3;
}
/*
* Get the hw conf, including MAC address, then init all rings.
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): "
"unable to allocate and initialize rings\n"));
goto fail3;
}
/*
* Initialize mutexs and condvars
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): ddi_get_iblock_cookie() failed\n"));
goto fail4;
}
/*
* interrupt lock
*/
/*
* tx ring lock
*/
/*
* rescheuled lock
*/
/*
* initialize the mfthread
*/
sc->sc_mfthread_switch = 0;
/*
* Initialize the wifi part, which will be used by
* generic layer
*/
/*
* Future, could use s/w to handle encryption: IEEE80211_C_WEP
* and need to add support for IEEE80211_C_IBSS
*/
for (i = 1; i < 16; i++) {
/* IEEE80211_CHAN_B */
}
}
/*
* init Wifi layer
*/
/*
* Override 80211 default routines
*/
/*
* initialize default tx key
*/
ic->ic_def_txkey = 0;
/*
* Set the Authentication to AUTH_Open only.
*/
/*
* Add the interrupt handler
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): ddi_add_intr() failed\n"));
goto fail5;
}
/*
* Initialize pointer to device specific functions
*/
if (err != 0) {
"ipw2100_attach(): mac_alloc() failed\n"));
goto fail6;
}
/*
* Register the macp to mac
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): mac_register() failed\n"));
goto fail6;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
if (err != DDI_SUCCESS)
"ipw2100_attach(): ddi_create_minor_node() failed\n"));
/*
* Cache firmware, always return true
*/
(void) ipw2100_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 ipw2100_softc *sc =
int err;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (ipw2100_cpr_suspend(sc));
default:
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);
/*
* destroy the cv
*/
/*
* detach ieee80211
*/
(void) ipw2100_free_firmware(sc);
return (DDI_SUCCESS);
}
int
{
"ipw2100_cpr_suspend(): enter\n"));
/*
* Destroy the mf_thread
*/
sc->sc_mfthread_switch = 0;
break;
}
/*
* stop the hardware; this mask all interrupts
*/
(void) ipw2100_free_firmware(sc);
return (DDI_SUCCESS);
}
int
{
int err;
"ipw2100_cpr_resume(): enter\n"));
/*
* Reset the chip
*/
if (err != DDI_SUCCESS) {
"ipw2100_attach(): reset failed\n"));
return (DDI_FAILURE);
}
/*
* Get the hw conf, including MAC address, then init all rings.
*/
/* ipw2100_hwconf_get(sc); */
if (err != DDI_SUCCESS) {
"ipw2100_attach(): "
"unable to allocate and initialize rings\n"));
return (DDI_FAILURE);
}
/*
* Cache firmware, always return true
*/
(void) ipw2100_cache_firmware(sc);
/*
* Notify link is down now
*/
/*
* create the mf thread to handle the link status,
* recovery fatal error, etc.
*/
/*
* enable all interrupts
*/
sc->sc_suspended = 0;
/*
* initialize ipw2100 hardware
*/
(void) ipw2100_init(sc);
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.
* Contributed by Juergen Keil, <jk@tools.de>.
*/
static int
{
struct ipw2100_softc *sc =
return (DDI_FAILURE);
/*
* No more blocking is allowed while we are in the
* quiesce(9E) entry point.
*/
/*
* Disable and mask all interrupts.
*/
return (DDI_SUCCESS);
}
static void
{
}
static void
{
}
static int
{
struct ipw2100_security sec;
struct ipw2100_wep_key wkey;
struct ipw2100_scan_options sopt;
struct ipw2100_configuration cfg;
int err, i;
/*
* operation mode
*/
case IEEE80211_M_STA:
case IEEE80211_M_HOSTAP:
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
break;
}
if (err != DDI_SUCCESS)
return (err);
/*
* operation channel if IBSS or MONITOR
*/
if (err != DDI_SUCCESS)
return (err);
}
/*
* set MAC address
*/
"ipw2100_config(): Setting MAC address to "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
if (err != DDI_SUCCESS)
return (err);
/*
* configuration capabilities
*/
"ipw2100_config(): Setting configuration to 0x%x\n",
if (err != DDI_SUCCESS)
return (err);
/*
* set 802.11 Tx rates
*/
"ipw2100_config(): Setting 802.11 Tx rates to 0x%x\n",
if (err != DDI_SUCCESS)
return (err);
/*
* set 802.11b Tx rates
*/
"ipw2100_config(): Setting 802.11b Tx rates to 0x%x\n",
if (err != DDI_SUCCESS)
return (err);
/*
* set power mode
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set power index
*/
"ipw2100_config(): Setting Tx power index to %u\n",
if (err != DDI_SUCCESS)
return (err);
}
/*
* set RTS threshold
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set frag threshold
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set ESSID
*/
"ipw2100_config(): Setting ESSID to %u, ESSID[0]%c\n",
if (err != DDI_SUCCESS)
return (err);
/*
* no mandatory BSSID
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set BSSID, if any
*/
"ipw2100_config(): Setting BSSID to %u\n",
if (err != DDI_SUCCESS)
return (err);
}
/*
* set security information
*/
/*
* use the value set to ic_bss to retrieve current sharedmode
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set WEP if any
*/
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
continue;
if (err != DDI_SUCCESS)
return (err);
}
if (err != DDI_SUCCESS)
return (err);
}
/*
* turn on WEP
*/
if (err != DDI_SUCCESS)
return (err);
/*
* set beacon interval if IBSS or HostAP
*/
"ipw2100_config(): Setting beacon interval to %u\n",
if (err != DDI_SUCCESS)
return (err);
}
/*
* set scan options
*/
if (err != DDI_SUCCESS)
return (err);
"ipw2100_config(): Enabling adapter\n"));
}
static int
{
struct ipw2100_bd *txbd;
/*
* prepare command buffer
*/
/*
* copy data if any
*/
/*
* get host & device descriptor to submit command
*/
/*
* command need 1 descriptor
*/
}
"ipw2100_cmd(): tx-cur=%d\n", idx));
/*
* sync for device
*/
sizeof (struct ipw2100_cmd), DDI_DMA_SYNC_FORDEV);
idx * sizeof (struct ipw2100_bd),
sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
/*
* ring move forward
*/
sc->sc_tx_free--;
/*
* wait for command done
*/
/*
* pending for the response
*/
clk, TR_CLOCK_TICK) < 0)
break;
}
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
int
{
int err;
"ipw2100_init(): enter\n"));
/*
* no firmware is available, return fail directly
*/
"ipw2100_init(): no firmware is available\n"));
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
"ipw2100_init(): could not reset adapter\n"));
goto fail;
}
/*
* load microcode
*/
"ipw2100_init(): loading microcode\n"));
if (err != DDI_SUCCESS) {
"ipw2100_init(): could not load microcode, try again\n"));
goto fail;
}
/*
* load firmware
*/
"ipw2100_init(): loading firmware\n"));
if (err != DDI_SUCCESS) {
"ipw2100_init(): could not load firmware, try again\n"));
goto fail;
}
/*
* initialize tables
*/
/*
* Hardware will be enabled after configuration
*/
if (err != DDI_SUCCESS) {
"ipw2100_init(): device configuration failed\n"));
goto fail;
}
return (DDI_SUCCESS);
fail:
return (err);
}
/*
* get hardware configurations from EEPROM embedded within chip
*/
static void
{
int i;
/*
* MAC address
*/
i = 0;
/*
* formatted MAC address string
*/
"%02x:%02x:%02x:%02x:%02x:%02x",
/*
* channel mask
*/
if (val == 0)
val = 0x7ff;
/*
* radio switch
*/
if (val & 0x08)
"ipw2100_hwconf_get(): has-radio-switch=%s(%u)\n",
val));
}
/*
* all ipw2100 interrupts will be masked by this routine
*/
static void
{
int ntries;
/*
* disable interrupts
*/
break;
drv_usecwait(10);
}
"ipw2100_master_stop(): timeout when stop master\n"));
}
/*
* all ipw2100 interrupts will be masked by this routine
*/
static int
{
int ntries;
/*
* move adapter to DO state
*/
/*
* wait for clock stabilization
*/
break;
drv_usecwait(200);
}
if (ntries == 1000)
return (DDI_FAILURE);
drv_usecwait(10);
return (DDI_SUCCESS);
}
/*
* get the radio status from IPW_CSR_IO, invoked by wificonfig/dladm
*/
int
{
return (0);
else
return (1);
}
/*
* This function is used to get the statistic, invoked by wificonfig/dladm
*/
void
{
"ipw2100_get_statistic(): fw doesn't download yet."));
return;
}
}
/*
* To retrieve the statistic information into proper places. There are
* lot of information.
*/
"ipw2100_get_statistic(): \n"
"operating mode = %u\n"
"type of authentification= %u\n"
"average RSSI= %u\n"
"current channel = %d\n",
/* WIFI_STAT_TX_FRAGS */
/* WIFI_STAT_MCAST_TX = (all frame - unicast frame) */
/* WIFI_STAT_TX_RETRANS */
/* WIFI_STAT_TX_FAILED */
/* MAC_STAT_OBYTES */
/* WIFI_STAT_RX_FRAGS */
/* WIFI_STAT_MCAST_RX */
/* MAC_STAT_IBYTES */
/* WIFI_STAT_ACK_FAILURE */
/* WIFI_STAT_RTS_SUCCESS */
}
/*
* dma region alloc
*/
static int
{
int err;
"ipw2100_dma_region_alloc() name=%s size=%u\n",
if (err != DDI_SUCCESS) {
"ipw2100_dma_region_alloc(): "
"ddi_dma_alloc_handle() failed\n"));
goto fail0;
}
if (err != DDI_SUCCESS) {
"ipw2100_dma_region_alloc(): "
"ddi_dma_mem_alloc() failed\n"));
goto fail1;
}
if (err != DDI_DMA_MAPPED) {
"ipw2100_dma_region_alloc(): "
"ddi_dma_addr_bind_handle() failed\n"));
goto fail2;
}
err = DDI_FAILURE;
goto fail3;
}
"ipw2100_dma_region_alloc(): get physical-base=0x%08x\n",
return (DDI_SUCCESS);
return (err);
}
static void
{
}
static int
{
int err, i;
/*
* tx ring
*/
if (err != DDI_SUCCESS)
goto fail0;
/*
* tx bufs
*/
for (i = 0; i < IPW2100_NUM_TXBUF; i++) {
if (err != DDI_SUCCESS) {
while (i > 0) {
i--;
}
goto fail1;
}
}
/*
* rx ring
*/
if (err != DDI_SUCCESS)
goto fail2;
/*
* rx bufs
*/
for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
if (err != DDI_SUCCESS) {
while (i > 0) {
i--;
}
goto fail3;
}
}
/*
* status
*/
if (err != DDI_SUCCESS)
goto fail4;
/*
* command
*/
if (err != DDI_SUCCESS)
goto fail5;
return (DDI_SUCCESS);
for (i = 0; i < IPW2100_NUM_RXBUF; i++)
for (i = 0; i < IPW2100_NUM_TXBUF; i++)
return (err);
}
static void
{
int i;
/*
* tx ring
*/
/*
* tx buf
*/
for (i = 0; i < IPW2100_NUM_TXBUF; i++)
/*
* rx ring
*/
/*
* rx buf
*/
for (i = 0; i < IPW2100_NUM_RXBUF; i++)
/*
* status
*/
/*
* command
*/
}
static void
{
int i;
/*
* tx ring
*/
for (i = 0; i < IPW2100_NUM_TXBUF; i++)
/*
* rx ring
*/
for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
/*
* initialize Rx buffer descriptors, both host and device
*/
}
/*
* command
*/
}
/*
* tx, rx rings and command initialization
*/
static int
{
int err;
if (err != DDI_SUCCESS)
return (err);
return (DDI_SUCCESS);
}
static void
{
/*
* tx ring
*/
/*
* no new packet to transmit, tx-rd-index == tx-wr-index
*/
/*
* rx ring
*/
/*
* all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
*/
"ipw2100_ring_hwsetup(): rx-cur=%u, backward=%u\n",
/*
* status
*/
}
/*
* ieee80211_new_state() is not be used, since the hardware can handle the
* state transfer. Here, we just keep the status of the hardware notification
* result.
*/
/* ARGSUSED */
static int
{
struct ieee80211_node *in;
wifi_data_t wd = { 0 };
"ipw2100_newstate(): %s -> %s\n",
switch (state) {
case IEEE80211_S_RUN:
/*
* we only need to use BSSID as to find the node
*/
break;
/*
* We can send data now; update the fastpath with our
* current associated BSSID.
*/
else
break;
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
break;
}
/*
* notify to update the link
*/
/*
* previously disconnected and now connected
*/
(state != IEEE80211_S_RUN)) {
/*
* previously connected andd now disconnected
*/
}
return (DDI_SUCCESS);
}
/*
* GLD operations
*/
/* ARGSUSED */
static int
{
"ipw2100_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, retrieve net80211 value once 1s
*/
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_TX_FAILED:
case WIFI_STAT_TX_RETRANS:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
/*
* Get blow information from net80211
*/
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_RX_DUPS:
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
/*
* need be supported in the future
*/
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 */
"ipw2100_m_multicst(): enter\n"));
return (0);
}
/*
* This thread function is used to handle the fatal error.
*/
static void
{
int stat_cnt = 0;
"ipw2100_thread(): into ipw2100 thread--> %d\n",
sc->sc_linkstate));
while (sc->sc_mfthread_switch) {
/*
* notify the link state
*/
"ipw2100_thread(): link status --> %d\n",
sc->sc_linkstate));
}
/*
* recovery interrupt fatal error
*/
"try to recover fatal hw error\n"));
}
/*
* 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
ipw2100_m_start(void *arg)
{
"ipw2100_m_start(): enter\n"));
/*
* initialize ipw2100 hardware
*/
(void) ipw2100_init(sc);
/*
* fix KCF bug. - workaround, need to fix it in net80211
*/
(void) crypto_mech2id(SUN_CKM_RC4);
return (0);
}
static void
ipw2100_m_stop(void *arg)
{
"ipw2100_m_stop(): enter\n"));
}
static int
{
int err;
"ipw2100_m_unicst(): enter\n"));
"ipw2100_m_unicst(): GLD setting MAC address to "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
if (err != DDI_SUCCESS) {
"ipw2100_m_unicst(): "
"device configuration failed\n"));
goto fail;
}
}
}
return (0);
fail:
return (EIO);
}
static int
{
"ipw2100_m_promisc(): enter. "
"GLD setting promiscuous mode - %d\n", on));
recfg = 0;
if (on)
recfg = 1;
}
else
recfg = 1;
}
if (err != DDI_SUCCESS) {
"ipw2100_m_promisc(): "
"device configuration failed\n"));
goto fail;
}
}
return (0);
fail:
return (EIO);
}
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.
*/
"ipw2100_m_tx(): discard msg, ic_state = %u\n",
return (NULL);
}
DDI_SUCCESS) {
break;
}
}
return (mp);
}
/* ARGSUSED */
static int
{
struct ieee80211_node *in;
struct ieee80211_key *k;
struct ipw2100_txb *txbuf;
struct dma_region *dr;
struct ipw2100_hdr *h;
int err;
m = NULL;
err = DDI_SUCCESS;
"ipw2100_send(): enter\n"));
/*
* it is impossible to send non-data 802.11 frame in current
* ipw driver. Therefore, drop the package
*/
err = DDI_SUCCESS;
goto fail0;
}
/*
* need 2 descriptors: 1 for SEND cmd parameter header,
* and the other for payload, i.e., 802.11 frame including 802.11
* frame header
*/
"ipw2100_send(): no enough descriptors(%d)\n",
sc->sc_tx_free));
err = DDI_FAILURE;
goto fail1;
}
"ipw2100_send(): tx-free=%d,tx-curr=%d\n",
err = DDI_SUCCESS;
goto fail1;
}
/*
* it is very bad that ieee80211_crypto_encap can only accept a
* single continuous buffer.
*/
/*
* allocate 32 more bytes is to be compatible with further
* ieee802.11i standard.
*/
if (m == NULL) { /* can not alloc buf, drop this package */
"ipw2100_send(): msg allocation failed\n"));
err = DDI_SUCCESS;
goto fail1;
}
off = 0;
while (m0) {
if (cnt) {
}
}
"ipw2100_send(): "
"Encrypting 802.11 frame started, %d, %d\n",
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) { /* can not get the key, drop packages */
"ipw2100_send(): "
"Encrypting 802.11 frame failed\n"));
err = DDI_SUCCESS;
goto fail2;
}
"ipw2100_send(): "
"Encrypting 802.11 frame finished, %d, %d, k=0x%08x\n",
}
/*
* header descriptor
*/
if ((idx & 1) == 0)
sc->sc_tx_free--;
/*
* payload descriptor
*/
if ((idx & 1) == 0)
sc->sc_tx_free--;
/*
* one buffer, SEND cmd header and payload buffer
*/
/*
* extract 802.11 header from message, fill wh from m0
*/
off = 0;
if (m)
m0 = m;
else
if (cnt) {
}
else
}
/*
* prepare SEND cmd header
*/
h->encrypt = 0;
h->keyidx = 0;
h->keysz = 0;
else
/*
* extract payload from message into tx data buffer
*/
off = 0;
while (m0) {
if (cnt) {
}
}
/*
* fill SEND cmd header descriptor
*/
/*
* fill payload descriptor
*/
/*
* dma sync
*/
sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
/*
* since txbd[1] may not be successive to txbd[0] due to the ring
* organization, another dma_sync is needed to simplify the logic
*/
sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
/*
* update txcur
*/
if (mp) /* success, free the original message */
if (m)
freemsg(m);
"ipw2100_send(): exit - err=%d\n", err));
return (err);
}
/*
* IOCTL Handler
*/
#define IEEE80211_IOCTL_REQUIRED (1)
#define IEEE80211_IOCTL_NOT_REQUIRED (0)
static void
{
int err;
"ipw2100_m_ioctl(): enter\n"));
/*
* check whether or not need to handle this in net80211
*/
return; /* succes or fail */
(void) ipw2100_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
(void) ipw2100_chip_reset(sc);
}
}
static int
{
"ipw2100_ioctl(): ioctl buffer too short, %u\n",
MBLKL(m)));
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* Validate the command
*/
switch (cmd) {
case WLAN_SET_PARAM:
case WLAN_COMMAND:
break;
case WLAN_GET_PARAM:
break;
default:
"ieee80211_ioctl(): unknown cmd 0x%x", cmd));
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* sanity check
*/
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* assuming single data block
*/
}
if (!need_net80211) {
"ipw2100_ioctl(): go to call miocack with "
return (IEEE80211_IOCTL_NOT_REQUIRED);
}
/*
* IEEE80211_IOCTL_REQUIRED - need net80211 handle
*/
return (IEEE80211_IOCTL_REQUIRED);
}
static int
{
int ret; /* IEEE80211_IOCTL - handled by net80211 */
"ipw2100_getset(): id = 0x%x\n", id));
switch (id) {
/*
* which is not supported by net80211, so it
* has to be handled from driver side
*/
case WL_RADIO:
break;
/*
* so far, drier doesn't support fix-rates
*/
case WL_DESIRED_RATES:
break;
/*
* current net80211 implementation clears the bssid while
* this command received, which will result in the all zero
* mac address for scan'ed AP which is just disconnected.
* This is a workaround solution until net80211 find a
* better method.
*/
case WL_DISASSOCIATE:
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:
*/
/*
* When radio is off, need to ignore all ioctl. What need to
* do is to check radio status firstly. If radio is ON, pass
* it to net80211, otherwise, return to upper layer directly.
*
* Considering the WL_SUCCESS also means WL_CONNECTED for
* checking linkstatus, one exception for WL_LINKSTATUS is to
* let net80211 handle it.
*/
if ((ipw2100_get_radio(sc) == 0) &&
(id != WL_LINKSTATUS)) {
"ipw: RADIO is OFF\n"));
ret = 0;
break;
}
return (0);
}
/*
* we will overwrite everything
*/
return (ret);
}
/*
*/
static int
{
int err = 0;
switch (wldp_pr_num) {
/* mac_prop_id */
"ipw2100_m_getprop(): Not Support DESIRED_RATES\n"));
break;
case MAC_PROP_WL_RADIO:
break;
default:
/* go through net80211 */
break;
}
return (err);
}
static void
{
}
static int
{
int err;
switch (wldp_pr_num) {
/* mac_prop_id */
"ipw2100_m_setprop(): Not Support DESIRED_RATES\n"));
break;
case MAC_PROP_WL_RADIO:
"ipw2100_m_setprop(): Not Support RADIO\n"));
break;
default:
/* go through net80211 */
wldp_buf);
break;
}
(void) ipw2100_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
err = 0;
}
return (err);
}
static int
{
switch (cmd) {
case WLAN_GET_PARAM:
ret = 0; /* command sucess */
break;
case WLAN_SET_PARAM:
default:
break;
}
return (ret);
}
static int
{
/*
* return success, but with result NOTSUPPORTED
*/
return (0);
}
static int
{
/*
* init the state
*/
}
/*
* return success always
*/
return (0);
}
/* End of IOCTL Handler */
static 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
{
mblk_t *m;
struct ieee80211_node *in;
if (m) {
ipw2100_fix_channel(ic, m);
} else
"ipw2100_rcvpkg(): cannot allocate receive message(%u)\n",
}
static uint_t
{
struct ipw2100_status *status;
struct dma_region *dr;
#if DEBUG
struct ipw2100_bd *rxbd;
#endif
if (sc->sc_suspended)
return (DDI_INTR_UNCLAIMED);
if (!(ireg & IPW2100_INTR_MASK_ALL))
return (DDI_INTR_UNCLAIMED);
/*
* mask all interrupts
*/
/*
* acknowledge all fired interrupts
*/
"ipw2100_intr(): interrupt is fired. int=0x%08x\n", ireg));
if (ireg & IPW2100_INTR_MASK_ERR) {
"ipw2100_intr(): interrupt is fired, MASK = 0x%08x\n",
ireg));
/*
* inform mfthread to recover hw error
*/
goto enable_interrupt;
}
/*
* FW intr
*/
if (ireg & IPW2100_INTR_FW_INIT_DONE) {
}
/*
* RX intr
*/
if (ireg & IPW2100_INTR_RX_TRANSFER) {
/*
* sync
*/
i * sizeof (struct ipw2100_status),
sizeof (struct ipw2100_status),
i * sizeof (struct ipw2100_bd),
sizeof (struct ipw2100_bd),
sizeof (struct ipw2100_rxb),
"ipw2100_intr(): status code=0x%04x, len=0x%08x, "
"flags=0x%02x, rssi=%02x\n",
#if DEBUG
"ipw2100_intr(): rxbd,phyaddr=0x%08x, len=0x%08x, "
"flags=0x%02x,nfrag=%02x\n",
#endif
/*
* command complete response
*/
break;
/*
* change state
*/
"ipw2100_intr(): newstate,state=0x%x\n",
state));
switch (state) {
case IPW2100_STATE_ASSOCIATED:
IEEE80211_S_RUN, -1);
break;
case IPW2100_STATE_DISABLED:
IEEE80211_S_INIT, -1);
break;
/*
* When radio is OFF, need a better
* scan approach to ensure scan
* result correct.
*/
"ipw2100_intr(): RADIO is OFF\n"));
break;
break;
case IPW2100_STATE_SCANNING:
IEEE80211_S_SCAN, -1);
break;
default:
break;
}
break;
break;
break;
default:
"ipw2100_intr(): "
"unknown status code 0x%04x\n",
break;
}
}
/*
* write sc_rx_cur backward 1 step to RX_WRITE_INDEX
*/
}
/*
* TX intr
*/
if (ireg & IPW2100_INTR_TX_TRANSFER) {
"ipw2100_intr(): len=%d\n", len));
}
}
/*
* enable all interrupts
*/
return (DDI_INTR_CLAIMED);
}
/*
* Module Loading Data & Entry Points
*/
static struct modldrv ipw2100_modldrv = {
};
static struct modlinkage ipw2100_modlinkage = {
};
int
_init(void)
{
int status;
sizeof (struct ipw2100_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
{
}