/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2006
* Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* 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/byteorder.h>
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#include <sys/net80211_proto.h>
#include "wpireg.h"
#include "wpivar.h"
#include <inet/wifi_ioctl.h>
#ifdef DEBUG
#define WPI_DBG(x) \
wpi_dbg x
#else
#define WPI_DBG(x)
#endif
#include "fw-wpi/ipw3945.ucode.hex"
};
/* DMA attributes for a shared page */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0xffffffffU, /* maximum DMAable byte count */
0x1000, /* alignment in bytes */
0x1000, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA attributes for a ring descriptor */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0xffffffffU, /* maximum DMAable byte count */
0x4000, /* alignment in bytes */
0x100, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA attributes for a tx cmd */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0xffffffffU, /* maximum DMAable byte count */
4, /* alignment in bytes */
0x100, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA attributes for a rx buffer */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0xffffffffU, /* maximum DMAable byte count */
1, /* alignment in bytes */
0x100, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/*
* DMA attributes for a tx buffer.
* the maximum number of segments is 4 for the hardware.
* now all the wifi drivers put the whole frame in a single
* descriptor, so we define the maximum number of segments 4,
* just the same as the rx_buffer. we consider leverage the HW
* ability in the future, that is why we don't define rx and tx
* buffer_dma_attr as the same.
*/
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0xffffffffU, /* maximum DMAable byte count */
1, /* alignment in bytes */
0x100, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA attributes for a load firmware */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0x7fffffff, /* maximum DMAable byte count */
4, /* alignment in bytes */
0x100, /* burst sizes (any?) */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffU, /* maximum segment length */
4, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* regs access attributes */
};
/* DMA access attributes */
};
static int wpi_ring_init(wpi_sc_t *);
static void wpi_ring_free(wpi_sc_t *);
static int wpi_alloc_shared(wpi_sc_t *);
static void wpi_free_shared(wpi_sc_t *);
static int wpi_alloc_fw_dma(wpi_sc_t *);
static void wpi_free_fw_dma(wpi_sc_t *);
static int wpi_alloc_rx_ring(wpi_sc_t *);
static void wpi_reset_rx_ring(wpi_sc_t *);
static void wpi_free_rx_ring(wpi_sc_t *);
static void wpi_node_free(ieee80211_node_t *);
static void wpi_mem_lock(wpi_sc_t *);
static void wpi_mem_unlock(wpi_sc_t *);
const uint32_t *, int);
static int wpi_load_microcode(wpi_sc_t *);
wpi_rx_data_t *);
wpi_rx_data_t *);
static uint8_t wpi_plcp_signal(int);
static void wpi_read_eeprom(wpi_sc_t *);
static int wpi_mrr_setup(wpi_sc_t *);
static int wpi_config(wpi_sc_t *);
static void wpi_stop_master(wpi_sc_t *);
static int wpi_power_up(wpi_sc_t *);
static void wpi_hw_config(wpi_sc_t *);
static void wpi_amrr_init(wpi_amrr_t *);
static void wpi_amrr_timeout(wpi_sc_t *);
static void wpi_amrr_ratectl(void *, ieee80211_node_t *);
/*
* GLD specific operations
*/
static int wpi_m_start(void *arg);
static void wpi_m_stop(void *arg);
/*
* Supported rates for 802.11a/b/g modes (in 500Kbps unit).
*/
{ 4, { 2, 4, 11, 22 } };
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
/* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */
0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3,
/* CCK: device-dependent */
10, 20, 55, 110
};
/*
* For mfthread only
*/
extern pri_t minclsyspri;
/*
* Module Loading Data & Entry Points
*/
};
};
int
_init(void)
{
int status;
sizeof (wpi_sc_t), 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
{
}
/*
* Mac Call Back entries
*/
NULL,
NULL,
NULL,
NULL,
};
#ifdef DEBUG
void
{
if (flags & wpi_dbg_flags) {
}
}
#endif
/*
* device operations
*/
int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
err = DDI_FAILURE;
goto attach_fail1;
}
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to allocate soft state\n");
goto attach_fail1;
}
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to map config spaces regs\n");
goto attach_fail2;
}
/*
* Map operating registers
*/
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to map device regs\n");
goto attach_fail2;
}
/*
* Allocate shared page.
*/
if (err != DDI_SUCCESS) {
goto attach_fail3;
}
/*
* Get the hw conf, including MAC address, then init all rings.
*/
if (err != DDI_SUCCESS) {
"failed to allocate and initialize ring\n");
goto attach_fail4;
}
/* firmware image layout: |HDR|<--TEXT-->|<--DATA-->|<--BOOT-->| */
if (err != DDI_SUCCESS) {
"failed to allocate firmware dma\n");
goto attach_fail5;
}
/*
* Initialize mutexs and condvars
*/
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to do ddi_get_iblock_cookie()\n");
goto attach_fail6;
}
/*
* initialize the mfthread
*/
sc->sc_mf_thread_switch = 0;
/*
* Initialize the wifi part, which will be used by
* generic layer
*/
/*
* use software WEP and TKIP, hardware CCMP;
*/
/* set supported .11b and .11g rates */
/* set supported .11b and .11g channels (1 through 14) */
for (i = 1; i <= 14; i++) {
}
/*
* init Wifi layer
*/
/* register WPA door */
/*
* Override 80211 default routines
*/
/*
* initialize default tx key
*/
ic->ic_def_txkey = 0;
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to do ddi_add_softintr()\n");
goto attach_fail7;
}
/*
* Add the interrupt handler
*/
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to do ddi_add_intr()\n");
goto attach_fail8;
}
/*
* Initialize pointer to device specific functions
*/
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to do mac_alloc()\n");
goto attach_fail9;
}
/*
* Register the macp to mac
*/
if (err != DDI_SUCCESS) {
"wpi_attach(): failed to do mac_register()\n");
goto attach_fail9;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
if (err != DDI_SUCCESS)
"wpi_attach(): failed to do ddi_create_minor_node()\n");
/*
* Notify link is down now
*/
/*
* create the mf thread to handle the link status,
* recovery fatal error, etc.
*/
return (DDI_SUCCESS);
return (err);
}
int
{
int err;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
if (err != DDI_SUCCESS)
return (err);
/*
* Destroy the mf_thread
*/
sc->sc_mf_thread_switch = 0;
break;
}
/*
* Unregiste from the MAC layer subsystem
*/
/*
* detach ieee80211
*/
return (DDI_SUCCESS);
}
static void
{
}
/*
* 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
{
}
}
}
}
/*
* Allocate an area of dma memory for firmware load.
* Idealy, this allocation should be a one time action, that is,
* the memory will be freed after the firmware is uploaded to the
* card. but since a recovery mechanism for the fatal firmware need
* reload the firmware, and re-allocate dma at run time may be failed,
* so we allocate it at attach and keep it in the whole lifecycle of
* the driver.
*/
static int
{
&sc->sc_dma_fw_text);
if (err != DDI_SUCCESS) {
"text dma memory");
goto fail;
}
}
&sc->sc_dma_fw_data);
if (err != DDI_SUCCESS) {
"data dma memory");
goto fail;
}
}
fail:
return (err);
}
static void
{
}
/*
* Allocate a shared page between host and NIC.
*/
static int
{
/* must be aligned on a 4K-page boundary */
if (err != DDI_SUCCESS)
goto fail;
return (err);
fail:
return (err);
}
static void
{
}
static int
{
if (err != DDI_SUCCESS) {
goto fail;
}
/*
* Allocate Rx buffers.
*/
for (i = 0; i < WPI_RX_RING_COUNT; i++) {
if (err != DDI_SUCCESS) {
"failed\n", i));
goto fail;
}
}
return (err);
fail:
return (err);
}
static void
{
int ntries;
break;
DELAY(1000);
}
if (ntries == 2000)
}
static void
{
int i;
for (i = 0; i < WPI_RX_RING_COUNT; i++) {
}
}
static int
{
if (err != DDI_SUCCESS) {
qid));
goto fail;
}
/* update shared page with ring's base address */
if (err != DDI_SUCCESS) {
qid));
goto fail;
}
/*
* Allocate Tx buffers.
*/
goto fail;
}
for (i = 0; i < count; i++) {
if (err != DDI_SUCCESS) {
"failed\n", i));
goto fail;
}
}
return (err);
fail:
return (err);
}
static void
{
int i, ntries;
break;
DELAY(10);
}
#ifdef DEBUG
}
#endif
}
}
}
/*ARGSUSED*/
static void
{
int i;
}
}
}
static int
{
for (i = 0; i < 4; i++) {
i);
if (err != DDI_SUCCESS)
goto fail;
}
if (err != DDI_SUCCESS)
goto fail;
if (err != DDI_SUCCESS)
goto fail;
if (err != DDI_SUCCESS)
goto fail;
return (err);
fail:
return (err);
}
static void
{
int i = 4;
while (--i >= 0) {
}
}
/* ARGSUSED */
static ieee80211_node_t *
{
}
static void
{
}
/*ARGSUSED*/
static int
{
switch (nstate) {
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_INIT:
{
sc->sc_scan_next = 0;
/* make the link LED blink while we're scanning */
/*
* clear association to receive beacons from all
* BSS'es
*/
"filter %x\n",
sizeof (wpi_config_t), 1);
if (err != WPI_SUCCESS) {
"could not clear association\n");
return (err);
}
/* add broadcast node to send probe request */
sizeof (node), 1);
if (err != WPI_SUCCESS) {
"could not add broadcast node\n");
return (err);
}
break;
}
case IEEE80211_S_SCAN:
/* step to next channel before actual FW scan */
"could not initiate scan\n");
}
return (err);
default:
break;
}
break;
case IEEE80211_S_AUTH:
if (ostate == IEEE80211_S_SCAN) {
}
/* reset state to handle reassociations correctly */
"could not send authentication request\n"));
return (err);
}
break;
case IEEE80211_S_RUN:
if (ostate == IEEE80211_S_SCAN) {
}
/* link LED blinks while monitoring */
break;
}
/* need setup beacon here */
}
/* update adapter's configuration */
sizeof (wpi_config_t), 1);
if (err != WPI_SUCCESS) {
"could not update configuration\n"));
return (err);
}
/* start automatic rate control */
/* set rate to some reasonable initial value */
while (i > 0 && IEEE80211_RATE(i) > 72)
i--;
} else {
}
/* link LED always on while associated */
break;
case IEEE80211_S_INIT:
break;
case IEEE80211_S_ASSOC:
break;
}
}
/*ARGSUSED*/
{
int err;
case IEEE80211_CIPHER_WEP:
case IEEE80211_CIPHER_TKIP:
return (1); /* sofeware do it. */
case IEEE80211_CIPHER_AES_CCM:
break;
default:
return (0);
}
if (IEEE80211_IS_MULTICAST(mac)) {
} else {
}
if (k->wk_flags & IEEE80211_KEY_XMIT) {
} else {
}
if (err != WPI_SUCCESS) {
"failed to update ap node\n");
return (0);
}
return (1);
}
/*
* Grab exclusive access to NIC memory.
*/
static void
{
int ntries;
/* spin until we actually get the lock */
break;
DELAY(10);
}
if (ntries == 1000)
}
/*
* Release lock on NIC memory.
*/
static void
{
}
static uint32_t
{
}
static void
{
}
static void
{
}
/*
* Read 16 bits from the EEPROM. We access EEPROM through the MAC instead of
* using the traditional bit-bang method.
*/
static uint16_t
{
int ntries;
break;
DELAY(10);
}
if (ntries == 10) {
return (0xdead);
}
return (val >> 16);
}
/*
* The firmware boot code is small and is intended to be copied directly into
* the NIC internal memory.
*/
static int
{
const char *ucode;
int size;
/* check that microcode size is a multiple of 4 */
if (size & 3)
return (EINVAL);
/* copy microcode image into NIC memory */
size);
/* run microcode */
return (WPI_SUCCESS);
}
/*
* The firmware text and data segments are transferred to the NIC using DMA.
* The driver just copies the firmware into DMA-safe memory and tells the NIC
* where to find it. Once the NIC has copied the firmware into its internal
* memory, we can free our local copy in the driver.
*/
static int
{
const char *fw;
int size;
/* only text and data here */
if (target == WPI_FW_TEXT) {
} else {
}
/* copy firmware image to DMA-safe memory */
/* make sure the adapter will get up-to-date values */
}
/* tell adapter where to copy image in its internal memory */
/* copy firmware descriptor into NIC memory */
/* wait while the adapter is busy copying the firmware */
break;
DELAY(1000);
}
if (ntries == 100) {
}
return (err);
}
/*ARGSUSED*/
static void
{
return;
}
return;
}
/*
* Discard Rx frames with bad CRC early
*/
return;
}
/* update Rx descriptor */
/* ring->desc[ring->cur] = LE_32(data->dma_data.cookie.dmac_address); */
#ifdef WPI_BPF
#ifndef WPI_CURRENT
#else
#endif
tap->wr_chan_freq =
tap->wr_chan_flags =
/* CCK rates */
/* OFDM rates */
/* unknown rate: should not happen */
}
}
#endif
/* grab a reference to the source node */
#ifdef DEBUG
if (wpi_dbg_flags & WPI_DEBUG_RX)
#endif
if (mp) {
/* send the frame to the 802.11 layer */
} else {
sc->sc_rx_nobuf++;
"wpi_rx_intr(): alloc rx buf failed\n"));
}
/* release node reference */
}
/*ARGSUSED*/
static void
{
/* wpi_tx_data_t *txdata = &ring->data[desc->idx]; */
"rate=%x duration=%d status=%x\n",
sc->sc_tx_retries++;
}
sc->sc_tx_timer = 0;
sc->sc_need_reschedule = 0;
}
}
static void
{
return; /* not a command ack */
}
}
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
"qid=%x idx=%d flags=%x type=%d len=%d\n",
case WPI_RX_DONE:
/* a 802.11 frame was received */
break;
case WPI_TX_DONE:
/* a 802.11 frame has been transmitted */
break;
case WPI_UC_READY:
{
/* the microcontroller is ready */
"microcode alive notification version %x "
"microcontroller initialization failed\n"));
}
break;
}
case WPI_STATE_CHANGED:
{
/*
* the radio button has to be pushed(OFF). It
* is considered as a hw error, the
* wpi_thread() tries to recover it after the
* button is pushed again(ON)
*/
"wpi: Radio transmitter is off\n");
IEEE80211_S_INIT, -1);
}
break;
}
case WPI_START_SCAN:
{
"scanning channel %d status %x\n",
break;
}
case WPI_STOP_SCAN:
{
"completed channel %d (burst of %d) status %02x\n",
sc->sc_scan_pending = 0;
sc->sc_scan_next++;
break;
}
default:
break;
}
}
/* tell the firmware what we have processed */
sc->sc_notif_softint_pending = 0;
return (DDI_INTR_CLAIMED);
}
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
if (r == 0 || r == 0xffffffff) {
return (DDI_INTR_UNCLAIMED);
}
/* disable interrupts */
/* ack interrupts */
return (DDI_INTR_CLAIMED);
}
if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) {
}
/* not capable of fast recovery */
if (!WPI_CHK_FAST_RECOVER(sc))
return (DDI_INTR_CLAIMED);
}
if ((r & (WPI_RX_INTR | WPI_RX_SWINT)) ||
(rfh & 0x40070000)) {
}
if (r & WPI_ALIVE_INTR) { /* firmware initialized */
}
/* re-enable interrupts */
return (DDI_INTR_CLAIMED);
}
static uint8_t
{
switch (rate) {
/* CCK rates (returned values are device-dependent) */
case 2: return (10);
case 4: return (20);
case 11: return (55);
case 22: return (110);
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
case 12: return (0xd);
case 18: return (0xf);
case 24: return (0x5);
case 36: return (0x7);
case 48: return (0x9);
case 72: return (0xb);
case 96: return (0x1);
case 108: return (0x3);
/* unsupported rates (should not get there) */
default: return (0);
}
}
static mblk_t *
{
return (NULL);
}
return (NULL);
}
return (mp);
}
break;
}
}
return (mp);
}
/* ARGSUSED */
static int
{
struct ieee80211_key *k;
if ((type & IEEE80211_FC0_TYPE_MASK) !=
}
goto exit;
}
if ((type & IEEE80211_FC0_TYPE_MASK) !=
}
sc->sc_tx_nobuf++;
goto exit;
}
hdrlen = sizeof (struct ieee80211_frame);
if (m == NULL) { /* can not alloc buf, drop this package */
"wpi_send(): failed to allocate msgbuf\n");
err = WPI_SUCCESS;
goto exit;
}
}
freemsg(m);
err = WPI_SUCCESS;
goto exit;
}
} else {
}
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
freemsg(m);
err = WPI_SUCCESS;
goto exit;
}
}
/* packet header may have moved, reset our local pointer */
}
#ifdef DEBUG
if (wpi_dbg_flags & WPI_DEBUG_TX)
#endif
/* pickup a rate */
/* mgmt frames are sent at the lowest available bit-rate */
rate = 2;
} else {
} else
}
#ifdef WPI_BPF
#ifndef WPI_CURRENT
#else
#endif
}
#endif
/* retrieve destination node's id */
/* tell h/w to set timestamp in probe responses */
else
} else
/* be very persistant at sending frames out */
/* save and trim IEEE802.11 header */
/* kick ring */
freemsg(m);
/* release node reference */
if (sc->sc_tx_timer == 0)
exit:
return (err);
}
static void
{
int err;
/*
* This is special for the hidden AP connection.
* In any case, we should make sure only one 'scan'
* in the driver for a 'connect' CLI command. So
* when connecting to a hidden AP, the scan is just
* sent out to the air when we know the desired
* essid of the AP we want to connect.
*/
if (ic->ic_des_esslen) {
wpi_m_stop(sc);
(void) wpi_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
}
}
/*
*/
/* ARGSUSED */
static int
{
int err = 0;
return (err);
}
static void
{
}
static int
{
int err;
if (ic->ic_des_esslen) {
wpi_m_stop(sc);
(void) wpi_m_start(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
err = 0;
}
return (err);
}
/*ARGSUSED*/
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 (WPI_SUCCESS);
}
static int
{
int err;
if (err != WPI_SUCCESS) {
DELAY(1000000);
}
if (err) {
/*
* The hw init err(eg. RF is OFF). Return Success to make
* the 'plumb' succeed. The wpi_thread() tries to re-init
* background.
*/
return (WPI_SUCCESS);
}
return (WPI_SUCCESS);
}
static void
{
}
/*ARGSUSED*/
static int
{
int err;
if (err != WPI_SUCCESS) {
"wpi_m_unicst(): "
"failed to configure device\n");
goto fail;
}
}
return (WPI_SUCCESS);
fail:
return (err);
}
/*ARGSUSED*/
static int
{
return (WPI_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (WPI_SUCCESS);
}
static void
{
while (sc->sc_mf_thread_switch) {
if (tmp & WPI_GPIO_HW_RF_KILL) {
} else {
}
/*
* If in SUSPEND or the RF is OFF, do nothing
*/
continue;
}
/*
* recovery fatal error
*/
"wpi_thread(): "
"try to recover fatal hw error: %d\n", times++));
if (WPI_CHK_FAST_RECOVER(sc)) {
/* save runtime configuration */
} else {
}
if (err != WPI_SUCCESS) {
n++;
if (n < 3)
continue;
}
n = 0;
if (!err)
if (!WPI_CHK_FAST_RECOVER(sc) ||
IEEE80211_S_SCAN, 0);
}
}
"wpi_thread(): "
"lazy resume\n"));
/*
* NB: under WPA mode, this call hangs (door problem?)
* when called in wpi_attach() and wpi_detach() while
* system is in the procedure of CPR. To be safe, let
* the thread do this.
*/
}
/*
* scan next channel
*/
"wpi_thread(): "
"wait for probe response\n"));
sc->sc_scan_next--;
}
/*
* rate ctl
*/
clk = ddi_get_lbolt();
}
}
if (sc->sc_tx_timer) {
timeout++;
if (timeout == 10) {
sc->sc_tx_timer--;
if (sc->sc_tx_timer == 0) {
"wpi_thread(): send fail\n"));
}
timeout = 0;
}
}
}
}
/*
* Extract various information from EEPROM.
*/
static void
{
int i;
/* read MAC address */
"mac:%2x:%2x:%2x:%2x:%2x:%2x\n",
/* read power settings for 2.4GHz channels */
for (i = 0; i < 14; i++) {
"channel %d pwr1 0x%04x pwr2 0x%04x\n", i + 1,
}
}
/*
* Send a command to the firmware.
*/
static int
{
/* kick cmd ring */
if (async)
return (WPI_SUCCESS);
else {
< 0)
break;
}
return (WPI_SUCCESS);
else
return (WPI_FAIL);
}
}
/*
* Configure h/w multi-rate retries.
*/
static int
{
int i, err;
/* CCK rates (not used with 802.11a) */
/* fallback to the immediate lower CCK rate (if any) */
/* try one time at this rate before falling back to "next" */
}
/* OFDM rates (not used with 802.11b) */
for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) {
/* fallback to the immediate lower OFDM rate (if any) */
/* try one time at this rate before falling back to "next" */
}
/* setup MRR for control frames */
if (err != WPI_SUCCESS) {
"could not setup MRR for control frames\n"));
return (err);
}
/* setup MRR for data frames */
if (err != WPI_SUCCESS) {
"could not setup MRR for data frames\n"));
return (err);
}
return (WPI_SUCCESS);
}
static void
{
}
static int
{
int err;
/* update adapter's configuration */
} else { /* assume 802.11b/g */
}
" bssid:%02x:%02x:%02x:%02x:%02x:%2x\n",
sizeof (wpi_config_t), 1);
if (err != WPI_SUCCESS) {
return (err);
}
/* add default node */
if (err != WPI_SUCCESS) {
return (err);
}
if (err != WPI_SUCCESS) {
return (err);
}
return (WPI_SUCCESS);
}
/*
* Send a scan request to the firmware.
*/
static int
{
/* previous scan not completed */
if (sc->sc_scan_pending) {
return (WPI_SUCCESS);
}
if (ic->ic_des_esslen) {
} else {
}
/*
* Build a probe request frame. Most of the following code is a
* copy & paste of what is done in net80211. Unfortunately, the
* functions to add IEs are static and thus can't be reused here.
*/
/* add essid IE */
essid));
}
*frm++ = IEEE80211_ELEMID_SSID;
/* add supported rates IE */
*frm++ = IEEE80211_ELEMID_RATES;
if (nrates > IEEE80211_RATE_SIZE)
/* add supported xrates IE */
*frm++ = IEEE80211_ELEMID_XRATES;
}
/* add optionnal IE (usually an RSN IE) */
}
/* setup length of probe request */
/* align on a 4-byte boundary */
if (ic->ic_des_esslen) {
} else {
}
frm += sizeof (wpi_scan_chan_t);
}
/* kick cmd ring */
return (WPI_SUCCESS); /* will be notified async. of failure/success */
}
static int
{
#ifdef WPI_BLUE_COEXISTENCE
#endif
int err;
/* Intel's binary only daemon is a joke.. */
/* set Tx power for 2.4GHz channels (values read from EEPROM) */
if (err != WPI_SUCCESS) {
return (err);
}
/* set power mode */
if (err != WPI_SUCCESS) {
return (err);
}
#ifdef WPI_BLUE_COEXISTENCE
/* configure bluetooth coexistence */
sizeof (bluetooth), 0);
if (err != WPI_SUCCESS) {
"wpi_config(): "
"failed to configurate bluetooth coexistence\n");
return (err);
}
#endif
/* configure adapter */
case IEEE80211_M_STA:
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
break;
case IEEE80211_M_HOSTAP:
break;
case IEEE80211_M_MONITOR:
break;
}
sizeof (wpi_config_t), 0);
if (err != WPI_SUCCESS) {
"failed to set configure command\n");
return (err);
}
/* add broadcast node */
if (err != WPI_SUCCESS) {
"failed to add broadcast node\n");
return (err);
}
return (WPI_SUCCESS);
}
static void
{
int ntries;
return; /* already asleep */
break;
DELAY(1000);
}
if (ntries == 2000)
}
static int
{
int ntries;
break;
DELAY(10);
}
if (ntries == 5000) {
"wpi_power_up(): timeout waiting for NIC to power up\n");
return (ETIMEDOUT);
}
return (WPI_SUCCESS);
}
static int
{
int ntries;
/* clear any pending interrupts */
/* wait for clock stabilization */
break;
DELAY(10);
}
if (ntries == 1000) {
"wpi_reset(): timeout waiting for clock stabilization\n");
return (ETIMEDOUT);
}
/* initialize EEPROM */
if ((tmp & WPI_EEPROM_VERSION) == 0) {
return (EIO);
}
return (WPI_SUCCESS);
}
static void
{
/* voodoo from the Linux "driver".. */
hw |= WPI_HW_ALM_MB;
hw |= WPI_HW_ALM_MM;
hw |= WPI_HW_SKU_MRC;
hw &= ~WPI_HW_REV_D;
hw |= WPI_HW_REV_D;
hw |= WPI_HW_TYPE_B;
}
static int
{
DELAY(20);
(void) wpi_power_up(sc);
if (!(tmp & WPI_GPIO_HW_RF_KILL)) {
goto fail1;
}
/* init Rx ring */
/* init Tx rings */
}
/* clear "radio off" and "disable command" bits (reversed logic) */
/* clear any pending interrupts */
/* enable interrupts */
/* load firmware boot code into NIC */
if (err != WPI_SUCCESS) {
goto fail1;
}
/* load firmware .text segment into NIC */
if (err != WPI_SUCCESS) {
"failed to load firmware(text)\n");
goto fail1;
}
/* load firmware .data segment into NIC */
if (err != WPI_SUCCESS) {
"failed to load firmware(data)\n");
goto fail1;
}
/* now press "execute" ;-) */
/* ..and wait at most one second for adapter to initialize */
break;
}
"wpi_init(): timeout waiting for firmware init\n");
goto fail1;
}
/* wait for thermal sensors to calibrate */
break;
DELAY(10);
}
if (ntries == 1000) {
"wpi_init(): timeout waiting for thermal sensors "
"calibration\n"));
}
if (err) {
goto fail1;
}
return (WPI_SUCCESS);
return (err);
}
static int
{
int err;
/* restore runtime configuration */
"failed to setup authentication\n");
return (err);
}
sizeof (wpi_config_t), 1);
if (err != WPI_SUCCESS) {
return (err);
}
/* link LED on */
/* update keys */
for (int i = 0; i < IEEE80211_KEY_MAX; i++) {
continue;
/* failure */
if (err == 0) {
"failed to setup hardware keys\n");
return (WPI_FAIL);
}
}
}
/* start queue */
return (WPI_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 int
{
return (DDI_FAILURE);
#ifdef DEBUG
/* by pass any messages, if it's quiesce */
wpi_dbg_flags = 0;
#endif
/*
* No more blocking is allowed while we are in the
* quiesce(9E) entry point.
*/
/*
* Disable and mask all interrupts.
*/
return (DDI_SUCCESS);
}
static void
{
int ac;
/* no mutex operation, if it's quiesced */
/* disable interrupts */
/* reset all Tx rings */
/* reset Rx ring */
DELAY(5);
sc->sc_tx_timer = 0;
sc->sc_scan_pending = 0;
sc->sc_scan_next = 0;
/* no mutex operation, if it's quiesced */
}
/*
* Naive implementation of the Adaptive Multi Rate Retry algorithm:
* "IEEE 802.11 Rate Adaptation: A Practical Approach"
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
* INRIA Sophia - Projet Planete
*/
static void
{
}
static void
{
else
}
/* ARGSUSED */
static void
{
int need_change = 0;
!is_max_rate(in)) {
"AMRR increasing rate %d (txcnt=%d retrycnt=%d)\n",
need_change = 1;
} else {
}
} else if (is_failure(amrr)) {
if (!is_min_rate(in)) {
if (amrr->success_threshold >
} else {
}
"AMRR decreasing rate %d (txcnt=%d retrycnt=%d)\n",
need_change = 1;
}
}
}