blob: 49d79cbb4a855c9be32dc1c9ce98c91034fd5e71 [file] [log] [blame]
/*
* Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP)
*
* Copyright (c) 2003, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004-2005, Michael Wu <flamingice@sourmilk.net>
* Some parts copyright (c) 2003 by David Young <dyoung@pobox.com>
* and used with permission.
*
* Much thanks to Infineon-ADMtek for their support of this driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/if.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/if_arp.h>
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/wireless.h>
#include <net/ieee80211.h>
#include <net/iw_handler.h>
#include <asm/delay.h>
#include <asm/unaligned.h>
#include <asm/types.h>
#include <asm/div64.h>
#include "adm8211.h"
#include "adm8211_ioctl.h"
#include "ieee80211_sta.h"
#include "avs_caphdr.h"
#define RELEASE_DATE "20060414"
MODULE_AUTHOR("Jouni Malinen <j@w1.fi>, Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless LAN cards based on ADMtek"
" ADM8211");
MODULE_SUPPORTED_DEVICE("ADM8211");
MODULE_LICENSE("GPL");
static unsigned int tx_ring_size = 16;
static unsigned int rx_ring_size = 16;
static int debug = 1;
module_param(tx_ring_size, uint, 0);
module_param(rx_ring_size, uint, 0);
module_param(debug, int, 0);
static const char *version = KERN_INFO "adm8211: "
"Copyright 2003, Jouni Malinen <j@w1.fi>; "
"Copyright 2004-2005, Michael Wu <flamingice@sourmilk.net>\n";
static struct pci_device_id adm8211_pci_id_table[] __devinitdata = {
/* ADMtek ADM8211 */
{ 0x10B7, 0x6000, PCI_ANY_ID, PCI_ANY_ID }, /* 3Com 3CRSHPW796 */
{ 0x1200, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ? */
{ 0x1317, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211A */
{ 0x1317, 0x8211, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211B/C */
{ 0 }
};
#define ADM8211_INTMASK \
(ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | \
ADM8211_IER_TDUIE | ADM8211_IER_GPTIE)
#define PLCP_SIGNAL_1M 0x0a
#define PLCP_SIGNAL_2M 0x14
#define PLCP_SIGNAL_5M5 0x37
#define PLCP_SIGNAL_11M 0x6e
/* RX status - stored in skb->cb so this structure must be 48 bytes or less */
struct adm8211_rx_status {
u8 rssi;
u8 rate;
};
struct adm8211_tx_hdr {
u8 da[6];
u8 signal; /* PLCP signal / TX rate in 100 Kbps */
u8 service;
__le16 frame_body_size;
__le16 frame_control;
__le16 plcp_frag_tail_len;
__le16 plcp_frag_head_len;
__le16 dur_frag_tail;
__le16 dur_frag_head;
u8 address4[6];
#define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0)
#define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1)
#define ADM8211_TXHDRCTL_MORE_DATA (1 << 2)
#define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */
#define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4)
#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5)
#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */
__le16 header_control;
/*
#ifdef BIG_ENDIAN
u32 retry_limit:8;
u32 reserved_0:8;
u32 frag_number:4;
u32 frag_threshold:12;
#else
u32 frag_threshold:12;
u32 frag_number:4;
u32 reserved_0:8;
u32 retry_limit:8;
#endif
*/
__le16 frag;
u8 reserved_0;
u8 retry_limit;
u32 wep2key0;
u32 wep2key1;
u32 wep2key2;
u32 wep2key3;
u8 keyid;
u8 entry_control; // huh??
u16 reserved_1;
u32 reserved_2;
} __attribute__ ((packed));
#define RX_COPY_BREAK 128
#define RX_PKT_SIZE 2500
/* Serial EEPROM reading for 93C66/93C46 */
#define EE_ENB (0x4000 | ADM8211_SPR_SRS | ADM8211_SPR_SCS)
#define EE_READ_CMD (6)
#define eeprom_delay() ADM8211_CSR_READ(SPR);
static u16 adm8211_eeprom_read_word(struct net_device *dev, unsigned int addr,
unsigned int addr_len)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int read_cmd = addr | (EE_READ_CMD << addr_len);
int i;
u16 retval = 0;
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS));
eeprom_delay();
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
eeprom_delay();
/* Shift the read command bits out. */
for (i = 4 + addr_len; i >= 0; i--) {
u32 dataval = EE_ENB | ((read_cmd & (1 << i)) ? ADM8211_SPR_SDI : 0);
ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval));
eeprom_delay();
ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval | ADM8211_SPR_SCLK));
eeprom_delay();
}
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
eeprom_delay();
for (i = 16; i > 0; i--) {
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB | ADM8211_SPR_SCLK));
eeprom_delay();
retval <<= 1;
if (ADM8211_CSR_READ(SPR) & __constant_cpu_to_le32(ADM8211_SPR_SDO))
retval |= 1;
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
eeprom_delay();
}
/* Terminate the EEPROM access. */
ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS));
return retval;
}
static int adm8211_read_eeprom(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int addr_len, words, i;
u16 cr49;
if (ADM8211_CSR_READ(CSR_TEST0) & __constant_cpu_to_le32(ADM8211_CSR_TEST0_EPTYP)) {
printk(KERN_DEBUG "%s (adm8211): EEPROM type: 93C66\n", pci_name(priv->pdev));
/* 256 * 16-bit = 512 bytes */
addr_len = 8;
words = 256;
} else {
printk(KERN_DEBUG "%s (adm8211): EEPROM type 93C46\n", pci_name(priv->pdev));
/* 64 * 16-bit = 128 bytes */
addr_len = 6;
words = 64;
}
priv->eeprom_len = words * 2;
priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL);
if (priv->eeprom == NULL)
return -ENOMEM;
for (i = 0; i < words; i++)
*((u16 *) &((u8 *)priv->eeprom)[i * 2]) =
adm8211_eeprom_read_word(dev, i, addr_len);
cr49 = le16_to_cpu(priv->eeprom->cr49);
priv->rf_type = (cr49 >> 3) & 0x7;
switch (priv->rf_type) {
case ADM8211_TYPE_INTERSIL:
case ADM8211_TYPE_RFMD:
case ADM8211_TYPE_MARVEL:
case ADM8211_TYPE_AIROHA:
case ADM8211_TYPE_ADMTEK:
break;
default:
if (priv->revid < ADM8211_REV_CA)
priv->rf_type = ADM8211_TYPE_RFMD;
else
priv->rf_type = ADM8211_TYPE_AIROHA;
printk(KERN_WARNING "%s (adm8211): Invalid or unsupported RFtype: %d, assuming %d\n",
pci_name(priv->pdev), (cr49 >> 3) & 0x7, priv->rf_type);
}
priv->bbp_type = cr49 & 0x7;
switch (priv->bbp_type) {
case ADM8211_TYPE_INTERSIL:
case ADM8211_TYPE_RFMD:
case ADM8211_TYPE_MARVEL:
case ADM8211_TYPE_AIROHA:
case ADM8211_TYPE_ADMTEK:
break;
default:
if (priv->revid < ADM8211_REV_CA)
priv->bbp_type = ADM8211_TYPE_RFMD;
else
priv->bbp_type = ADM8211_TYPE_ADMTEK;
printk(KERN_WARNING "%s (adm8211): Invalid or unsupported BBPtype: %d, assuming %d\n",
pci_name(priv->pdev), cr49 >> 3, priv->bbp_type);
}
if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) {
printk(KERN_WARNING "%s (adm8211): Invalid country code (%d) in EEPROM, assuming ETSI\n",
pci_name(priv->pdev), priv->eeprom->country_code);
priv->ieee80211.chan_range = cranges[2];
} else
priv->ieee80211.chan_range = cranges[priv->eeprom->country_code];
printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n",
pci_name(priv->pdev), (int)priv->ieee80211.chan_range.min, (int)priv->ieee80211.chan_range.max);
switch (priv->eeprom->specific_bbptype) {
case ADM8211_BBP_RFMD3000:
case ADM8211_BBP_RFMD3002:
case ADM8211_BBP_ADM8011:
priv->specific_bbptype = priv->eeprom->specific_bbptype;
break;
default:
if (priv->revid < ADM8211_REV_CA)
priv->specific_bbptype = ADM8211_BBP_RFMD3000;
else
priv->specific_bbptype = ADM8211_BBP_ADM8011;
printk(KERN_WARNING "%s (adm8211): Invalid or unsupported specific BBP: %d, assuming %d\n",
pci_name(priv->pdev), priv->eeprom->specific_bbptype, priv->specific_bbptype);
}
switch (priv->eeprom->specific_rftype) {
case ADM8211_RFMD2948:
case ADM8211_RFMD2958:
case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
case ADM8211_MAX2820:
case ADM8211_AL2210L:
priv->transceiver_type = priv->eeprom->specific_rftype;
break;
default:
if (priv->revid == ADM8211_REV_BA)
priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER;
else if (priv->revid == ADM8211_REV_CA)
priv->transceiver_type = ADM8211_AL2210L;
else if (priv->revid == ADM8211_REV_AB)
priv->transceiver_type = ADM8211_RFMD2948;
printk(KERN_WARNING "%s (adm8211): Invalid or unsupported transceiver: %d, assuming %d\n",
pci_name(priv->pdev), priv->eeprom->specific_rftype, priv->transceiver_type);
break;
}
printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d Transceiver=%d\n",
pci_name(priv->pdev), priv->rf_type, priv->bbp_type,
priv->specific_bbptype, priv->transceiver_type);
return 0;
}
static inline void adm8211_write_sram(struct net_device *dev, u32 addr, __le32 data)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(addr | ADM8211_WEPCTL_TABLE_WR |
(priv->revid < ADM8211_REV_BA ?
0 : ADM8211_WEPCTL_SEL_WEPTABLE )) );
ADM8211_CSR_READ(WEPCTL);
mdelay(1);
ADM8211_CSR_WRITE(WESK, data);
ADM8211_CSR_READ(WESK);
mdelay(1);
}
static void adm8211_write_sram_bytes(struct net_device *dev,
unsigned int addr, u8 *buf, unsigned int len)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
__le32 reg = ADM8211_CSR_READ(WEPCTL);
unsigned int i;
if (priv->revid < ADM8211_REV_BA) {
for (i = 0; i < len; i += 2) {
u16 val = buf[i] | buf[i + 1] << 8;
adm8211_write_sram(dev, addr + i / 2, cpu_to_le32(val));
}
} else {
for (i = 0; i < len; i += 4) {
u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) |
(buf[i + 2] << 16) | (buf[i + 3] << 24);
adm8211_write_sram(dev, addr + i / 4, cpu_to_le32(val));
}
}
ADM8211_CSR_WRITE(WEPCTL, reg);
}
static void adm8211_clear_sram(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
__le32 reg = ADM8211_CSR_READ(WEPCTL);
unsigned int addr;
for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++)
adm8211_write_sram(dev, addr, 0);
ADM8211_CSR_WRITE(WEPCTL, reg);
}
static void adm8211_set_rx_mode(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int i, bit_nr;
__le32 mc_filter[2];
struct dev_mc_list *mclist;
if (dev->flags & IFF_PROMISC) {
priv->nar |= ADM8211_NAR_PR;
priv->nar &= ~ADM8211_NAR_MM;
mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
} else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 32)) {
priv->nar &= ~ADM8211_NAR_PR;
priv->nar |= ADM8211_NAR_MM;
mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
} else {
priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
mc_filter[1] = mc_filter[0] = 0;
mclist = dev->mc_list;
for (i = 0; i < dev->mc_count; i++) {
if (!mclist)
break;
bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
bit_nr &= 0x3F;
mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31));
mclist = mclist->next;
}
}
ADM8211_IDLE_RX();
ADM8211_CSR_WRITE(MAR0, mc_filter[0]);
ADM8211_CSR_WRITE(MAR1, mc_filter[1]);
ADM8211_CSR_READ(NAR);
ADM8211_RESTORE();
}
static int adm8211_set_mac_address(struct net_device *dev, void *p)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct sockaddr *addr = p;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
if (!(dev->flags & IFF_UP))
return 0;
/* sure, we can change the MAC addr while running */
/* .. probably not a good idea, though */
ADM8211_IDLE_RX();
ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
ADM8211_RESTORE();
return 0;
}
static void adm8211_rx_skb(struct net_device *dev, struct sk_buff *skb)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_hdr *hdr;
struct adm8211_rx_status *stat;
struct iw_statistics *wstats = &priv->wstats;
struct ieee80211_rx_stats rx_status;
static const u8 rate[] = {10, 20, 55, 110, 220};
u16 fc;
stat = (struct adm8211_rx_status *) skb->cb;
/* FIX: this is a hack */
if (priv->revid < ADM8211_REV_CA)
wstats->qual.qual = stat->rssi;
else
wstats->qual.qual = 100 - stat->rssi;
wstats->qual.updated |= IW_QUAL_QUAL_UPDATED;
if (skb->len < 14)
goto drop;
/* TODO: Add crc checking here for cards/modes that need it */
memset(&rx_status, 0, sizeof(rx_status));
rx_status.rssi = wstats->qual.qual;
if (stat->rate <= 4)
rx_status.rate = rate[stat->rate];
hdr = (struct ieee80211_hdr *) skb->data;
fc = le16_to_cpu(hdr->frame_ctl);
/* TODO: drop all this once ieee80211 has radiotap support */
if (ieee->iw_mode == IW_MODE_MONITOR) {
struct avs_caphdr *chdr;
if (skb_headroom(skb) < sizeof(struct avs_caphdr)) {
if (pskb_expand_head(skb, sizeof(struct avs_caphdr), 0,
GFP_ATOMIC)) {
printk(KERN_DEBUG "%s: failed to allocate room for prism2 "
"header\n", dev->name);
goto drop;
}
}
memset(skb->cb, 0, sizeof(skb->cb));
chdr = (struct avs_caphdr *) skb_push(skb, sizeof(struct avs_caphdr));
chdr->version = __constant_cpu_to_be32(0x80211001);
chdr->length = cpu_to_be32(sizeof(struct avs_caphdr));
chdr->mactime = 0;
chdr->hosttime = cpu_to_be64(jiffies);
chdr->phytype = __constant_cpu_to_be32(4); /* phytype_dsss_dot11_b */
chdr->channel = cpu_to_be32(data->channel);
chdr->datarate = cpu_to_be32(rx_status.rate);
chdr->antenna = 0; /* TODO: once antenna setting is possible.. */
chdr->priority = 0; /* hmm, dunno if this is possible.. */
chdr->ssi_type = __constant_cpu_to_be32(3); /* Raw RSSI */
chdr->ssi_signal = cpu_to_be32(rx_status.rssi);
chdr->ssi_noise = __constant_cpu_to_be32(0xFFFFFFFF);
if (skb->len >= 14 + sizeof(struct avs_caphdr))
chdr->preamble = cpu_to_be32(
fc & WLAN_CAPABILITY_SHORT_PREAMBLE
? 2 : 1);
else
chdr->preamble = 0;
chdr->encoding = cpu_to_be32(1); /* CCK */
ieee->stats.rx_bytes += skb->len;
ieee->stats.rx_packets++;
skb->pkt_type = PACKET_OTHERHOST;
skb_reset_mac_header(skb);
netif_rx(skb);
dev->last_rx = jiffies;
return;
}
if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL ||
ieee80211_filter_duplicates(data->tuple, (struct ieee80211_hdr_3addr *) skb->data))
goto drop;
rx_status.received_channel = data->channel;
rx_status.freq = IEEE80211_24GHZ_BAND;
rx_status.mask = IEEE80211_STATMASK_RSSI;
rx_status.len = skb->len;
/* remove FCS */
if (dev->flags & IFF_PROMISC)
skb_trim(skb, skb->len - IEEE80211_FCS_LEN);
switch (WLAN_FC_GET_TYPE(fc)) {
case IEEE80211_FTYPE_MGMT:
ieee80211_rx_mgmt(&priv->ieee80211, skb, &rx_status);
break;
case IEEE80211_FTYPE_DATA:
memset(skb->cb, 0, sizeof(skb->cb));
ieee80211_rx(ieee, skb, &rx_status);
break;
}
return;
drop:
dev_kfree_skb(skb);
}
static void adm8211_rx_tasklet(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
struct adm8211_priv *priv = ieee80211_priv(dev);
struct sk_buff *skb;
while ((skb = skb_dequeue(&priv->rx_queue)) != NULL)
adm8211_rx_skb(dev, skb);
}
static void adm8211_interrupt_tci(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
unsigned dirty_tx;
#if 0
printk(KERN_DEBUG "TCI (dirty_tx=%d cur_tx=%d)\n",
priv->dirty_tx, priv->cur_tx);
#endif
spin_lock(&priv->lock);
for (dirty_tx = priv->dirty_tx;
priv->cur_tx - dirty_tx > 0; dirty_tx++) {
unsigned entry = dirty_tx % priv->tx_ring_size;
u32 status = le32_to_cpu(priv->tx_ring[entry].status);
if (status & TDES0_CONTROL_OWN ||
!(status & TDES0_CONTROL_DONE))
break;
if (status & TDES0_STATUS_ES) {
ieee->stats.tx_errors++;
if (status & (TDES0_STATUS_TUF | TDES0_STATUS_TRO))
ieee->stats.tx_fifo_errors++;
if (status & (TDES0_STATUS_TLT | TDES0_STATUS_SOFBR))
priv->wstats.discard.misc++;
if (status & TDES0_STATUS_TRT)
priv->wstats.discard.retries++;
}
pci_unmap_single(priv->pdev, priv->tx_buffers[entry].mapping,
priv->tx_buffers[entry].skb->len,
PCI_DMA_TODEVICE);
dev_kfree_skb_irq(priv->tx_buffers[entry].skb);
priv->tx_buffers[entry].skb = NULL;
}
if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 4 &&
netif_carrier_ok(dev))
netif_wake_queue(dev);
priv->dirty_tx = dirty_tx;
spin_unlock(&priv->lock);
}
static void adm8211_interrupt_rci(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
unsigned int entry = priv->cur_rx % priv->rx_ring_size;
u32 status;
unsigned pktlen;
struct sk_buff *skb, *newskb;
unsigned int limit = priv->rx_ring_size;
u8 rssi, rate;
#if 0
printk(KERN_DEBUG "RCI\n");
#endif
while (!(priv->rx_ring[entry].status &
__constant_cpu_to_le32(RDES0_STATUS_OWN))) {
if (limit-- == 0)
break;
status = le32_to_cpu(priv->rx_ring[entry].status);
rate = (status & RDES0_STATUS_RXDR) >> 12;
rssi = le32_to_cpu(priv->rx_ring[entry].length) &
RDES1_STATUS_RSSI;
#if 0
printk(KERN_DEBUG "%s: RX %02x RXDR=%d FL=%d RSSI=%d "
"%s%s%s%s\n",
dev->name, status, rate,
status & RDES0_STATUS_FL,
rssi,
status & RDES0_STATUS_ES ? "[ES]" : "",
status & RDES0_STATUS_FS ? "[FS]" : "",
status & RDES0_STATUS_LS ? "[LS]" : "",
status & RDES0_STATUS_CRC16E ? "[CRC16E]" : "");
#endif
pktlen = status & RDES0_STATUS_FL;
if (pktlen > RX_PKT_SIZE) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: too long frame (pktlen=%d)\n",
dev->name, pktlen);
pktlen = RX_PKT_SIZE;
}
if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) {
#if 0
printk(KERN_DEBUG "%s: dropped RX frame with error "
"(status=0x%x)\n", dev->name, status);
#endif
skb = NULL; /* old buffer will be reused */
ieee->stats.rx_errors++;
if (status & (RDES0_STATUS_SFDE |
RDES0_STATUS_SIGE | RDES0_STATUS_RXTOE))
priv->wstats.discard.misc++;
if (status & RDES0_STATUS_ICVE)
priv->wstats.discard.code++;
if (status & (RDES0_STATUS_CRC16E | RDES0_STATUS_CRC32E))
ieee->stats.rx_crc_errors++;
} else if (pktlen < RX_COPY_BREAK) {
skb = dev_alloc_skb(pktlen);
if (skb) {
skb->dev = dev;
pci_dma_sync_single_for_cpu(
priv->pdev,
priv->rx_buffers[entry].mapping,
pktlen, PCI_DMA_FROMDEVICE);
memcpy(skb_put(skb, pktlen),
priv->rx_buffers[entry].skb->tail,
pktlen);
pci_dma_sync_single_for_device(
priv->pdev,
priv->rx_buffers[entry].mapping,
RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
}
} else {
newskb = dev_alloc_skb(RX_PKT_SIZE);
if (newskb) {
newskb->dev = dev;
skb = priv->rx_buffers[entry].skb;
skb_put(skb, pktlen);
pci_unmap_single(
priv->pdev,
priv->rx_buffers[entry].mapping,
RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
priv->rx_buffers[entry].skb = newskb;
priv->rx_buffers[entry].mapping =
pci_map_single(priv->pdev,
newskb->tail,
RX_PKT_SIZE,
PCI_DMA_FROMDEVICE);
} else {
skb = NULL;
ieee->stats.rx_dropped++;
}
priv->rx_ring[entry].buffer1 =
cpu_to_le32(priv->rx_buffers[entry].mapping);
}
priv->rx_ring[entry].status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
priv->rx_ring[entry].length =
cpu_to_le32(RX_PKT_SIZE |
(entry == priv->rx_ring_size - 1 ?
RDES1_CONTROL_RER : 0));
if (skb) {
struct adm8211_rx_status *stat =
(struct adm8211_rx_status *) skb->cb;
#if 0
{
int i;
printk(KERN_DEBUG "RX[%d/%d]",
pktlen, skb->len);
for (i = 0; i < skb->len; i++)
printk(" %02x", skb->data[i]);
printk("\n");
}
#endif
stat->rssi = rssi;
stat->rate = rate;
skb_reset_mac_header(skb);
skb->protocol = __constant_htons(ETH_P_802_2);
skb_queue_tail(&priv->rx_queue, skb);
tasklet_schedule(&priv->rx_tasklet);
}
entry = (++priv->cur_rx) % priv->rx_ring_size;
}
ieee->stats.rx_missed_errors += le32_to_cpu(ADM8211_CSR_READ(LPC)) & 0xFFFF;
}
static irqreturn_t adm8211_interrupt(int irq, void *dev_id)
{
#define ADM8211_INT(x) if (unlikely(stsr & ADM8211_STSR_ ## x)) printk(KERN_DEBUG "%s: " #x "\n", dev->name)
struct net_device *dev = dev_id;
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int count = 0;
u32 stsr;
do {
stsr = le32_to_cpu(ADM8211_CSR_READ(STSR));
ADM8211_CSR_WRITE(STSR, cpu_to_le32(stsr));
if (stsr == 0xffffffff)
return IRQ_HANDLED;
if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS)))
break;
/*if (stsr & ADM8211_STSR_TBTT) {
//priv->ieee80211.beacon_sync(dev);
if (net_ratelimit())
printk(KERN_DEBUG "%s: TBTT\n", dev->name);
}*/
if (stsr & ADM8211_STSR_RCI)
adm8211_interrupt_rci(dev);
if (stsr & ADM8211_STSR_TCI)
adm8211_interrupt_tci(dev);
if (stsr & (ADM8211_STSR_RCI | ADM8211_STSR_TCI))
mod_timer(&priv->timer, jiffies + 5*HZ);
if ((stsr & (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff))
!= (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) {
if (stsr & ADM8211_STSR_LinkOn) {
printk(KERN_DEBUG "%s: LinkOn\n", dev->name);
priv->ieee80211.flags |= LINK_ON;
}
if (stsr & ADM8211_STSR_LinkOff) {
printk(KERN_DEBUG "%s: LinkOff\n", dev->name);
priv->ieee80211.flags &= ~LINK_ON;
if (dev->flags & IFF_UP)
ieee80211_linkcheck(&priv->ieee80211);
}
}
ADM8211_INT(PCF);
ADM8211_INT(BCNTC);
ADM8211_INT(GPINT);
ADM8211_INT(ATIMTC);
ADM8211_INT(TSFTF);
ADM8211_INT(TSCZ);
ADM8211_INT(SQL);
ADM8211_INT(WEPTD);
ADM8211_INT(ATIME);
/*ADM8211_INT(TBTT);*/
ADM8211_INT(TEIS);
ADM8211_INT(FBE);
ADM8211_INT(REIS);
ADM8211_INT(GPTT);
ADM8211_INT(RPS);
ADM8211_INT(RDU);
ADM8211_INT(TUF);
/*ADM8211_INT(TRT);*/
/*ADM8211_INT(TLT);*/
/*ADM8211_INT(TDU);*/
ADM8211_INT(TPS);
} while (count++ < 20);
return IRQ_RETVAL(count);
#undef ADM8211_INT
}
#define WRITE_SYN(valmask,valshift,addrmask,addrshift,bits,prewrite,postwrite) do {\
struct adm8211_priv *priv = ieee80211_priv(dev);\
unsigned int i;\
u32 reg, bitbuf;\
\
value &= valmask;\
addr &= addrmask;\
bitbuf = (value << valshift) | (addr << addrshift);\
\
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_1));\
ADM8211_CSR_READ(SYNRF);\
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_0));\
ADM8211_CSR_READ(SYNRF);\
\
if (prewrite) {\
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_WRITE_SYNDATA_0));\
ADM8211_CSR_READ(SYNRF);\
}\
\
for (i = 0; i <= bits; i++) {\
if ( bitbuf & (1 << (bits - i)) )\
reg = ADM8211_SYNRF_WRITE_SYNDATA_1;\
else\
reg = ADM8211_SYNRF_WRITE_SYNDATA_0;\
\
ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg));\
ADM8211_CSR_READ(SYNRF);\
\
ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_1));\
ADM8211_CSR_READ(SYNRF);\
ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_0));\
ADM8211_CSR_READ(SYNRF);\
}\
\
if (postwrite == 1) {\
ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_0));\
ADM8211_CSR_READ(SYNRF);\
}\
if (postwrite == 2) {\
ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_1));\
ADM8211_CSR_READ(SYNRF);\
}\
\
ADM8211_CSR_WRITE(SYNRF, 0);\
ADM8211_CSR_READ(SYNRF);\
} while (0)
static void adm8211_rf_write_syn_max2820 (struct net_device *dev, u16 addr, u32 value)
{
WRITE_SYN(0x00FFF, 0, 0x0F, 12, 15, 1, 1);
}
static void adm8211_rf_write_syn_al2210l (struct net_device *dev, u16 addr, u32 value)
{
WRITE_SYN(0xFFFFF, 4, 0x0F, 0, 23, 1, 1);
}
static void adm8211_rf_write_syn_rfmd2958 (struct net_device *dev, u16 addr, u32 value)
{
WRITE_SYN(0x3FFFF, 0, 0x1F, 18, 23, 0, 1);
}
static void adm8211_rf_write_syn_rfmd2948 (struct net_device *dev, u16 addr, u32 value)
{
WRITE_SYN(0x0FFFF, 4, 0x0F, 0, 21, 0, 2);
}
static int adm8211_write_bbp(struct net_device *dev, u8 addr, u8 data)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int timeout;
u32 reg;
timeout = 10;
while (timeout > 0) {
reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD)))
break;
timeout--;
mdelay(2);
}
if (timeout == 0) {
printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed prewrite "
"(reg=0x%08x)\n",
dev->name, addr, data, reg);
return -ETIMEDOUT;
}
switch (priv->bbp_type) {
case ADM8211_TYPE_INTERSIL:
reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */
break;
case ADM8211_TYPE_RFMD:
reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
(0x01<<18);
break;
case ADM8211_TYPE_ADMTEK:
reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
(0x05<<18);
break;
}
reg |= ADM8211_BBPCTL_WR | (addr << 8) | data;
ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg));
timeout = 10;
while (timeout > 0) {
reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
if (!(reg & ADM8211_BBPCTL_WR))
break;
timeout--;
mdelay(2);
}
if (timeout == 0) {
ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) &
__constant_cpu_to_le32(~ADM8211_BBPCTL_WR));
printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed postwrite "
"(reg=0x%08x)\n",
dev->name, addr, data, reg);
return -ETIMEDOUT;
}
return 0;
}
static int adm8211_rf_set_channel(struct net_device *dev, unsigned int channel)
{
static const u32 adm8211_rfmd2958_reg5[] =
{0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340,
0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7};
static const u32 adm8211_rfmd2958_reg6[] =
{0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000,
0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745};
struct adm8211_priv *priv = ieee80211_priv(dev);
u8 ant_power = priv->ant_power > 0x3F ?
priv->eeprom->antenna_power[channel-1] : priv->ant_power;
u8 tx_power = priv->tx_power > 0x3F ?
priv->eeprom->tx_power[channel-1] : priv->tx_power;
u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ?
priv->eeprom->lpf_cutoff[channel-1] : priv->lpf_cutoff;
u8 lnags_thresh = priv->lnags_threshold == 0xFF ?
priv->eeprom->lnags_threshold[channel-1] : priv->lnags_threshold;
u32 reg;
if (channel < 1 || channel > 14)
return -EINVAL;
ADM8211_IDLE();
/* Program synthesizer to new channel */
switch (priv->transceiver_type) {
case ADM8211_RFMD2958:
case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007);
adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033);
adm8211_rf_write_syn_rfmd2958(dev, 0x05,
adm8211_rfmd2958_reg5[channel-1]);
adm8211_rf_write_syn_rfmd2958(dev, 0x06,
adm8211_rfmd2958_reg6[channel-1]);
break;
case ADM8211_RFMD2948:
adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2);
adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN,
SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB);
adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0);
adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV,
(channel == 14 ? 2110 : (2033 + (channel * 5))));
adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496);
adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44);
adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44);
break;
case ADM8211_MAX2820:
adm8211_rf_write_syn_max2820(dev, 0x3,
(channel == 14 ? 0x054 : (0x7 + (channel * 5))));
break;
case ADM8211_AL2210L:
adm8211_rf_write_syn_al2210l(dev, 0x0,
(channel == 14 ? 0x229B4 : (0x22967 + (channel * 5))));
break;
default:
printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
dev->name, priv->transceiver_type);
break;
}
/* write BBP regs */
if (priv->bbp_type == ADM8211_TYPE_RFMD) {
/* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */
/* TODO: remove if SMC 2635W doesn't need this */
if (priv->transceiver_type == ADM8211_RFMD2948) {
reg = le32_to_cpu(ADM8211_CSR_READ(GPIO));
reg &= 0xfffc0000;
reg |= ADM8211_CSR_GPIO_EN0;
if (channel != 14)
reg |= ADM8211_CSR_GPIO_O0;
ADM8211_CSR_WRITE(GPIO, cpu_to_le32(reg));
}
if (priv->transceiver_type == ADM8211_RFMD2958) {
/* set PCNT2 */
adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100);
/* set PCNT1 P_DESIRED/MID_BIAS */
reg = le16_to_cpu(priv->eeprom->cr49);
reg >>= 13;
reg <<= 15;
reg |= ant_power<<9;
adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg);
/* set TXRX TX_GAIN */
adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 |
(priv->revid < ADM8211_REV_CA ? tx_power : 0));
} else {
reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD));
reg &= 0xff00ffff;
reg |= tx_power<<18;
ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg));
}
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF |
ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST));
ADM8211_CSR_READ(SYNRF);
mdelay(30);
/* RF3000 BBP */
if (priv->transceiver_type != ADM8211_RFMD2958)
adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT,
tx_power<<2);
adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff);
adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh);
adm8211_write_bbp(dev, 0x1c, priv->revid == ADM8211_REV_BA
? priv->eeprom->cr28 : 0);
adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
ADM8211_CSR_WRITE(SYNRF, 0);
} else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) { /* Nothing to do for ADMtek BBP */
printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
dev->name, priv->bbp_type);
}
ADM8211_RESTORE();
/* update current channel for adhoc (and maybe AP mode) */
reg = le32_to_cpu(ADM8211_CSR_READ(CAP0));
reg &= ~0xF;
reg |= channel;
ADM8211_CSR_WRITE(CAP0, cpu_to_le32(reg));
return 0;
}
static void adm8211_write_wepkey(struct net_device *dev, unsigned int index, unsigned int len, u8 *key)
{
#define ADM8211_WEP_ENABLE (1 << 7)
#define ADM8211_WEP_A_104 (1 << 6)
#define ADM8211_WEP_B_104 (1 << 4)
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int addr;
u8 buf[32];
memset(buf, 0, sizeof(buf));
if (priv->revid < ADM8211_REV_BA) {
addr = (index * 7) + ADM8211_SRAM_A_SHARE_KEY;
/* control entry */
if (len > 5)
buf[1] = ADM8211_WEP_ENABLE | ADM8211_WEP_A_104;
else if (len > 0)
buf[1] = ADM8211_WEP_ENABLE;
if (len > 0) {
buf[0] = key[0];
memcpy(buf + 2, key + 1, len - 1);
}
adm8211_write_sram_bytes(dev, addr, buf, 14);
} else {
addr = (index * 5) + ADM8211_SRAM_B_SHARE_KEY;
/* control entry */
if (len > 5)
*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE | ADM8211_WEP_B_104);
else if (len > 0)
*(__le32 *)buf = cpu_to_le32(ADM8211_WEP_ENABLE);
if (len > 0)
memcpy(buf + 4, key, len);
adm8211_write_sram_bytes(dev, addr, buf, 17);
}
}
static void adm8211_write_weptable(struct net_device *dev)
{
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_crypt_data *crypt;
unsigned int i;
for (i = 0; i < WEP_KEYS; i++) {
crypt = ieee->crypt[i];
if (crypt != NULL && strcmp(crypt->ops->name, "WEP") == 0) {
u8 key[14];
int keylen = crypt->ops->get_key(key, 14, NULL, crypt->priv);
adm8211_write_wepkey(dev, i, keylen, key);
} else
adm8211_write_wepkey(dev, i, 0, NULL);
}
}
static void adm8211_update_wep(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
u32 reg;
ADM8211_IDLE();
reg = le32_to_cpu(ADM8211_CSR_READ(MACTEST));
if (!ieee->host_encrypt) {
reg &= ~(3<<20);
reg |= (1<<22);
reg |= (ieee->tx_keyidx << 20);
} else
reg &= ~(7<<20);
ADM8211_CSR_WRITE(MACTEST, cpu_to_le32(reg));
reg = le32_to_cpu(ADM8211_CSR_READ(WEPCTL));
if (!ieee->host_encrypt)
reg |= ADM8211_WEPCTL_WEPENABLE;
else
reg &= ~ADM8211_WEPCTL_WEPENABLE;
/* no working hardware WEP RX decryption on the ADM8211A */
if (ieee->host_decrypt)
reg |= ADM8211_WEPCTL_WEPRXBYP;
else
reg &= ~ADM8211_WEPCTL_WEPRXBYP;
ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(reg));
ADM8211_RESTORE();
}
static void adm8211_set_security(struct net_device *dev, struct ieee80211_security *sec)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
int changed = 0;
/* don't know how to do TKIP or CCMP in hardware yet */
/* ADM8211A only supports WEP, don't know about ADM8211B */
if (sec->level != SEC_LEVEL_1 || !sec->enabled) {
if (ieee->host_encrypt == 0)
changed = 1;
ieee->host_decrypt = ieee->host_mc_decrypt = ieee->host_encrypt_msdu = ieee->host_encrypt = 1;
} else {
if (ieee->host_encrypt == 1)
changed = 1;
ieee->host_encrypt = ieee->host_encrypt_msdu = 0;
if (priv->revid >= ADM8211_REV_BA)
ieee->host_decrypt = ieee->host_mc_decrypt = 0;
}
if (sec->level == SEC_LEVEL_1)
adm8211_write_weptable(dev);
if (changed && dev->flags & IFF_UP)
adm8211_update_wep(dev);
memcpy(&ieee->sec, sec, sizeof(struct ieee80211_security));
}
void adm8211_update_mode(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
ADM8211_IDLE();
priv->soft_rx_crc = 0;
switch (ieee->iw_mode) {
case IW_MODE_INFRA:
priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA);
priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR;
ADM8211_CSR_WRITE(CAP1, data->capab<<16);
break;
case IW_MODE_ADHOC:
priv->nar &= ~ADM8211_NAR_PR;
priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR;
ADM8211_CSR_WRITE(CAP1, cpu_to_le32(data->capab<<16));
/* don't trust the error bits on rev 0x20 and up in adhoc */
if (priv->revid >= ADM8211_REV_BA)
priv->soft_rx_crc = 1;
break;
case IW_MODE_MONITOR:
priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST);
priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR;
ADM8211_CSR_WRITE(CAP1, 0);
break;
}
ADM8211_RESTORE();
}
static void adm8211_hw_init_syn(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
switch (priv->transceiver_type) {
case ADM8211_RFMD2958:
case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
/* comments taken from ADMtek driver */
/* Reset RF2958 after power on */
adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000);
/* Initialize RF VCO Core Bias to maximum */
adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F);
/* Initialize IF PLL */
adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03);
/* Initialize IF PLL Coarse Tuning */
adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F);
/* Initialize RF PLL */
adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403);
/* Initialize RF PLL Coarse Tuning */
adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F);
/* Initialize TX gain and filter BW (R9) */
adm8211_rf_write_syn_rfmd2958(dev, 0x09,
(priv->transceiver_type == ADM8211_RFMD2958
? 0x10050 : 0x00050) );
/* Initialize CAL register */
adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8);
break;
case ADM8211_MAX2820:
adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E);
adm8211_rf_write_syn_max2820(dev, 0x2, 0x001);
adm8211_rf_write_syn_max2820(dev, 0x3, 0x054);
adm8211_rf_write_syn_max2820(dev, 0x4, 0x310);
adm8211_rf_write_syn_max2820(dev, 0x5, 0x000);
break;
case ADM8211_AL2210L:
adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C);
adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB);
adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F);
adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9);
adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280);
adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641);
adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130);
adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000);
adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F);
adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C);
adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000);
adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000);
break;
case ADM8211_RFMD2948:
default:
break;
}
}
static int adm8211_hw_init_bbp(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg;
/* write addresses */
if (priv->bbp_type == ADM8211_TYPE_INTERSIL) {
ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x100E0C0A));
ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00007c7e));
ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x00100000));
} else if (priv->bbp_type == ADM8211_TYPE_RFMD ||
priv->bbp_type == ADM8211_TYPE_ADMTEK) {
/* check specific BBP type */
switch (priv->specific_bbptype) {
case ADM8211_BBP_RFMD3000:
case ADM8211_BBP_RFMD3002:
ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x00009101));
ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00000301));
break;
case ADM8211_BBP_ADM8011:
ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x00008903));
ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00001716));
reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
reg &= ~ADM8211_BBPCTL_TYPE;
reg |= 0x5 << 18;
ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg));
break;
}
switch (priv->revid) {
case ADM8211_REV_CA:
if (priv->transceiver_type == ADM8211_RFMD2958 ||
priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
priv->transceiver_type == ADM8211_RFMD2948)
ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x1 << 22));
else if (priv->transceiver_type == ADM8211_MAX2820 ||
priv->transceiver_type == ADM8211_AL2210L)
ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x3 << 22));
break;
case ADM8211_REV_BA:
reg = le32_to_cpu(ADM8211_CSR_READ(MMIRD1));
reg &= 0x0000FFFF;
reg |= 0x7e100000;
ADM8211_CSR_WRITE(MMIRD1, cpu_to_le32(reg));
break;
case ADM8211_REV_AB:
case ADM8211_REV_AF:
default:
ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x7e100000));
break;
}
/* For RFMD */
ADM8211_CSR_WRITE(MACTEST, __constant_cpu_to_le32(0x800));
}
adm8211_hw_init_syn(dev);
/* Set RF Power control IF pin to PE1+PHYRST# */
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF |
ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST));
ADM8211_CSR_READ(SYNRF);
mdelay(20);
/* write BBP regs */
if (priv->bbp_type == ADM8211_TYPE_RFMD) {
/* RF3000 BBP */
/* another set:
* 11: c8
* 14: 14
* 15: 50 (chan 1..13; chan 14: d0)
* 1c: 00
* 1d: 84
*/
adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80);
adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); /* antenna selection: diversity */
adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74);
adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38);
adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40);
if (priv->eeprom->major_version < 2) {
adm8211_write_bbp(dev, 0x1c, 0x00);
adm8211_write_bbp(dev, 0x1d, 0x80);
} else {
if (priv->revid == ADM8211_REV_BA)
adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28);
else
adm8211_write_bbp(dev, 0x1c, 0x00);
adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
}
} else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) {
adm8211_write_bbp(dev, 0x00, 0xFF); /* reset baseband */
adm8211_write_bbp(dev, 0x07, 0x0A); /* antenna selection: diversity */
/* TODO: find documentation for this */
switch (priv->transceiver_type) {
case ADM8211_RFMD2958:
case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
adm8211_write_bbp(dev, 0x00, 0x00);
adm8211_write_bbp(dev, 0x01, 0x00);
adm8211_write_bbp(dev, 0x02, 0x00);
adm8211_write_bbp(dev, 0x03, 0x00);
adm8211_write_bbp(dev, 0x06, 0x0f);
adm8211_write_bbp(dev, 0x09, 0x00);
adm8211_write_bbp(dev, 0x0a, 0x00);
adm8211_write_bbp(dev, 0x0b, 0x00);
adm8211_write_bbp(dev, 0x0c, 0x00);
adm8211_write_bbp(dev, 0x0f, 0xAA);
adm8211_write_bbp(dev, 0x10, 0x8c);
adm8211_write_bbp(dev, 0x11, 0x43);
adm8211_write_bbp(dev, 0x18, 0x40);
adm8211_write_bbp(dev, 0x20, 0x23);
adm8211_write_bbp(dev, 0x21, 0x02);
adm8211_write_bbp(dev, 0x22, 0x28);
adm8211_write_bbp(dev, 0x23, 0x30);
adm8211_write_bbp(dev, 0x24, 0x2d);
adm8211_write_bbp(dev, 0x28, 0x35);
adm8211_write_bbp(dev, 0x2a, 0x8c);
adm8211_write_bbp(dev, 0x2b, 0x81);
adm8211_write_bbp(dev, 0x2c, 0x44);
adm8211_write_bbp(dev, 0x2d, 0x0A);
adm8211_write_bbp(dev, 0x29, 0x40);
adm8211_write_bbp(dev, 0x60, 0x08);
adm8211_write_bbp(dev, 0x64, 0x01);
break;
case ADM8211_MAX2820:
adm8211_write_bbp(dev, 0x00, 0x00);
adm8211_write_bbp(dev, 0x01, 0x00);
adm8211_write_bbp(dev, 0x02, 0x00);
adm8211_write_bbp(dev, 0x03, 0x00);
adm8211_write_bbp(dev, 0x06, 0x0f);
adm8211_write_bbp(dev, 0x09, 0x05);
adm8211_write_bbp(dev, 0x0a, 0x02);
adm8211_write_bbp(dev, 0x0b, 0x00);
adm8211_write_bbp(dev, 0x0c, 0x0f);
adm8211_write_bbp(dev, 0x0f, 0x55);
adm8211_write_bbp(dev, 0x10, 0x8d);
adm8211_write_bbp(dev, 0x11, 0x43);
adm8211_write_bbp(dev, 0x18, 0x4a);
adm8211_write_bbp(dev, 0x20, 0x20);
adm8211_write_bbp(dev, 0x21, 0x02);
adm8211_write_bbp(dev, 0x22, 0x23);
adm8211_write_bbp(dev, 0x23, 0x30);
adm8211_write_bbp(dev, 0x24, 0x2d);
adm8211_write_bbp(dev, 0x2a, 0x8c);
adm8211_write_bbp(dev, 0x2b, 0x81);
adm8211_write_bbp(dev, 0x2c, 0x44);
adm8211_write_bbp(dev, 0x29, 0x4a);
adm8211_write_bbp(dev, 0x60, 0x2b);
adm8211_write_bbp(dev, 0x64, 0x01);
break;
case ADM8211_AL2210L:
adm8211_write_bbp(dev, 0x00, 0x00);
adm8211_write_bbp(dev, 0x01, 0x00);
adm8211_write_bbp(dev, 0x02, 0x00);
adm8211_write_bbp(dev, 0x03, 0x00);
adm8211_write_bbp(dev, 0x06, 0x0f);
adm8211_write_bbp(dev, 0x07, 0x05);
adm8211_write_bbp(dev, 0x08, 0x03);
adm8211_write_bbp(dev, 0x09, 0x00);
adm8211_write_bbp(dev, 0x0a, 0x00);
adm8211_write_bbp(dev, 0x0b, 0x00);
adm8211_write_bbp(dev, 0x0c, 0x10);
adm8211_write_bbp(dev, 0x0f, 0x55);
adm8211_write_bbp(dev, 0x10, 0x8d);
adm8211_write_bbp(dev, 0x11, 0x43);
adm8211_write_bbp(dev, 0x18, 0x4a);
adm8211_write_bbp(dev, 0x20, 0x20);
adm8211_write_bbp(dev, 0x21, 0x02);
adm8211_write_bbp(dev, 0x22, 0x23);
adm8211_write_bbp(dev, 0x23, 0x30);
adm8211_write_bbp(dev, 0x24, 0x2d);
adm8211_write_bbp(dev, 0x2a, 0xaa);
adm8211_write_bbp(dev, 0x2b, 0x81);
adm8211_write_bbp(dev, 0x2c, 0x44);
adm8211_write_bbp(dev, 0x29, 0xfa);
adm8211_write_bbp(dev, 0x60, 0x2d);
adm8211_write_bbp(dev, 0x64, 0x01);
break;
case ADM8211_RFMD2948:
break;
default:
printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
dev->name, priv->transceiver_type);
break;
}
} else {
printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
dev->name, priv->bbp_type);
}
ADM8211_CSR_WRITE(SYNRF, 0);
/* Set RF CAL control source to MAC control */
reg = le32_to_cpu(ADM8211_CSR_READ(SYNCTL));
reg |= ADM8211_SYNCTL_SELCAL;
ADM8211_CSR_WRITE(SYNCTL, cpu_to_le32(reg));
return 0;
}
static void adm8211_set_beacon (struct net_device *dev) {
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
unsigned int blen, len, rem;
u32 reg;
if (ieee->iw_mode == IW_MODE_ADHOC)
blen = 24 +
8 + 2 + 2 + 2 + data->ssid_len + 2 + data->num_supp_rates +
3 + 4 + IEEE80211_FCS_LEN;
else
blen = 0;
if (priv->revid < ADM8211_REV_BA) {
/* 11M PLCP length */
blen *= 8;
rem = blen % 11;
len = blen / 11;
if (rem) {
len++;
if (rem <= 3)
len |= 1<<7;
}
reg = len << 16;
/* 5.5M PLCP length */
rem = (blen*2) % 11;
len = (blen*2) / 11;
if (rem)
len++;
reg |= len << 8;
reg |= blen;
ADM8211_CSR_WRITE(BCNT, cpu_to_le32(reg));
} else {
len = blen;
rem = (blen*80) % priv->plcp_signal;
len = (blen*80) / priv->plcp_signal;
if (rem) {
len++;
if (data->rate == 0x16 && ((blen*8) % 11) <= 3)
len |= 1<<15;
}
len &= 0xFFFF;
ADM8211_CSR_WRITE(BCNT, cpu_to_le32(len));
reg = le32_to_cpu(ADM8211_CSR_READ(MMIRD1));
reg &= 0xFFFF0000;
reg |= len;
ADM8211_CSR_WRITE(MMIRD1, cpu_to_le32(reg));
}
}
int adm8211_set_rate(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
u32 reg;
int i = 0;
u8 rate_buf[12] = {0};
/* write supported rates */
if (priv->revid != ADM8211_REV_BA) {
rate_buf[0] = data->num_supp_rates;
for (i = 0; i < data->num_supp_rates; i++)
rate_buf[i+1] = data->supp_rates[i] |
(data->supp_rates[i] <= data->rate ? 0x80 : 0);
} else {
/* workaround for rev BA specific bug */
rate_buf[0]=4;
rate_buf[1]=0x82;
rate_buf[2]=0x04;
rate_buf[3]=0x0b;
rate_buf[4]=0x16;
}
adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, data->num_supp_rates+1);
priv->plcp_signal = data->rate * 5;
reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD)) & 0x00FFFFFF; /* keep bits 0-23 */
reg |= (1 << 15); /* short preamble */
reg |= priv->plcp_signal << 24;
ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg));
/* MTMLT = 512 TU (max TX MSDU lifetime)
* BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate)
* SRTYLIM = 224 (short retry limit, value in TX header used by default) */
ADM8211_CSR_WRITE(TXLMT, cpu_to_le32((512<<16) | (priv->plcp_signal<<8) | (224<<0)));
if (priv->revid >= ADM8211_REV_BA)
adm8211_set_beacon(dev);
return 0;
}
void adm8211_update_powersave(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg = le32_to_cpu(ADM8211_CSR_READ(FRCTL)) & ADM8211_FRCTL_AID;
switch (priv->powersave) {
case PS_OFF:
reg &= ~ADM8211_FRCTL_PWRMGT;
case PS_FAST:
reg |= ADM8211_FRCTL_PWRMGT;
}
ADM8211_CSR_WRITE(FRCTL, cpu_to_le32(reg));
}
static void adm8211_hw_init(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg;
u8 cacheline;
reg = le32_to_cpu(ADM8211_CSR_READ(PAR));
reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME;
reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL);
if (!pci_set_mwi(priv->pdev)) {
reg |= (0x1<<24);
pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cacheline);
switch (cacheline) {
case 0x8: reg |= (0x1<<14);
break;
case 0x16: reg |= (0x2<<14);
break;
case 0x32: reg |= (0x3<<14);
break;
default: reg |= (0x0<<14);
break;
}
}
ADM8211_CSR_WRITE(PAR, cpu_to_le32(reg));
reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
reg &= ~(0xF<<28);
reg |= ((1 << 28) | (1 << 31));
ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
/* lose link after 4 lost beacons */
reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE;
ADM8211_CSR_WRITE(WCSR, cpu_to_le32(reg));
/* Disable APM, enable receive FIFO threshold, and set drain receive
* threshold to store-and-forward */
reg = le32_to_cpu(ADM8211_CSR_READ(CMDR));
reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT);
reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF;
ADM8211_CSR_WRITE(CMDR, cpu_to_le32(reg));
adm8211_set_rate(dev);
/* 4-bit values:
* PWR1UP = 8 * 2 ms
* PWR0PAPE = 8 us or 5 us
* PWR1PAPE = 1 us or 3 us
* PWR0TRSW = 5 us
* PWR1TRSW = 12 us
* PWR0PE2 = 13 us
* PWR1PE2 = 1 us
* PWR0TXPE = 8 or 6 */
if (priv->revid < ADM8211_REV_CA)
ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8815cd18));
else
ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8535cd16));
/* Enable store and forward for transmit */
priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB;
ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar));
/* Reset RF */
ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_RADIO));
ADM8211_CSR_READ(SYNRF);
mdelay(10);
ADM8211_CSR_WRITE(SYNRF, 0);
ADM8211_CSR_READ(SYNRF);
mdelay(5);
/* Set CFP Max Duration to 0x10 TU */
reg = le32_to_cpu(ADM8211_CSR_READ(CFPP));
reg &= ~(0xffff<<8);
reg |= 0x0010<<8;
ADM8211_CSR_WRITE(CFPP, cpu_to_le32(reg));
/* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us
* TUCNT = 0x3ff - Tu counter 1024 us */
ADM8211_CSR_WRITE(TOFS0, __constant_cpu_to_le32((0x16 << 24) | 0x3ff));
/* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us),
* DIFS=50 us, EIFS=100 us */
if (priv->revid < ADM8211_REV_CA)
ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32(
(20 << 23) | (110 << 15) |
(50 << 9) | 100));
else
ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32(
(20 << 23) | (24 << 15) |
(50 << 9) | 100));
/* PCNT = 1 (MAC idle time awake/sleep, unit S)
* RMRD = 2346 * 8 + 1 us (max RX duration) */
ADM8211_CSR_WRITE(RMD, __constant_cpu_to_le32((1 << 16) | 18769));
/* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */
ADM8211_CSR_WRITE(RSPT, __constant_cpu_to_le32(0xffffff00));
/* Initialize BBP (and SYN) */
adm8211_hw_init_bbp(dev);
/* make sure interrupts are off */
ADM8211_CSR_WRITE(IER, 0);
/* ACK interrupts */
ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR));
/* Setup WEP */
adm8211_update_wep(dev);
/* Clear the missed-packet counter. */
ADM8211_CSR_READ(LPC);
/* set mac address */
ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
//adm8211_update_powersave(dev);
}
static int adm8211_hw_reset(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg;
__le32 tmp;
int timeout = 50;
/* Power-on issue */
/* TODO: check if this is necessary */
ADM8211_CSR_WRITE(FRCTL, 0);
/* Reset the chip */
tmp = ADM8211_CSR_READ(PAR);
ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR);
while ((ADM8211_CSR_READ(PAR) & __constant_cpu_to_le32(ADM8211_PAR_SWR)) && timeout--)
mdelay(100);
if (timeout <= 0)
return -ETIMEDOUT;
ADM8211_CSR_WRITE(PAR, tmp);
if (priv->revid == ADM8211_REV_BA &&
( priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER
|| priv->transceiver_type == ADM8211_RFMD2958)) {
reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
reg |= (1 << 4) | (1 << 5);
ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
} else if (priv->revid == ADM8211_REV_CA) {
reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
reg &= ~((1 << 4) | (1 << 5));
ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
}
ADM8211_CSR_WRITE(FRCTL, 0);
reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST0));
reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */
ADM8211_CSR_WRITE(CSR_TEST0, cpu_to_le32(reg));
adm8211_clear_sram(dev);
return 0;
}
static void adm8211_set_tbtt(struct net_device *dev, u16 tbtt)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
/* TSFTOFSR (RX TSFT Offset) = 1 us
* TBTTPRE (prediction time) = tbtt TU
* TBTTOFS (Wake up time offset before TBTT) = 20 TU */
ADM8211_CSR_WRITE(TOFS1, cpu_to_le32((1 << 24) | (tbtt << 8) | 20));
ADM8211_CSR_READ(TOFS1);
}
static u64 adm8211_get_tsft(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 tsftl;
u64 tsft;
tsftl = le32_to_cpu(ADM8211_CSR_READ(TSFTL));
tsft = le32_to_cpu(ADM8211_CSR_READ(TSFTH));
tsft <<= 32;
tsft |= tsftl;
return tsft;
}
static void adm8211_set_interval(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
u32 reg;
/* BP (beacon interval) = data->beacon_interval
* LI (listen interval) = data->listen_interval (in beacon intervals) */
reg = (data->beacon_interval << 16) | data->listen_interval;
ADM8211_CSR_WRITE(BPLI, cpu_to_le32(reg));
/*
reg = le32_to_cpu(ADM8211_CSR_READ(WCSR));
reg &= ~(0xFF<<21);
reg |= (0x04<<21);
ADM8211_CSR_WRITE(WCSR, cpu_to_le32(reg));
ADM8211_CSR_READ(WCSR);*/
}
static int adm8211_set_bssid(struct net_device *dev, u8 *bssid)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg;
reg = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24);
ADM8211_CSR_WRITE(BSSID0, cpu_to_le32(reg));
reg = le32_to_cpu(ADM8211_CSR_READ(ABDA1));
reg &= 0x0000ffff;
reg |= (bssid[4] << 16) | (bssid[5] << 24);
ADM8211_CSR_WRITE(ABDA1, cpu_to_le32(reg));
return 0;
}
static int adm8211_set_ssid(struct net_device *dev, u8 *ssid, size_t ssid_len)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
u8 buf[36];
if (ssid_len > 32)
return -EINVAL;
memset(buf, 0, sizeof(buf));
buf[0] = ssid_len;
memcpy(buf + 1, ssid, ssid_len);
adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33);
adm8211_set_beacon(dev);
return 0;
}
static void adm8211_scan(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
adm8211_set_interval(dev);
adm8211_set_bssid(dev, data->scan_param.bssid.sa_data);
}
static void adm8211_associate(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
adm8211_rf_set_channel(dev, data->channel);
adm8211_set_bssid(dev, data->bssid);
adm8211_set_rate(dev);
adm8211_set_interval(dev);
}
static void adm8211_setup_ibss(struct net_device *dev, int start)
{
#define BEACON_SKIP 2
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
u64 tbttpre;
u32 beacon = data->beacon_interval << 10;
if (start)
data->timestamp = adm8211_get_tsft(dev);
adm8211_set_rate(dev);
adm8211_set_interval(dev);
adm8211_set_bssid(dev, data->bssid);
if (start)
adm8211_set_ssid(dev, data->ssid, data->ssid_len);
else
adm8211_set_ssid(dev, data->bss.network->ssid, data->bss.network->ssid_len);
adm8211_rf_set_channel(dev, data->channel);
tbttpre = data->timestamp;
tbttpre = data->timestamp - do_div(tbttpre, beacon);
tbttpre += (beacon * BEACON_SKIP) << 10; /* skip a few beacons in case we're slow */
tbttpre -= 20 << 10; /* wake up early */
tbttpre >>= 10; /* convert everything to TUs */
adm8211_set_tbtt(data->dev, (u16)(tbttpre & 0xFFFF));
}
static void adm8211_link_change(struct net_device *dev, int link)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
u32 reg;
/* Set aid for beacon TIM element decoding */
reg = le32_to_cpu(ADM8211_CSR_READ(FRCTL));
reg &= ~ADM8211_FRCTL_AID;
if (link) {
reg |= ADM8211_FRCTL_AID_ON;
reg |= priv->ieee80211.aid;
}
ADM8211_CSR_WRITE(FRCTL, cpu_to_le32(reg));
adm8211_set_bssid(dev, ieee->bssid);
if (link && data->bss.network)
adm8211_set_ssid(dev, data->bss.network->ssid, data->bss.network->ssid_len);
else if (link)
adm8211_set_ssid(dev, data->ssid, data->ssid_len);
else
adm8211_set_ssid(dev, NULL, 0);
}
static int adm8211_init_rings(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct adm8211_desc *desc = NULL;
struct adm8211_ring_info *info;
unsigned int i;
for (i = 0; i < priv->rx_ring_size; i++) {
desc = &priv->rx_ring[i];
desc->status = 0;
desc->length = cpu_to_le32(RX_PKT_SIZE);
priv->rx_buffers[i].skb = NULL;
}
/* Mark the end of RX ring; hw returns to base address after this
* descriptor */
desc->length |= cpu_to_le32(RDES1_CONTROL_RER);
for (i = 0; i < priv->rx_ring_size; i++) {
desc = &priv->rx_ring[i];
info = &priv->rx_buffers[i];
info->skb = dev_alloc_skb(RX_PKT_SIZE);
if (info->skb == NULL)
break;
info->mapping = pci_map_single(priv->pdev, info->skb->tail,
RX_PKT_SIZE,
PCI_DMA_FROMDEVICE);
info->skb->dev = dev;
desc->buffer1 = cpu_to_le32(info->mapping);
desc->status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
}
/* Setup TX ring. TX buffers descriptors will be filled in as needed */
for (i = 0; i < priv->tx_ring_size; i++) {
desc = &priv->tx_ring[i];
info = &priv->tx_buffers[i];
info->skb = NULL;
info->mapping = 0;
desc->status = 0;
}
desc->length = cpu_to_le32(TDES1_CONTROL_TER);
priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0;
ADM8211_CSR_WRITE(RDB, cpu_to_le32(priv->rx_ring_dma));
ADM8211_CSR_WRITE(TDBD, cpu_to_le32(priv->tx_ring_dma));
return 0;
}
static void adm8211_free_rings(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int i;
for (i = 0; i < priv->rx_ring_size; i++) {
if (!priv->rx_buffers[i].skb)
continue;
pci_unmap_single(
priv->pdev,
priv->rx_buffers[i].mapping,
RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
dev_kfree_skb(priv->rx_buffers[i].skb);
}
for (i = 0; i < priv->tx_ring_size; i++) {
if (!priv->tx_buffers[i].skb)
continue;
pci_unmap_single(
priv->pdev,
priv->tx_buffers[i].mapping,
priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE);
dev_kfree_skb(priv->tx_buffers[i].skb);
}
}
static int adm8211_open(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
int retval;
/* Power up MAC and RF chips */
retval = adm8211_hw_reset(dev);
if (retval) {
printk(KERN_ERR "%s: hardware reset failed\n", dev->name);
goto fail;
}
retval = adm8211_init_rings(dev);
if (retval) {
printk(KERN_ERR "%s: failed to initialize rings\n", dev->name);
goto fail;
}
/* Init hardware */
adm8211_hw_init(dev);
adm8211_rf_set_channel(dev, max(priv->ieee80211.pref_channel,
priv->ieee80211.chan_range.min));
adm8211_set_rx_mode(dev);
retval = request_irq(dev->irq, &adm8211_interrupt,
IRQF_SHARED, dev->name, dev);
if (retval) {
printk(KERN_ERR "%s: failed to register IRQ handler\n",
dev->name);
goto fail;
}
ADM8211_CSR_WRITE(IER, __constant_cpu_to_le32(ADM8211_INTMASK));
adm8211_update_mode(dev);
ADM8211_CSR_WRITE(RDR, 0);
priv->ieee80211.flags |= AUTO_ASSOCIATE | SCAN_NOTIFY;
ieee80211_start_scan(&priv->ieee80211);
return 0;
fail:
return retval;
}
static int adm8211_stop(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
del_singleshot_timer_sync(&priv->timer);
ieee80211_stop(&priv->ieee80211);
priv->nar = 0;
ADM8211_CSR_WRITE(NAR, 0);
ADM8211_CSR_WRITE(IER, 0);
ADM8211_CSR_READ(NAR);
free_irq(dev->irq, dev);
adm8211_free_rings(dev);
skb_queue_purge(&priv->rx_queue);
adm8211_hw_reset(dev);
return 0;
}
static void adm8211_timer(unsigned long ptr)
{
struct net_device *dev = (struct net_device *) ptr;
struct adm8211_priv *priv = ieee80211_priv(dev);
u32 reg;
u16 rra, rwa;
if (priv->revid != ADM8211_REV_BA ||
!(dev->flags & IFF_UP))
return;
/* checks for stalls on adm8211b */
reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
rra = (reg >> 12) & 0x1FF;
rwa = (reg >> 2 ) & 0x1FF;
if ( (rra != rwa) && !(reg & (1<<1)) ) {
printk(KERN_DEBUG "%s: stalled. please manually reset the card.\n", dev->name);
//adm8211_stop(dev);
//adm8211_open(dev);
}
}
static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len,
int plcp_signal, int short_preamble)
{
/* Alternative calculation from NetBSD: */
/* IEEE 802.11b durations for DSSS PHY in microseconds */
#define IEEE80211_DUR_DS_LONG_PREAMBLE 144
#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72
#define IEEE80211_DUR_DS_FAST_PLCPHDR 24
#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48
#define IEEE80211_DUR_DS_SLOW_ACK 112
#define IEEE80211_DUR_DS_FAST_ACK 56
#define IEEE80211_DUR_DS_SLOW_CTS 112
#define IEEE80211_DUR_DS_FAST_CTS 56
#define IEEE80211_DUR_DS_SLOT 20
#define IEEE80211_DUR_DS_SIFS 10
int remainder;
*dur = (80 * (IEEE80211_3ADDR_LEN + payload_len) + plcp_signal - 1)
/ plcp_signal;
if (plcp_signal <= PLCP_SIGNAL_2M)
/* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */
*dur += 3 * (IEEE80211_DUR_DS_SIFS +
IEEE80211_DUR_DS_SHORT_PREAMBLE +
IEEE80211_DUR_DS_FAST_PLCPHDR) +
IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK;
else
/* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */
*dur += 3 * (IEEE80211_DUR_DS_SIFS +
IEEE80211_DUR_DS_SHORT_PREAMBLE +
IEEE80211_DUR_DS_FAST_PLCPHDR) +
IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK;
/* lengthen duration if long preamble */
if (!short_preamble)
*dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE -
IEEE80211_DUR_DS_SHORT_PREAMBLE) +
3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR -
IEEE80211_DUR_DS_FAST_PLCPHDR);
*plcp = (80 * len) / plcp_signal;
remainder = (80 * len) % plcp_signal;
if (plcp_signal == PLCP_SIGNAL_11M &&
remainder <= 30 && remainder > 0)
*plcp = (*plcp | 0x8000) + 1;
else if (remainder)
(*plcp)++;
}
/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */
static void adm8211_tx_raw(struct net_device *dev, struct sk_buff *skb)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned long flags;
dma_addr_t mapping;
unsigned entry;
u32 flag;
mapping = pci_map_single(priv->pdev, skb->data, skb->len,
PCI_DMA_TODEVICE);
spin_lock_irqsave(&priv->lock, flags);
if (priv->cur_tx - priv->dirty_tx < priv->tx_ring_size / 2)
flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
else if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2)
flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
else if (priv->cur_tx - priv->dirty_tx < priv->tx_ring_size - 4)
flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
else if (priv->cur_tx - priv->dirty_tx < priv->tx_ring_size - 2) {
flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
netif_stop_queue(dev);
} else {
if (net_ratelimit())
printk(KERN_WARNING "%s: Egads! TX ring almost overflowed. Dropping frame.\n", dev->name);
dev_kfree_skb(skb);
spin_unlock_irqrestore(&priv->lock, flags);
return;
}
entry = priv->cur_tx % priv->tx_ring_size;
priv->tx_buffers[entry].skb = skb;
priv->tx_buffers[entry].mapping = mapping;
priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
if (entry == priv->tx_ring_size - 1)
flag |= TDES1_CONTROL_TER;
priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len);
/* Set TX rate (SIGNAL field in PLCP PPDU format) */
flag = TDES0_CONTROL_OWN | (0x0A << 20) | 8 /* ? */;
priv->tx_ring[entry].status = cpu_to_le32(flag);
priv->cur_tx++;
spin_unlock_irqrestore(&priv->lock, flags);
/* Trigger transmit poll */
ADM8211_CSR_WRITE(TDR, 0);
dev->trans_start = jiffies;
}
/* TODO: add addr4 to this */
/* Put adm8211_tx_hdr on skb and transmit */
static int adm8211_tx(struct net_device *dev, struct sk_buff *skb)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_data *data = &priv->ieee80211;
struct adm8211_tx_hdr *txhdr;
struct ieee80211_hdr_4addr *hdr;
u16 fc_;
__le16 fc;
u8 dst[ETH_ALEN];
size_t payload_len;
int plcp, dur, len;
u16 ether_type;
int plcp_signal;
int short_preamble = 0;
struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
// TODO: Add support for preamble on adhoc
if (ieee->iw_mode == IW_MODE_INFRA) {
if (ieee->state == IEEE80211_ASSOCIATED && data->bss.network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
short_preamble = 1;
}
hdr = (struct ieee80211_hdr_4addr *)skb->data;
fc = hdr->frame_ctl;
fc_ = le16_to_cpu(fc);
switch (fc_ & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
case IEEE80211_FCTL_FROMDS:
memcpy(dst, hdr->addr1, ETH_ALEN);
break;
case IEEE80211_FCTL_TODS:
memcpy(dst, hdr->addr3, ETH_ALEN);
break;
// yeah, uh, this just won't work yet..
case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
memcpy(dst, hdr->addr3, ETH_ALEN);
break;
case 0:
memcpy(dst, hdr->addr1, ETH_ALEN);
break;
}
// add sanity check here?
skb_pull(skb, ieee80211_get_hdrlen(fc_));
ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
if (WLAN_FC_GET_TYPE(fc_) == IEEE80211_FTYPE_MGMT) {
if (fc_ & IEEE80211_FCTL_PROTECTED && crypt->ops->encrypt_mpdu(skb, 0, crypt->priv)) {
printk(KERN_DEBUG "%s: failed to WEP encrypt frame\n", dev->name);
dev_kfree_skb(skb);
return 0;
}
fc_ &= ~IEEE80211_FCTL_PROTECTED;
} else if (ieee->host_encrypt) {
fc_ &= ~IEEE80211_FCTL_PROTECTED;
} else if (crypt && !ieee->host_encrypt &&
!(ether_type == ETH_P_PAE && ieee->ieee802_1x)) {
fc |= __constant_cpu_to_le16(IEEE80211_FCTL_PROTECTED);
fc_ |= IEEE80211_FCTL_PROTECTED;
}
payload_len = skb->len;
if (skb_headroom(skb) < sizeof(struct adm8211_tx_hdr)) {
if (pskb_expand_head(skb, sizeof(struct adm8211_tx_hdr), 0, GFP_ATOMIC)) {
printk(KERN_DEBUG "%s: failed to allocate room for TX "
"header\n", dev->name);
dev_kfree_skb(skb);
return 0;
}
}
plcp_signal = ieee80211_get_rate(&priv->ieee80211, dst) * 5;
txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr));
memset(txhdr, 0, sizeof(*txhdr));
memcpy(txhdr->da, dst, ETH_ALEN);
txhdr->signal = plcp_signal;
txhdr->frame_body_size = cpu_to_le16(payload_len);
txhdr->frame_control = fc;
len = IEEE80211_3ADDR_LEN + payload_len + IEEE80211_FCS_LEN;
if (fc_ & IEEE80211_FCTL_PROTECTED)
len += 8;
if (len > ieee->fts && is_valid_ether_addr(dst)) {
txhdr->frag = cpu_to_le16(ieee->fts);
len = IEEE80211_3ADDR_LEN + ieee->fts + IEEE80211_FCS_LEN;
if (fc_ & IEEE80211_FCTL_PROTECTED)
len += 8;
adm8211_calc_durations(&dur, &plcp, ieee->fts,
len, plcp_signal, short_preamble);
txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
txhdr->dur_frag_head = cpu_to_le16(dur);
if (payload_len % ieee->fts) {
len = IEEE80211_3ADDR_LEN +
(payload_len % ieee->fts) + IEEE80211_FCS_LEN;
if (fc_ & IEEE80211_FCTL_PROTECTED)
len += 8;
adm8211_calc_durations(&dur, &plcp, payload_len % ieee->fts,
len, plcp_signal, short_preamble);
txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
txhdr->dur_frag_tail = cpu_to_le16(dur);
} else {
txhdr->plcp_frag_tail_len = txhdr->plcp_frag_head_len;
txhdr->dur_frag_tail = txhdr->dur_frag_head;
}
} else {
txhdr->frag = cpu_to_le16(0x0FFF);
adm8211_calc_durations(&dur, &plcp, payload_len,
len, plcp_signal, short_preamble);
txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
txhdr->dur_frag_head = cpu_to_le16(dur);
txhdr->dur_frag_tail = cpu_to_le16(dur);
}
txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER);
if (short_preamble)
txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
if ((payload_len + (fc_ & IEEE80211_FCTL_PROTECTED ? 8 : 0)) > ieee->rts
&& is_valid_ether_addr(dst))
txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
if (fc_ & IEEE80211_FCTL_PROTECTED)
txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE);
txhdr->retry_limit = priv->retry_limit;
adm8211_tx_raw(dev, skb);
return NETDEV_TX_OK;
}
static int adm8211_hard_start_xmit(struct ieee80211_txb *txb,
struct net_device *dev, int pri)
{
struct ieee80211_device *ieee = netdev_priv(dev);
unsigned int i;
int ret = NETDEV_TX_OK;
// FIXME: this is really dangerous.
// this makes overflowing the tx ring very easy
for (i = 0; i < txb->nr_frags; i++) {
if (unlikely(i)) {
printk(KERN_ERR "%s: Fragments are dangerous!\n", dev->name);
return 0;
}
spin_lock_bh(&ieee->lock);
ret = adm8211_tx(dev, txb->fragments[i]);
spin_unlock_bh(&ieee->lock);
if (ret)
break;
}
return ret;
}
int adm8211_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
memcpy(haddr, skb_mac_header(skb) + 10 + sizeof(struct avs_caphdr),
ETH_ALEN); /* addr2 */
return ETH_ALEN;
}
static int adm8211_alloc_rings(struct net_device *dev)
{
struct adm8211_priv *priv = ieee80211_priv(dev);
unsigned int ring_size;
priv->rx_buffers = kmalloc(sizeof(struct adm8211_ring_info) *
(priv->rx_ring_size + priv->tx_ring_size), GFP_KERNEL);
if (!priv->rx_buffers)
return -ENOMEM;
priv->tx_buffers = ((void *)priv->rx_buffers) + sizeof(struct adm8211_ring_info) * priv->tx_ring_size;
/* Allocate TX/RX descriptors */
ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size +
sizeof(struct adm8211_desc) * priv->tx_ring_size;
priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size,
&priv->rx_ring_dma);
if (!priv->rx_ring) {
kfree(priv->rx_buffers);
priv->rx_buffers = priv->tx_buffers = NULL;
return -ENOMEM;
}
priv->tx_ring = (struct adm8211_desc *) (priv->rx_ring + priv->rx_ring_size);
priv->tx_ring_dma = priv->rx_ring_dma +
sizeof(struct adm8211_desc) * priv->rx_ring_size;
return 0;
}
static int __devinit adm8211_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct net_device *dev;
struct ieee80211_device *ieee;
struct adm8211_priv *priv;
unsigned long mem_addr, mem_len;
unsigned int io_addr, io_len;
int err;
u32 reg;
#ifndef MODULE
static unsigned int cardidx;
if (!cardidx++) {
printk(version);
printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
}
#endif
err = pci_enable_device(pdev);
if (err) {
printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev));
return err;
}
io_addr = pci_resource_start(pdev, 0);
io_len = pci_resource_len(pdev, 0);
mem_addr = pci_resource_start(pdev, 1);
mem_len = pci_resource_len(pdev, 1);
if (io_len < 256 || mem_len < 1024) {
printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev));
goto err_disable_pdev;
}
/* check signature */
pci_read_config_dword(pdev, 0x80 /* CR32 */, &reg);
if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) {
printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg);
goto err_disable_pdev;
}
err = pci_request_regions(pdev, "adm8211");
if (err) {
printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev));
return err; /* someone else grabbed it? don't disable it */
}
if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) ||
pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev));
goto err_free_reg;
}
pci_set_master(pdev);
dev = alloc_ieee80211(sizeof(struct adm8211_priv));
if (!dev) {
printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", pci_name(pdev));
err = -ENOMEM;
goto err_free_reg;
}
ieee = netdev_priv(dev);
priv = ieee80211_priv(dev);
priv->pdev = pdev;
spin_lock_init(&priv->lock);
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
pci_set_drvdata(pdev, dev);
priv->msg_enable = netif_msg_init(debug, NETIF_MSG_DRV | NETIF_MSG_PROBE);
priv->map = pci_iomap(pdev, 1, mem_len);
if (!priv->map)
priv->map = pci_iomap(pdev, 0, io_len);
if (!priv->map) {
printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev));
goto err_free_dev;
}
dev->mem_start = mem_addr;
dev->mem_end = mem_addr + mem_len;
dev->base_addr = io_addr;
dev->irq = pdev->irq;
priv->rx_ring_size = rx_ring_size;
priv->tx_ring_size = tx_ring_size;
if (adm8211_alloc_rings(dev)) {
printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev));
goto err_iounmap;
}
skb_queue_head_init(&priv->rx_queue);
tasklet_init(&priv->rx_tasklet,
adm8211_rx_tasklet, (unsigned long) dev);
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &priv->revid);
put_unaligned(ADM8211_CSR_READ(PAR0), (u32 *) dev->dev_addr);
put_unaligned(ADM8211_CSR_READ(PAR1) & (__force u32) __constant_cpu_to_le32(0xffff),
(u16 *) &dev->dev_addr[4]);
if (!is_valid_ether_addr(dev->dev_addr)) {
printk(KERN_WARNING "%s (adm8211): Invalid hwaddr! Using randomly generated hwaddr\n", pci_name(pdev));
random_ether_addr(dev->dev_addr);
}
dev->features |= NETIF_F_LLTX;
dev->open = adm8211_open;
dev->stop = adm8211_stop;
dev->set_multicast_list = adm8211_set_rx_mode;
dev->set_mac_address = adm8211_set_mac_address;
dev->wireless_handlers =
(struct iw_handler_def *) &adm8211_iw_handler_def;
dev->wireless_data = &priv->pub_data;
dev->wireless_data->spy_data = &priv->spy;
init_timer(&priv->timer);
priv->timer.data = (unsigned long) dev;
priv->timer.function = adm8211_timer;
ieee->tx_headroom = sizeof(struct adm8211_tx_hdr) - IEEE80211_3ADDR_LEN;
ieee->iw_mode = IW_MODE_INFRA;
ieee->mode = IEEE_B;
ieee->modulation = IEEE80211_CCK_MODULATION;
ieee->freq_band = IEEE80211_24GHZ_BAND;
ieee->set_security = adm8211_set_security;
ieee->hard_start_xmit = adm8211_hard_start_xmit;
priv->wstats.qual.updated = IW_QUAL_LEVEL_INVALID | IW_QUAL_NOISE_INVALID;
priv->ieee80211.dev = dev;
priv->ieee80211.ieee = ieee;
priv->ieee80211.set_channel = adm8211_rf_set_channel;
priv->ieee80211.tx = adm8211_tx;
priv->ieee80211.scan = adm8211_scan;
priv->ieee80211.associate = adm8211_associate;
priv->ieee80211.setup_ibss = adm8211_setup_ibss;
priv->ieee80211.link_change = adm8211_link_change;
priv->ieee80211.supp_rates = (u8 *)IEEE80211_B_RATES;
priv->ieee80211.capab = WLAN_CAPABILITY_SHORT_PREAMBLE;
priv->ieee80211.flags = SOFT_SCAN | SOFT_ASSOCIATE;
ieee80211_init(&priv->ieee80211);
priv->plcp_signal = priv->ieee80211.rate * 5;
priv->retry_limit = 3;
priv->ant_power = 0x40;
priv->tx_power = 0x40;
priv->lpf_cutoff = 0xFF;
priv->lnags_threshold = 0xFF;
/* Power-on issue. EEPROM won't read correctly without */
if (priv->revid >= ADM8211_REV_BA) {
ADM8211_CSR_WRITE(FRCTL, 0);
ADM8211_CSR_READ(FRCTL);
ADM8211_CSR_WRITE(FRCTL, 1);
ADM8211_CSR_READ(FRCTL);
mdelay(100);
}
err = adm8211_read_eeprom(dev);
if (err) {
printk(KERN_ERR "%s (adm8211): Cannot allocate eeprom buffer\n", pci_name(pdev));
goto err_free_desc;
}
err = register_netdev(dev);
if (err) {
printk(KERN_ERR "%s (adm8211): Cannot register netdevice\n", pci_name(pdev));
goto err_free_desc;
}
printk("%s: hwaddr " MAC_FMT ", IRQ %d, Rev 0x%02x\n",
dev->name, MAC_ARG(dev->dev_addr), dev->irq, priv->revid);
return 0;
err_free_desc:
pci_free_consistent(pdev,
sizeof(struct adm8211_desc) * priv->rx_ring_size +
sizeof(struct adm8211_desc) * priv->tx_ring_size,
priv->rx_ring, priv->rx_ring_dma);
kfree(priv->rx_buffers);
err_iounmap:
pci_iounmap(pdev, priv->map);
err_free_dev:
pci_set_drvdata(pdev, NULL);
free_ieee80211(dev);
err_free_reg:
pci_release_regions(pdev);
err_disable_pdev:
pci_disable_device(pdev);
return err;
}
static void __devexit adm8211_remove(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct adm8211_priv *priv;
if (!dev)
return;
unregister_netdev(dev);
priv = ieee80211_priv(dev);
pci_free_consistent(pdev,
sizeof(struct adm8211_desc) * priv->rx_ring_size +
sizeof(struct adm8211_desc) * priv->tx_ring_size,
priv->rx_ring, priv->rx_ring_dma);
kfree(priv->rx_buffers);
kfree(priv->eeprom);
pci_iounmap(pdev, priv->map);
pci_release_regions(pdev);
pci_disable_device(pdev);
free_ieee80211(dev);
}
#ifdef CONFIG_PM
static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
netif_device_detach(dev);
if (dev->flags & IFF_UP)
dev->stop(dev);
pci_save_state(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int adm8211_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
if (dev->flags & IFF_UP)
dev->open(dev);
netif_device_attach(dev);
return 0;
}
#endif /* CONFIG_PM */
MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);
/* TODO: enable_wake */
static struct pci_driver adm8211_driver = {
.name = "adm8211",
.id_table = adm8211_pci_id_table,
.probe = adm8211_probe,
.remove = __devexit_p(adm8211_remove),
#ifdef CONFIG_PM
.suspend = adm8211_suspend,
.resume = adm8211_resume,
#endif /* CONFIG_PM */
};
static int __init adm8211_init(void)
{
#ifdef MODULE
printk(version);
printk(KERN_INFO "adm8211: release " RELEASE_DATE "\n");
#endif
return pci_register_driver(&adm8211_driver);
}
static void __exit adm8211_exit(void)
{
pci_unregister_driver(&adm8211_driver);
printk(KERN_INFO "adm8211: Driver unloaded\n");
}
module_init(adm8211_init);
module_exit(adm8211_exit);