rt2661.c revision 10115c80ec7040cb0f5be39c544c4febb00369ee
/*
* Copyright 2009 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.
*/
/*
* Ralink Technology RT2561, RT2561S and RT2661 chipset driver
*/
#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 <inet/wifi_ioctl.h>
#include "rt2661_reg.h"
#include "rt2661_var.h"
#include "rt2661_ucode.h"
#define RT2661_DBG_80211 (1 << 0)
uint32_t rt2661_dbg_flags = 0;
#ifdef DEBUG
#define RWD_DEBUG \
#else
#define RWD_DEBUG
#endif
static void *rt2661_soft_state_p = NULL;
int usize;
static const struct {
} rt2661_def_mac[] = {
};
static const struct {
} rt2661_def_bbp[] = {
};
static const struct rfprog {
} rt2661_rf5225_1[] = {
}, rt2661_rf5225_2[] = {
};
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t rt2661_csr_accattr = {
};
/*
* DMA access attributes for descriptors: NOT to be byte swapped.
*/
static ddi_device_acc_attr_t rt2661_desc_accattr = {
};
static ddi_device_acc_attr_t rt2661_buf_accattr = {
};
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t rt2661_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0xffffffffU, /* dma_attr_count_max */
1, /* dma_attr_align */
0x00000fff, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffU, /* dma_attr_maxxfer */
0xffffffffU, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
static const struct ieee80211_rateset rt2661_rateset_11b =
{ 4, { 2, 4, 11, 22 } };
static const struct ieee80211_rateset rt2661_rateset_11g =
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
static const char *rt2661_get_rf(int);
static void rt2661_read_eeprom(struct rt2661_softc *);
static int rt2661_load_microcode(struct rt2661_softc *,
const uint8_t *, int);
static void rt2661_free_dma_mem(struct dma_area *);
static int rt2661_alloc_tx_ring(struct rt2661_softc *,
struct rt2661_tx_ring *, int);
static void rt2661_reset_tx_ring(struct rt2661_softc *,
struct rt2661_tx_ring *);
static void rt2661_free_tx_ring(struct rt2661_softc *,
struct rt2661_tx_ring *);
static int rt2661_alloc_rx_ring(struct rt2661_softc *,
struct rt2661_rx_ring *, int);
static void rt2661_reset_rx_ring(struct rt2661_softc *,
struct rt2661_rx_ring *);
static void rt2661_free_rx_ring(struct rt2661_softc *,
struct rt2661_rx_ring *);
static void rt2661_tx_dma_intr(struct rt2661_softc *,
struct rt2661_tx_ring *);
static void rt2661_tx_intr(struct rt2661_softc *);
static void rt2661_rx_intr(struct rt2661_softc *);
static void rt2661_mcu_wakeup(struct rt2661_softc *);
static void rt2661_mcu_cmd_intr(struct rt2661_softc *);
static int rt2661_ack_rate(struct ieee80211com *, int);
static uint8_t rt2661_plcp_signal(int);
static void rt2661_setup_tx_desc(struct rt2661_softc *,
int, int);
static void rt2661_amrr_node_init(const struct rt2661_amrr *,
struct rt2661_amrr_node *);
static void rt2661_amrr_choose(struct rt2661_amrr *,
struct ieee80211_node *, struct rt2661_amrr_node *);
static void rt2661_update_promisc(struct rt2661_softc *);
static void rt2661_updateslot(struct ieee80211com *, int);
static void rt2661_set_slottime(struct rt2661_softc *);
static void rt2661_enable_mrr(struct rt2661_softc *);
static void rt2661_set_txpreamble(struct rt2661_softc *);
static void rt2661_set_basicrates(struct rt2661_softc *);
static void rt2661_updatestats(void *);
static void rt2661_rx_tune(struct rt2661_softc *);
static void rt2661_enable_tsf_sync(struct rt2661_softc *);
static int rt2661_newstate(struct ieee80211com *,
enum ieee80211_state, int);
static int rt2661_bbp_init(struct rt2661_softc *);
static void rt2661_select_band(struct rt2661_softc *,
struct ieee80211_channel *);
static void rt2661_select_antenna(struct rt2661_softc *);
static void rt2661_set_chan(struct rt2661_softc *,
struct ieee80211_channel *);
static void rt2661_stop_locked(struct rt2661_softc *);
static int rt2661_init(struct rt2661_softc *);
static void rt2661_stop(struct rt2661_softc *);
/*
* device operations
*/
static int rt2661_quiesce(dev_info_t *);
/*
* Module Loading Data & Entry Points
*/
static struct modldrv rwd_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"Ralink RT2661 driver v1.1", /* short description */
&rwd_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
(void *)&rwd_modldrv,
};
static int rt2661_m_start(void *);
static void rt2661_m_stop(void *);
static int rt2661_m_promisc(void *, boolean_t);
static int rt2661_m_unicst(void *, const uint8_t *);
static mac_callbacks_t rt2661_m_callbacks = {
NULL,
NULL,
NULL,
};
#ifdef DEBUG
void
{
if (dbg_flags & rt2661_dbg_flags) {
}
}
#endif
/*
* Read 16 bits at address 'addr' from the serial EEPROM (either 93C46 or
* 93C66).
*/
static uint16_t
{
int n;
/* clock C once before the first command */
RT2661_EEPROM_CTL(sc, 0);
/* write start bit (1) */
/* write READ opcode (10) */
/* write address (A5-A0 or A7-A0) */
for (; n >= 0; n--) {
}
/* read data Q15-Q0 */
val = 0;
for (n = 15; n >= 0; n--) {
}
RT2661_EEPROM_CTL(sc, 0);
/* clear Chip Select and clock C */
RT2661_EEPROM_CTL(sc, 0);
return (val);
}
static void
{
int i;
/* read MAC address */
/* XXX: test if different from 0xffff? */
"External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
/* adjust RSSI correction for external low-noise amplifier */
if (sc->ext_2ghz_lna)
if (sc->ext_5ghz_lna)
"RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
/* read Tx power for all a/b/g channels */
for (i = 0; i < 19; i++) {
"Channel=%d Tx power=%d\n",
"Channel=%d Tx power=%d\n",
}
/* read vendor-specific BBP values */
for (i = 0; i < 16; i++) {
continue;
}
}
static const char *
rt2661_get_rf(int rev)
{
switch (rev) {
case RT2661_RF_5225: return "RT5225";
case RT2661_RF_5325: return "RT5325 (MIMO XR)";
case RT2661_RF_2527: return "RT2527";
case RT2661_RF_2529: return "RT2529 (MIMO XR)";
default: return "unknown";
}
}
static int
{
int ntries;
/* reset 8051 */
/* cancel any pending Host to MCU command */
/* write 8051's microcode */
/* RT2661_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, ucode, size); */
for (i = 0; i < size; i++) {
}
/* kick 8051's ass */
/* wait for 8051 to initialize */
break;
DELAY(100);
}
if (ntries == 500) {
"timeout waiting for MCU to initialize\n");
return (RT2661_FAILURE);
}
"MCU initialized successfully\n");
return (RT2661_SUCCESS);
}
/*
* 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
{
}
}
}
/*ARGSUSED*/
static int
{
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
&ring->txdesc_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
goto fail1;
}
"failed to alloc tx buffer\n");
goto fail2;
}
for (i = 0; i < count; i++) {
&data->txdata_dma);
if (err != DDI_SUCCESS) {
"rwd: rt2661_alloc_tx_ring(): "
"failed to alloc tx buffer dma\n");
while (i >= 0) {
i--;
}
goto fail3;
}
}
0, size, DDI_DMA_SYNC_FORDEV);
return (DDI_SUCCESS);
count * sizeof (struct rt2661_tx_data));
return (err);
}
static void
{
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
int i;
}
}
if (!RT2661_IS_FASTREBOOT(sc))
}
/*ARGSUSED*/
static void
{
struct rt2661_tx_data *data;
int i;
}
}
}
}
}
/*ARGSUSED*/
static int
{
struct rt2661_rx_desc *desc;
struct rt2661_rx_data *data;
&ring->rxdesc_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
goto fail1;
}
"failed to alloc rx buffer\n");
goto fail2;
}
for (i = 0; i < count; i++) {
&data->rxdata_dma);
if (err != DDI_SUCCESS) {
"rwd: rt2661_alloc_rx_ring(): "
"failed to alloc rx buffer dma\n");
while (i >= 0) {
i--;
}
goto fail3;
}
}
0, size, DDI_DMA_SYNC_FORDEV);
return (DDI_SUCCESS);
count * sizeof (struct rt2661_rx_data));
return (err);
}
static void
{
int i;
if (!RT2661_IS_FASTREBOOT(sc))
}
/*ARGSUSED*/
static void
{
struct rt2661_rx_data *data;
int i;
}
}
}
}
static void
{
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
for (;;) {
break;
0, sc->sc_dmabuf_size,
/* descriptor is no longer valid */
}
}
static void
{
struct rt2661_tx_ring *ring;
struct rt2661_tx_data *data;
struct rt2661_node *rn;
for (;;) {
if (!(val & RT2661_TX_STAT_VALID))
break;
/* retrieve the queue in which this frame was send */
/* retrieve rate control algorithm context */
/* if no frame has been sent, ignore */
"no frame has been send, ignore\n");
continue;
}
switch (RT2661_TX_RESULT(val)) {
case RT2661_TX_SUCCESS:
"data frame sent successfully after "
"%d retries\n", retrycnt);
if (retrycnt > 0) {
sc->sc_tx_retries++;
}
break;
case RT2661_TX_RETRY_FAIL:
"sending data frame failed (too much retries)\n");
break;
default:
/* other failure */
"sending data frame failed 0x%08x\n", val);
}
/* faster than % count */
if (sc->sc_need_sched) {
sc->sc_need_sched = 0;
}
}
sc->sc_tx_timer = 0;
}
static void
{
struct rt2661_rx_ring *ring;
struct rt2661_rx_desc *desc;
struct rt2661_rx_data *data;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
mblk_t *m;
for (;;) {
int rssi;
break;
/*
* This should not happen since we did not request
* to receive those frames when we filled TXRX_CSR0.
*/
"PHY or CRC error flags 0x%08x\n",
goto skip;
}
goto skip;
}
0, sc->sc_dmabuf_size,
if ((pktlen < sizeof (struct ieee80211_frame_min)) ||
"bad fram length=%u\n", pktlen);
goto skip;
}
"allocate mblk failed.\n");
sc->sc_rx_nobuf++;
goto skip;
}
/* send the frame to the 802.11 layer */
/* node is no longer needed */
skip:
}
}
/*ARGSUSED*/
static uint_t
{
if (sc->sc_rx_pend) {
sc->sc_rx_pend = 0;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
static int
{
return (EIO); /* there is already a command pending */
return (0);
}
static void
{
/* send wakeup command to MCU */
}
static void
{
}
/*ARGSUSED*/
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED); /* not for us */
}
/* disable MAC and MCU interrupts */
/* acknowledge interrupts */
if (r1 & RT2661_MGT_DONE) {
"RT2661_MGT_DONE\n");
}
if (r1 & RT2661_RX_DONE) {
"RT2661_RX_DONE\n");
}
if (r1 & RT2661_TX0_DMA_DONE) {
"RT2661_TX0_DMA_DONE\n");
}
if (r1 & RT2661_TX1_DMA_DONE) {
"RT2661_TX1_DMA_DONE\n");
}
if (r1 & RT2661_TX2_DMA_DONE) {
"RT2661_TX2_DMA_DONE\n");
}
if (r1 & RT2661_TX3_DMA_DONE) {
"RT2661_TX3_DMA_DONE\n");
}
if (r1 & RT2661_TX_DONE) {
"RT2661_TX_DONE\n");
}
if (r2 & RT2661_MCU_CMD_DONE) {
"RT2661_MCU_CMD_DONE\n");
}
if (r2 & RT2661_MCU_WAKEUP) {
"RT2661_MCU_WAKEUP\n");
}
/* re-enable MAC and MCU interrupts */
return (RT2661_SUCCESS);
}
/*
* Retrieve the "Received Signal Strength Indicator" from the raw values
* contained in Rx descriptors. The computation depends on which band the
* frame was received. Correction values taken from the reference driver.
*/
static int
{
if (lna == 1)
rssi -= 64;
else if (lna == 2)
rssi -= 74;
else if (lna == 3)
rssi -= 90;
} else {
if (lna == 1)
rssi -= 64;
else if (lna == 2)
rssi -= 86;
else if (lna == 3)
rssi -= 100;
}
return (rssi);
}
/* quickly determine if a given rate is CCK or OFDM */
/*
* Return the expected ack rate for a frame transmitted at rate `rate'.
* XXX: this should depend on the destination node basic rate set.
*/
static int
{
switch (rate) {
/* CCK rates */
case 2:
return (2);
case 4:
case 11:
case 22:
/* OFDM rates */
case 12:
case 18:
return (12);
case 24:
case 36:
return (24);
case 48:
case 72:
case 96:
case 108:
return (48);
}
/* default to 1Mbps */
return (2);
}
/*
* Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
* The function automatically determines the operating mode depending on the
* given rate. `flags' indicates whether short preamble is in use or not.
*/
static uint16_t
{
if (RT2661_RATE_IS_OFDM(rate)) {
/* IEEE Std 802.11a-1999, pp. 37 */
} else {
/* IEEE Std 802.11b-1999, pp. 28 */
else
}
return (txtime);
}
static uint8_t
rt2661_plcp_signal(int rate)
{
switch (rate) {
/* CCK rates (returned values are device-dependent) */
case 2:
return (0x0);
case 4:
return (0x1);
case 11:
return (0x2);
case 22:
return (0x3);
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
case 12:
return (0xb);
case 18:
return (0xf);
case 24:
return (0xa);
case 36:
return (0xe);
case 48:
return (0x9);
case 72:
return (0xd);
case 96:
return (0x8);
case 108:
return (0xc);
/* unsupported rates (should not get there) */
default:
return (0xff);
}
}
static void
{
int remainder;
RT2661_QID(ac) |
RT2661_AIFSN(2) |
RT2661_LOGCWMIN(4) |
RT2661_LOGCWMAX(10));
/*
* Remember in which queue this frame was sent. This field is driver
* private data only. It will be made available by the NIC in STA_CSR4
* on Tx interrupts.
*/
/* setup PLCP fields */
len += IEEE80211_CRC_LEN;
if (RT2661_RATE_IS_OFDM(rate)) {
} else {
if (rate == 22) {
}
}
/* RT2x61 supports scatter with up to 5 segments */
}
static int
{
struct rt2661_tx_ring *ring;
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
err = DDI_SUCCESS;
sc->sc_tx_nobuf++;
goto fail1;
}
if (m == NULL) {
"can't alloc mblk.\n");
err = DDI_FAILURE;
goto fail1;
}
}
err = DDI_FAILURE;
goto fail2;
}
struct ieee80211_key *k;
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
err = DDI_FAILURE;
goto fail3;
}
/* packet header may have moved, reset our local pointer */
}
/* pickup a rate */
/* multicast frames are sent at the lowest avail. rate */
} else
if (rate == 0)
}
0, pktlen,
"sending data frame len=%u idx=%u rate=%u\n",
/* kick Tx */
freemsg(m);
if (err == DDI_SUCCESS)
return (err);
}
/*ARGSUSED*/
static int
{
struct rt2661_tx_ring *ring;
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
goto fail1;
}
err = DDI_SUCCESS;
sc->sc_tx_nobuf++;
goto fail1;
}
if (m == NULL) {
"can't alloc mblk.\n");
err = DDI_FAILURE;
goto fail1;
}
}
err = DDI_FAILURE;
goto fail2;
}
struct ieee80211_key *k;
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
err = DDI_FAILURE;
goto fail3;
}
/* packet header may have moved, reset our local pointer */
}
/* send mgt frames at the lowest available rate */
/* tell hardware to add timestamp in probe responses */
}
0, pktlen,
"sending mgmt frame len=%u idx=%u rate=%u\n",
/* kick Tx */
freemsg(m);
return (err);
}
static void
struct rt2661_amrr_node *amn)
{
amn->amn_success = 0;
amn->amn_recovery = 0;
}
static void
struct rt2661_amrr_node *amn)
{
#define is_success(amn) \
#define is_failure(amn) \
#define is_min_rate(ni) \
#define is_max_rate(ni) \
#define increase_rate(ni) \
#define decrease_rate(ni) \
int need_change = 0;
amn->amn_success++;
!is_max_rate(ni)) {
amn->amn_success = 0;
"increase rate = %d, #tx = %d, #retries = %d\n",
need_change = 1;
} else
amn->amn_recovery = 0;
} else if (is_failure(amn)) {
amn->amn_success = 0;
if (!is_min_rate(ni)) {
if (amn->amn_recovery) {
if (amn->amn_success_threshold >
} else {
}
"decrease rate = %d, #tx = %d, #retries = %d\n",
need_change = 1;
}
amn->amn_recovery = 0;
}
}
static void
{
tmp &= ~RT2661_DROP_NOT_TO_ME;
"%s promiscuous mode\n",
}
static void
{
"setting slot time to %uus\n", slottime);
}
static void
{
"setting slot time to %uus\n", slottime);
}
/*
* Enable multi-rate retries for frames sent at OFDM rates.
* In 802.11b/g mode, allow fallback to CCK rates.
*/
static void
{
}
static void
{
tmp &= ~RT2661_SHORT_PREAMBLE;
}
static void
{
/* update basic rate set */
/* 11b basic rates: 1, 2Mbps */
/* 11a basic rates: 6, 12, 24Mbps */
} else {
/* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
}
}
static void
{
}
/*
* Enable TSF synchronization and tell h/w to start sending beacons for IBSS
* and HostAP operating modes.
*/
static void
{
/* set beacon interval (in 1/16ms unit) */
}
static void
rt2661_next_scan(void *arg)
{
(void) ieee80211_next_scan(ic);
}
static void
{
int i;
/* set rate to some reasonable initial value */
i--;
}
static void
{
}
/*
* Dynamically tune Rx sensitivity (BBP register 17) based on average RSSI and
* false CCA count. This function is called periodically (every seconds) when
* in the RUN state. Values taken from the reference driver.
*/
static void
{
/*
* Tuning range depends on operating band and on the presence of an
* external low-noise amplifier.
*/
lo = 0x20;
lo += 0x08;
lo += 0x10;
/* retrieve false CCA count since last call (clear on read) */
if (dbm < -74) {
/* very bad RSSI, tune using false CCA count */
else if (cca > 512)
else if (cca < 100)
} else if (dbm < -66) {
} else if (dbm < -58) {
} else if (dbm < -35) {
} else { /* very good RSSI >= -35dBm */
}
}
}
/*
* This function is called periodically (every 500ms) in RUN state to update
* various settings like rate control statistics or Rx sensitivity.
*/
static void
rt2661_updatestats(void *arg)
{
else
/* update rx sensitivity every 1 sec */
}
static int
{
enum ieee80211_state ostate;
struct ieee80211_node *ni;
int err;
if (sc->sc_scan_id != 0) {
sc->sc_scan_id = 0;
}
if (sc->sc_rssadapt_id) {
sc->sc_rssadapt_id = 0;
}
switch (nstate) {
case IEEE80211_S_INIT:
if (ostate == IEEE80211_S_RUN) {
/* abort TSF synchronization */
}
break;
case IEEE80211_S_SCAN:
drv_usectohz(200000));
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
break;
case IEEE80211_S_RUN:
}
/* fake a join to init the tx rate */
}
}
break;
default:
break;
}
return (err);
}
/*ARGSUSED*/
static struct ieee80211_node *
{
struct rt2661_node *rn;
}
static void
{
}
static void
{
if (RT2661_IS_RUNNING(sc)) {
sc->sc_tx_timer = 0;
/* abort Tx (for all 5 Tx rings) */
/* disable Rx (value remains after reset!) */
/* reset ASIC */
/* disable interrupts */
/* clear any pending interrupt */
/* reset Tx and Rx rings */
}
}
static void
{
}
static uint8_t
{
int ntries;
break;
DELAY(1);
}
if (ntries == 100) {
"could not read from BBP\n");
return (0);
}
if (!(val & RT2661_BBP_BUSY))
return (val & 0xff);
DELAY(1);
}
"could not read from BBP\n");
return (0);
}
static int
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
int i, ntries;
/* wait for BBP to be ready */
break;
DELAY(100);
}
if (ntries == 100) {
"timeout waiting for BBP\n");
return (RT2661_FAILURE);
}
/* initialize BBP registers to default values */
for (i = 0; i < N(rt2661_def_bbp); i++) {
rt2661_def_bbp[i].val);
}
/* write vendor-specific BBP values (from EEPROM) */
for (i = 0; i < 16; i++) {
continue;
}
return (RT2661_SUCCESS);
#undef N
}
static void
{
int ntries;
break;
DELAY(1);
}
if (ntries == 100) {
"could not write to BBP\n");
return;
}
}
/*
* driver.
*/
static void
{
/* update all BBP registers that depend on the band */
if (IEEE80211_IS_CHAN_5GHZ(c)) {
}
}
}
if (IEEE80211_IS_CHAN_2GHZ(c))
tmp |= RT2661_PA_PE_2GHZ;
else
tmp |= RT2661_PA_PE_5GHZ;
/* 802.11a uses a 16 microseconds short interframe space */
}
static void
{
/* TBD */
/* make sure Rx is disabled before switching antenna */
/* restore Rx filter */
}
static void
{
int ntries;
break;
DELAY(1);
}
if (ntries == 100) {
"could not write to RF\n");
return;
}
(reg & 3);
/* remember last written value in sc */
}
static void
{
return;
/* select the appropriate RF settings based on what EEPROM says */
/* find the settings for this channel (we know it exists) */
i = 0;
i++;
if (power < 0) {
power = 0;
} else if (power > 31) {
power = 31;
}
/*
* If we are switching from the 2GHz band to the 5GHz band or
* vice-versa, BBP registers need to be reprogrammed.
*/
rt2661_select_band(sc, c);
}
sc->sc_curchan = c;
DELAY(200);
DELAY(200);
/* enable smart mode for MIMO-capable RFs */
bbp3 &= ~RT2661_SMART_MODE;
if (bbp94 != RT2661_BBPR94_DEFAULT)
/* 5GHz radio needs a 1ms delay here */
if (IEEE80211_IS_CHAN_5GHZ(c))
DELAY(1000);
}
static int
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
if (!RT2661_IS_FWLOADED(sc)) {
if (err != RT2661_SUCCESS) {
"could not load 8051 microcode\n");
return (DDI_FAILURE);
}
}
/* initialize Tx rings */
/* initialize Mgt ring */
/* initialize Rx ring */
/* initialize Tx rings sizes */
RT2661_TX_RING_COUNT << 24 |
RT2661_TX_RING_COUNT << 16 |
RT2661_TX_RING_COUNT << 8 |
RT2661_TX_DESC_WSIZE << 16 |
RT2661_TX_RING_COUNT << 8 |
/* initialize Rx rings */
RT2661_RX_DESC_BACK << 16 |
RT2661_RX_DESC_WSIZE << 8 |
/* XXX: some magic here */
/* load base addresses of all 5 Tx rings (4 data + 1 mgt) */
/* load base address of Rx ring */
/* initialize MAC registers to default values */
for (i = 0; i < N(rt2661_def_mac); i++)
/* set host ready */
break;
DELAY(1000);
}
if (ntries == 1000) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* select default channel */
/* update Rx filter */
tmp |= RT2661_DROP_TODS;
}
/* clear STA registers */
for (i = 0; i < N(sta); i++) {
}
/* initialize ASIC */
/* clear any pending interrupt */
/* enable interrupts */
/* kick Rx */
#undef N
return (DDI_SUCCESS);
}
static void
{
if (!RT2661_IS_FASTREBOOT(sc))
if (!RT2661_IS_FASTREBOOT(sc))
}
static int
rt2661_m_start(void *arg)
{
int err;
if (err != DDI_SUCCESS) {
"Hardware initialization failed\n");
goto fail1;
}
return (DDI_SUCCESS);
return (err);
}
static void
rt2661_m_stop(void *arg)
{
(void) rt2661_stop(sc);
}
static void
{
int err;
if (ic->ic_des_esslen) {
if (RT2661_IS_RUNNING(sc)) {
(void) rt2661_init(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
}
}
/*
*/
static int
{
int err = 0;
return (err);
}
static int
{
int err;
wldp_buf);
if (ic->ic_des_esslen) {
if (RT2661_IS_RUNNING(sc)) {
(void) rt2661_init(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
err = 0;
}
return (err);
}
static mblk_t *
{
if (RT2661_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);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
if (on) {
} else {
}
return (0);
}
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
{
struct rt2661_softc *sc;
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if (RT2661_IS_RUNNING(sc))
(void) rt2661_init(sc);
"resume now\n");
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
"unable to alloc soft_state_p\n");
return (err);
}
/* PCI configuration */
if (err != DDI_SUCCESS) {
"ddi_regs_map_setup() failed");
goto fail1;
}
if (cachelsz == 0)
cachelsz = 0x10;
"vendor 0x%x, device id 0x%x, cache size %d\n",
/*
* Enable response to memory space accesses,
* and enabe bus master.
*/
command);
/* pci i/o space */
if (err != DDI_SUCCESS) {
"ddi_regs_map_setup() failed");
goto fail2;
}
"PCI configuration is done successfully\n");
"fixed type interrupt is not supported\n");
goto fail3;
}
"no fixed interrupts\n");
goto fail3;
}
"ddi_intr_alloc() failed 0x%x\n", err);
goto faili4;
}
if (err != DDI_SUCCESS) {
"ddi_intr_get_pri() failed 0x%x\n", err);
goto faili5;
}
/* wait for NIC to initialize */
break;
DELAY(1000);
}
if (ntries == 1000) {
"timeout waiting for NIC to initialize\n");
goto faili5;
}
/* retrieve RF rev. no and various other things from EEPROM */
"MAC address is: %x:%x:%x:%x:%x:%x\n", val,
/*
* Load 8051 microcode into NIC.
*/
switch (device_id) {
case 0x0301:
usize = sizeof (rt2561s_ucode);
break;
case 0x0302:
usize = sizeof (rt2561_ucode);
break;
case 0x0401:
usize = sizeof (rt2661_ucode);
break;
}
if (err != RT2661_SUCCESS) {
"could not load 8051 microcode\n");
goto faili5;
}
/*
* Allocate Tx and Rx rings.
*/
if (err != RT2661_SUCCESS) {
"could not allocate Tx ring %d\n", ac);
goto fail4;
}
}
if (err != RT2661_SUCCESS) {
"could not allocate Mgt ring\n");
goto fail5;
}
if (err != RT2661_SUCCESS) {
"could not allocate Rx ring\n");
goto fail6;
}
/* set device capabilities */
/* set supported .11b and .11g rates */
/* set supported .11b and .11g channels (1 through 14) */
for (i = 1; i <= 14; i++) {
}
/* register WPA door */
/* override state transition machine */
ic->ic_def_txkey = 0;
if (err != DDI_SUCCESS) {
"ddi_add_softintr() failed");
goto fail7;
}
if (err != DDI_SUCCESS) {
"ddi_intr_addr_handle() failed\n");
goto fail8;
}
if (err != DDI_SUCCESS) {
"ddi_intr_enable() failed\n");
goto fail9;
}
/*
* Provide initial settings for the WiFi plugin; whenever this
* information changes, we need to call mac_plugindata_update()
*/
"MAC version mismatch\n");
goto fail10;
}
if (err != 0) {
"mac_register err %x\n", err);
goto fail10;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
"rwd", instance);
/*
* Notify link is down now
*/
"attach successfully\n");
return (DDI_SUCCESS);
while (--ac >= 0)
return (DDI_FAILURE);
}
static int
{
struct rt2661_softc *sc;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (RT2661_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);
}
static int
{
struct rt2661_softc *sc;
return (DDI_FAILURE);
#ifdef DEBUG
rt2661_dbg_flags = 0;
#endif
/*
* No more blocking is allowed while we are in quiesce(9E) entry point
*/
/*
* Disable all interrupts
*/
return (DDI_SUCCESS);
}
int
{
}
int
_init(void)
{
int status;
sizeof (struct rt2661_softc), 1);
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}