rt2860.c revision 8a3c961b6b8e22607c570d092514b791eb1519e9
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2007, 2008
* 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 RT2860 chipset driver
*/
#include <sys/byteorder.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 "rt2860_reg.h"
#include "rt2860_var.h"
#define RT2860_DBG_80211 (1 << 0)
#ifdef DEBUG
#define RWN_DEBUG \
#else
#define RWN_DEBUG
#endif
static void *rt2860_soft_state_p = NULL;
static uint8_t rt2860_fw_bin [] = {
#include "fw-rt2860/rt2860.ucode"
};
static const struct ieee80211_rateset rt2560_rateset_11b =
{ 4, { 2, 4, 11, 22 } };
static const struct ieee80211_rateset rt2560_rateset_11g =
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
static const struct {
} rt2860_def_mac[] = {
};
static const struct {
} rt2860_def_bbp[] = {
};
static const struct rfprog {
} rt2860_rf2850[] = {
};
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t rwn_csr_accattr = {
};
/*
* DMA access attributes for descriptors: NOT to be byte swapped.
*/
static ddi_device_acc_attr_t rt2860_desc_accattr = {
};
static ddi_device_acc_attr_t rt2860_buf_accattr = {
};
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t rt2860_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0xffffffffU, /* dma_attr_count_max */
16, /* 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 int rt2860_read_eeprom(struct rt2860_softc *);
const char *rt2860_get_rf(uint8_t);
static void rt2860_free_dma_mem(struct dma_area *);
static int rt2860_alloc_tx_ring(struct rt2860_softc *,
struct rt2860_tx_ring *);
static void rt2860_free_tx_ring(struct rt2860_softc *,
struct rt2860_tx_ring *);
static int rt2860_alloc_rx_ring(struct rt2860_softc *,
struct rt2860_rx_ring *);
static void rt2860_free_rx_ring(struct rt2860_softc *,
struct rt2860_rx_ring *);
static int rt2860_alloc_tx_pool(struct rt2860_softc *);
static void rt2860_free_tx_pool(struct rt2860_softc *);
static int rt2860_ack_rate(struct ieee80211com *, int);
const struct rt2860_rxwi *);
static void rt2860_drain_stats_fifo(struct rt2860_softc *);
static void rt2860_tx_intr(struct rt2860_softc *, int);
static void rt2860_rx_intr(struct rt2860_softc *);
static void rt2860_set_region_4(struct rt2860_softc *,
static int rt2860_load_microcode(struct rt2860_softc *);
static int rt2860_bbp_init(struct rt2860_softc *);
static void rt2860_select_chan_group(struct rt2860_softc *, int);
static void rt2860_set_chan(struct rt2860_softc *,
struct ieee80211_channel *);
static void rt2860_updateprot(struct ieee80211com *);
static void rt2860_next_scan(void *);
static void rt2860_iter_func(void *, struct ieee80211_node *);
static void rt2860_updateslot(struct rt2860_softc *);
static void rt2860_enable_mrr(struct rt2860_softc *);
static void rt2860_set_txpreamble(struct rt2860_softc *);
static void rt2860_set_basicrates(struct rt2860_softc *);
static void rt2860_amrr_node_init(const struct rt2860_amrr *,
struct rt2860_amrr_node *);
static void rt2860_amrr_choose(struct rt2860_amrr *,
struct ieee80211_node *, struct rt2860_amrr_node *);
int);
static void rt2860_enable_tsf_sync(struct rt2860_softc *);
static int rt2860_newstate(struct ieee80211com *,
enum ieee80211_state, int);
static int rt2860_init(struct rt2860_softc *);
static void rt2860_stop(struct rt2860_softc *);
static int rt2860_quiesce(dev_info_t *t);
/*
* device operations
*/
/*
* Module Loading Data & Entry Points
*/
static struct modldrv rwn_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"Ralink RT2700/2800 driver v1.1", /* short description */
&rwn_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
(void *)&rwn_modldrv,
};
static int rt2860_m_start(void *);
static void rt2860_m_stop(void *);
static int rt2860_m_promisc(void *, boolean_t);
static int rt2860_m_unicst(void *, const uint8_t *);
static mac_callbacks_t rt2860_m_callbacks = {
NULL,
NULL,
NULL,
};
#ifdef DEBUG
void
{
if (dbg_flags & rt2860_dbg_flags) {
}
}
#endif
const char *
{
switch (rev) {
case RT2860_RF_2820: return "RT2820";
case RT2860_RF_2850: return "RT2850";
case RT2860_RF_2720: return "RT2720";
case RT2860_RF_2750: return "RT2750";
default: return "unknown";
}
}
/*
* Read 16 bits at address 'addr' from the serial EEPROM (either 93C46,
* 93C66 or 93C86).
*/
static uint16_t
{
int n;
/* clock C once before the first command */
RT2860_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--) {
}
RT2860_EEPROM_CTL(sc, 0);
/* clear Chip Select and clock C */
RT2860_EEPROM_CTL(sc, 0);
return (val);
}
/*
* Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
* Used to adjust per-rate Tx power registers.
*/
static inline uint32_t
{
for (i = 0; i < 8; i++) {
if (b4 < 0)
b4 = 0;
else if (b4 > 0xf)
b4 = 0xf;
}
return (b32);
}
static int
{
/* read EEPROM version */
"EEPROM rev=%d, FAE=%d\n",
/* read MAC address */
"MAC address is: %x:%x:%x:%x:%x:%x\n",
/* read country code */
"EEPROM region code=0x%04x\n", val);
/* read default BBP settings */
for (i = 0; i < 8; i++) {
"BBP%d=0x%02x\n",
}
/* read RF frequency offset from EEPROM */
/* read LEDs operating mode */
} else {
/* broken EEPROM, use default settings */
}
"EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n",
/* read RF information */
if (val == 0xffff) {
/* broken EEPROM, default to RF2820 1T2R */
"invalid EEPROM antenna info, using default\n");
} else {
}
"EEPROM RF rev=0x%02x chains=%dT%dR\n",
/* check if RF supports automatic Tx access gain control */
"EEPROM CFG 0x%04x\n", val);
/* read PCIe power save level */
"rwn: rt2860_read_eeprom(): "
"EEPROM PCIe PS Level=%d\n",
}
}
/* read power settings for 2GHz channels */
for (i = 0; i < 14; i += 2) {
RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2);
RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2);
}
/* fix broken Tx power entries */
for (i = 0; i < 14; i++) {
"chan %d: power1=%d, power2=%d\n",
}
/* read power settings for 5GHz channels */
for (i = 0; i < 36; i += 2) {
RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2);
RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2);
}
/* fix broken Tx power entries */
for (i = 0; i < 36; i++) {
"chan %d: power1=%d, power2=%d\n",
}
/* read Tx power compensation for each Tx rate */
delta_2ghz = delta_5ghz = 0;
delta_2ghz = -delta_2ghz;
}
val >>= 8;
delta_5ghz = -delta_5ghz;
}
"power compensation=%d (2GHz), %d (5GHz) \n",
"ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
}
/* read factory-calibrated samples for temperature compensation */
"TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
/* check that ref value is correct, otherwise disable calibration */
sc->calib_2ghz = 0;
"TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
/* check that ref value is correct, otherwise disable calibration */
sc->calib_5ghz = 0;
/* read RSSI offsets and LNA gains from EEPROM */
/* fix broken 5GHz LNA entries */
"invalid LNA for channel group %d\n", 2);
}
"invalid LNA for channel group %d\n", 3);
}
/* fix broken RSSI offset entries */
"rwn: rt2860_read_eeprom(): "
"invalid RSSI%d offset: %d (2GHz)\n",
}
"rwn: rt2860_read_eeprom(): "
"invalid RSSI%d offset: %d (2GHz)\n",
}
}
return (RT2860_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
{
&ring->txdesc_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
goto fail1;
}
return (DDI_SUCCESS);
return (err);
}
void
{
struct rt2860_tx_data *data;
int i;
for (i = 0; i < RT2860_TX_RING_COUNT; i++) {
continue; /* nothing mapped in this slot */
/* by pass if it's quiesced */
}
}
/* by pass if it's quiesced */
}
/*ARGSUSED*/
static void
{
}
}
static int
{
struct rt2860_rx_data *data;
struct rt2860_rxd *rxd;
&ring->rxdesc_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
goto fail1;
}
/*
* Pre-allocate Rx buffers and populate Rx ring.
*/
for (i = 0; i < RT2860_RX_RING_COUNT; i++) {
/* alloc DMA memory */
}
return (DDI_SUCCESS);
return (err);
}
/*ARGSUSED*/
void
{
int i;
for (i = 0; i < RT2860_RX_RING_COUNT; i++)
}
/*ARGSUSED*/
static void
{
struct rt2860_rx_data *data;
int i, count;
for (i = 0; i < count; i++) {
}
}
}
static int
{
struct rt2860_tx_data *data;
/* init data_pool early in case of failure.. */
&sc->txpool_dma);
if (err != DDI_SUCCESS) {
"failed to alloc dma mem\n");
goto fail1;
}
for (i = 0; i < RT2860_TX_POOL_COUNT; i++) {
if (err != DDI_SUCCESS) {
"rwn: rt2860_alloc_tx_pool(): "
"failed to alloc dma mem\n");
goto fail2;
}
i * sizeof (struct rt2860_txwi);
}
return (DDI_SUCCESS);
return (err);
}
static void
{
struct rt2860_tx_data *data;
int i;
}
for (i = 0; i < RT2860_TX_POOL_COUNT; i++) {
}
}
/* quickly determine if a given rate is CCK or OFDM */
#define RT2860_SIFS_TIME 10
static uint8_t
{
switch (rate) {
/* CCK rates */
case 2:
return (0);
case 4:
return (1);
case 11:
return (2);
case 22:
return (3);
/* OFDM rates */
case 12:
return (0);
case 18:
return (1);
case 24:
return (2);
case 36:
return (3);
case 48:
return (4);
case 72:
return (5);
case 96:
return (6);
case 108:
return (7);
}
return (0); /* shouldn't get there */
}
/*
* Return the expected ack rate for a frame transmitted at rate `rate'.
*/
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 (RT2860_RATE_IS_OFDM(rate)) {
/* IEEE Std 802.11g-2003, pp. 44 */
} else {
/* IEEE Std 802.11b-1999, pp. 28 */
else
}
return (txtime);
}
static int
{
struct rt2860_tx_ring *ring;
struct rt2860_tx_data *data;
struct rt2860_txd *txd;
struct rt2860_txwi *txwi;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
err = DDI_SUCCESS;
if (RT2860_IS_SUSPEND(sc)) {
goto fail1;
}
if ((type & IEEE80211_FC0_TYPE_MASK) !=
else
qid = EDCA_AC_BE;
sc->sc_tx_nobuf++;
goto fail1;
}
/* the data pool contains at least one element, pick the first */
if (m == NULL) {
"rt2560_mgmt_send: can't alloc mblk.\n");
err = DDI_FAILURE;
goto fail1;
}
}
err = DDI_FAILURE;
goto fail2;
}
if ((type & IEEE80211_FC0_TYPE_MASK) ==
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 */
((type & IEEE80211_FC0_TYPE_MASK) !=
else {
else
}
/* get MCS code from rate */
/* setup TX Wireless Information */
if (!RT2860_RATE_IS_OFDM(rate)) {
mcs |= RT2860_PHY_SHPRE;
} else
/*
* We store the MCS code into the driver-private PacketID field.
* The PacketID is latched into TX_STAT_FIFO when Tx completes so
* that we know at which initial rate the frame was transmitted.
* We add 1 to the MCS code because setting the PacketID field to
* 0 means that we don't want feedback in TX_STAT_FIFO.
*/
else
}
/* copy and trim 802.11 header */
/* first segment is TXWI + 802.11 header */
/* finalize last segment */
/* remove from the free pool and link it into the SW Tx slot */
"sending frame qid=%d wcid=%d rate=%d cur = %x\n",
/* kick Tx */
freemsg(m);
err == DDI_SUCCESS)
return (err);
}
/*
* This function is called periodically (every 200ms) during scanning to
* switch from one channel to another.
*/
static void
rt2860_next_scan(void *arg)
{
(void) ieee80211_next_scan(ic);
}
static void
{
tmp &= ~0xff;
}
static void
{
}
static void
rt2860_updatestats(void *arg)
{
else
}
static void
{
OFDM(0)); /* 6-> 6 */
CCK(0)); /* 1-> 1 */
}
static void
{
tmp &= ~RT2860_CCK_SHORT_EN;
}
static void
{
}
static void
{
/* set basic rates mask */
else /* 11g */
}
static void
struct rt2860_amrr_node *amn)
{
amn->amn_success = 0;
amn->amn_recovery = 0;
}
static void
struct rt2860_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
{
int i;
/* only interested in true associations */
/* init WCID table entry */
for (i = 0; i < IEEE80211_ADDR_LEN; i++)
}
/* set rate to some reasonable initial value */
i--;
"new assoc isnew=%d WCID=%d, initial rate=%d\n",
"addr=%x:%x:%x:%x:%x:%x\n",
}
void
{
tmp &= ~0x1fffff;
/*
* Local TSF is always updated with remote TSF on beacon
* reception.
*/
}
}
static int
{
enum ieee80211_state ostate;
int err;
if (sc->sc_scan_id != 0) {
sc->sc_scan_id = 0;
}
if (sc->sc_rssadapt_id != 0) {
sc->sc_rssadapt_id = 0;
}
if (ostate == IEEE80211_S_RUN) {
/* turn link LED off */
}
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 */
}
}
/* turn link LED on */
break;
}
return (err);
}
/*
* Return the Rx chain with the highest RSSI for a given frame.
*/
static uint8_t
{
rxchain = 1;
rxchain = 2;
return (rxchain);
}
static void
{
struct rt2860_amrr_node *amn;
/* drain Tx status FIFO (maxsize = 16) */
"tx stat 0x%08\n", stat);
/* if no ACK was requested, no feedback is available */
continue;
/* update per-STA AMRR stats */
if (stat & RT2860_TXQ_OK) {
/*
* Check if there were retries, ie if the Tx success
* rate is different from the requested rate. Note
* that it works only because we do not allow rate
* fallback from OFDM to CCK.
*/
amn->amn_retrycnt++;
} else
amn->amn_retrycnt++;
}
}
/*ARGSUSED*/
static void
{
"hw = %x, ring->next = %x, queued = %d\n",
}
}
sizeof (struct rt2860_txd),
if (sc->sc_need_sched &&
sc->sc_need_sched = 0;
}
}
sc->sc_tx_timer = 0;
}
static void
{
struct ieee80211_node *ni;
struct ieee80211_frame *wh;
int pktlen;
for (;;) {
struct rt2860_rxwi *rxwi;
sizeof (struct rt2860_rxd),
"rx done!\n");
break;
}
"rx crc error & rx icv error!\n");
goto skip;
}
"rx mic error!\n");
goto skip;
}
"alloc mblk error\n");
sc->sc_rx_nobuf++;
goto skip;
}
/* HW may insert 2 padding bytes after 802.11 header */
"2 padding bytes after 80211 header!\n");
}
/* grab a reference to the source node */
/* node is no longer needed */
skip:
sizeof (struct rt2860_rxd),
}
/* tell HW what we have processed */
}
static uint_t
{
/*
* Check if the soft interrupt is triggered by another
* driver at the same level.
*/
if (sc->sc_rx_pend) {
sc->sc_rx_pend = 0;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
static uint_t
{
uint32_t r;
/*
* Note this can happen early on if the IRQ is shared.
*/
return (DDI_INTR_UNCLAIMED);
}
if (r == 0xffffffff) {
return (DDI_INTR_UNCLAIMED);
}
if (r == 0) {
return (DDI_INTR_UNCLAIMED);
}
/* acknowledge interrupts */
if (r & RT2860_TX_COHERENT)
"RT2860_TX_COHERENT\n");
if (r & RT2860_RX_COHERENT)
"RT2860_RX_COHERENT\n");
if (r & RT2860_MAC_INT_2) {
"RT2860_MAC_INT_2\n");
}
if (r & RT2860_TX_DONE_INT5) {
"RT2860_TX_DONE_INT5\n");
}
if (r & RT2860_RX_DONE_INT) {
"RT2860_RX_INT\n");
}
if (r & RT2860_TX_DONE_INT4) {
"RT2860_TX_DONE_INT4\n");
}
if (r & RT2860_TX_DONE_INT3) {
"RT2860_TX_DONE_INT3\n");
}
if (r & RT2860_TX_DONE_INT2) {
"RT2860_TX_DONE_INT2\n");
}
if (r & RT2860_TX_DONE_INT1) {
"RT2860_TX_DONE_INT1\n");
}
if (r & RT2860_TX_DONE_INT0) {
"RT2860_TX_DONE_INT0\n");
rt2860_tx_intr(sc, 0);
}
if (r & RT2860_MAC_INT_0) {
"RT2860_MAC_INT_0\n");
/* check if protection mode has changed */
}
}
if (r & RT2860_MAC_INT_3)
"RT2860_MAC_INT_3\n");
return (DDI_INTR_CLAIMED);
}
static void
{
}
static int
{
int ntries;
size = sizeof (rt2860_fw_bin);
"The size of ucode is: %x\n", size);
/* set "host program ram write selection" bit */
/* write microcode image */
for (i = 0; i < size; i++) {
}
/* kick microcontroller unit */
/* wait until microcontroller is ready */
break;
DELAY(1000);
}
if (ntries == 1000) {
"timeout waiting for MCU to initialie\n");
return (ETIMEDOUT);
}
return (0);
}
static void
{
}
/*
* Send a command to the 8051 microcontroller unit.
*/
static int
{
int ntries;
break;
DELAY(2);
}
if (ntries == 100)
return (EIO);
return (RT2860_SUCCESS);
}
/*
* We access the BBP through the 8051 microcontroller unit which means that
* the microcode must be loaded first.
*/
static uint8_t
{
int ntries;
if (!(RT2860_READ(sc,
break;
DELAY(1);
}
if (ntries == 100) {
"could not read from BBP through MCU\n");
return (0);
}
DELAY(1000);
if (!(val & RT2860_BBP_CSR_KICK))
return (val & 0xff);
DELAY(1);
}
"could not read from BBP through MCU\n");
return (0);
}
static void
{
int ntries;
if (!(RT2860_READ(sc,
break;
DELAY(1);
}
if (ntries == 100) {
"could not write to BBP through MCU\n");
return;
}
DELAY(1000);
}
static int
{
int i, ntries;
/* wait for BBP to wake up */
break;
}
if (ntries == 20) {
"timeout waiting for BBP to wake up\n");
return (ETIMEDOUT);
}
/* initialize BBP registers to default values */
for (i = 0; i < 12; i++) {
rt2860_def_bbp[i].val);
}
/* fix BBP69 and BBP73 for RT2860C */
}
return (0);
}
static void
{
int ntries;
break;
DELAY(1);
}
if (ntries == 100) {
"could not write to RF\n");
return;
}
/* RF registers are 24-bit on the RT2860 */
}
static void
{
/* enable appropriate Power Amplifiers and Low Noise Amplifiers */
if (group == 0) { /* 2GHz */
} else { /* 5GHz */
}
}
static void
{
return;
}
/* find the settings for this channel (we know it exists) */
i++;
/* use Tx power values from EEPROM */
if (IEEE80211_IS_CHAN_5GHZ(c)) {
}
DELAY(200);
DELAY(200);
/* 802.11a uses a 16 microseconds short interframe space */
/* determine channel group */
if (chan <= 14)
group = 0;
else if (chan <= 64)
group = 1;
else if (chan <= 128)
group = 2;
else
group = 3;
/* XXX necessary only when group has changed! */
DELAY(1000);
}
static void
{
/* setup protection frame rate (MCS code) */
/* CCK frames don't require protection */
}
}
static void
{
}
static int
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
struct ieee80211com *ic;
tmp &= 0xff0;
/* PBF hardware reset */
"could not load 8051 microcode\n");
return (err);
}
}
/* init Tx power for all Tx rates (from EEPROM) */
continue;
}
break;
DELAY(1000);
}
if (ntries == 100) {
"timeout waiting for DMA engine\n");
return (ETIMEDOUT);
}
tmp &= 0xff0;
/* reset Rx ring and all 6 Tx rings */
/* PBF hardware reset */
for (i = 0; i < N(rt2860_def_mac); i++)
/* wait while MAC is busy */
break;
DELAY(1000);
}
if (ntries == 100) {
"timeout waiting for MAC\n");
return (ETIMEDOUT);
}
/* clear Host to MCU mailbox */
return (err);
}
/* init Tx rings (4 EDCAs + HCCA + Mgt) */
}
/* init Rx ring */
/* setup maximum buffer sizes */
break;
DELAY(1000);
}
if (ntries == 100) {
"timeout waiting for DMA engine\n");
return (ETIMEDOUT);
}
tmp &= 0xff0;
/* disable interrupts mitigation */
/* write vendor-specific BBP values (from EEPROM) */
for (i = 0; i < 8; i++) {
continue;
}
/* send LEDs operating mode to microcontroller */
/* disable non-existing Rx chains */
/* disable non-existing Tx chains */
/* select default channel */
/* XXX not clear what the following 8051 command does.. */
/* set RTS threshold */
tmp &= ~0xffff00;
/* setup initial protection mode */
break;
DELAY(1000);
}
if (ntries == 200) {
"timeout waiting for DMA engine\n");
return (ETIMEDOUT);
}
DELAY(50);
/* turn radio LED on */
/* set Rx filter */
}
/* clear pending interrupts */
/* enable interrupts */
return (DDI_SUCCESS);
}
static int
{
struct rt2860_softc *sc;
return (DDI_FAILURE);
#ifdef DEBUG
rt2860_dbg_flags = 0;
#endif
/*
* No more blocking is allowed while we are in quiesce(9E) entry point
*/
/*
* Disable and mask all interrupts
*/
return (DDI_SUCCESS);
}
static void
{
int qid;
/* by pass if it's quiesced */
sc->sc_tx_timer = 0;
/* by pass if it's quiesced */
/* clear RX WCID search table */
/* clear pairwise key table */
/* clear WCID attribute table */
/* clear shared key table */
/* clear shared key mode */
/* disable interrupts */
/* disable Rx */
/* reset adapter */
/* reset Tx and Rx rings (and reclaim TXWIs) */
/* by pass if it's quiesced */
/* by pass if it's quiesced */
}
static int
rt2860_m_start(void *arg)
{
int err;
if (err != DDI_SUCCESS) {
"Hardware initialization failed\n");
goto fail1;
}
return (err);
return (err);
}
static void
rt2860_m_stop(void *arg)
{
(void) rt2860_stop(sc);
}
static void
{
int err;
if (ic->ic_des_esslen) {
if (RT2860_IS_RUNNING(sc)) {
(void) rt2860_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 (RT2860_IS_RUNNING(sc)) {
(void) rt2860_init(sc);
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
err = 0;
}
return (err);
}
static mblk_t *
{
if (RT2860_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
{
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 rt2860_softc *sc;
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if (RT2860_IS_RUNNING(sc))
(void) rt2860_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");
/* wait for NIC to initialize */
break;
DELAY(10);
}
if (ntries == 100) {
"timeout waiting for NIC initialize\n");
return (DDI_FAILURE);
}
(device_id == PRODUCT_RALINK_RT2890 ||
/* retrieve RF rev. no and various other things from EEPROM */
(void) rt2860_read_eeprom(sc);
/*
* Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings.
*/
"could not allocate Tx ring %d\n", qid);
goto fail3;
}
}
"could not allocte Rx ring\n");
goto fail4;
}
"could not allocte Tx pool\n");
goto fail5;
}
/* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */
/* set device capabilities */
IEEE80211_C_TXPMGT | /* tx power management */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT; /* short slot time supported */
/* 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 fail8;
}
if (err != DDI_SUCCESS) {
"Can not get iblock cookie for INT\n");
goto fail7;
}
if (err != DDI_SUCCESS) {
"unable to add device interrupt handler\n");
goto fail7;
}
/*
* Provide initial settings for the WiFi plugin; whenever this
* information changes, we need to call mac_plugindata_update()
*/
"MAC version mismatch\n");
goto fail9;
}
if (err != 0) {
"mac_register err %x\n", err);
goto fail9;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
"rwn", instance);
/*
* Notify link is down now
*/
return (DDI_SUCCESS);
while (--qid >= 0)
return (err);
}
static int
{
struct rt2860_softc *sc;
int qid;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (RT2860_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
*/
return (DDI_SUCCESS);
}
int
{
}
int
_init(void)
{
int status;
sizeof (struct rt2860_softc), 1);
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}