📄 adm8211.c
字号:
/* * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) * * Copyright (c) 2003, Jouni Malinen <j@w1.fi> * Copyright (c) 2004-2007, 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/init.h>#include <linux/if.h>#include <linux/skbuff.h>#include <linux/etherdevice.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/crc32.h>#include <linux/eeprom_93cx6.h>#include <net/mac80211.h>#include "adm8211.h"MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");MODULE_AUTHOR("Jouni Malinen <j@w1.fi>");MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211");MODULE_SUPPORTED_DEVICE("ADM8211");MODULE_LICENSE("GPL");static unsigned int tx_ring_size __read_mostly = 16;static unsigned int rx_ring_size __read_mostly = 16;module_param(tx_ring_size, uint, 0);module_param(rx_ring_size, uint, 0);static struct pci_device_id adm8211_pci_id_table[] __devinitdata = { /* ADMtek ADM8211 */ { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ { 0 }};static void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom){ struct adm8211_priv *priv = eeprom->data; u32 reg = ADM8211_CSR_READ(SPR); eeprom->reg_data_in = reg & ADM8211_SPR_SDI; eeprom->reg_data_out = reg & ADM8211_SPR_SDO; eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK; eeprom->reg_chip_select = reg & ADM8211_SPR_SCS;}static void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom){ struct adm8211_priv *priv = eeprom->data; u32 reg = 0x4000 | ADM8211_SPR_SRS; if (eeprom->reg_data_in) reg |= ADM8211_SPR_SDI; if (eeprom->reg_data_out) reg |= ADM8211_SPR_SDO; if (eeprom->reg_data_clock) reg |= ADM8211_SPR_SCLK; if (eeprom->reg_chip_select) reg |= ADM8211_SPR_SCS; ADM8211_CSR_WRITE(SPR, reg); ADM8211_CSR_READ(SPR); /* eeprom_delay */}static int adm8211_read_eeprom(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; unsigned int words, i; struct ieee80211_chan_range chan_range; u16 cr49; struct eeprom_93cx6 eeprom = { .data = priv, .register_read = adm8211_eeprom_register_read, .register_write = adm8211_eeprom_register_write }; if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) { /* 256 * 16-bit = 512 bytes */ eeprom.width = PCI_EEPROM_WIDTH_93C66; words = 256; } else { /* 64 * 16-bit = 128 bytes */ eeprom.width = PCI_EEPROM_WIDTH_93C46; words = 64; } priv->eeprom_len = words * 2; priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); if (!priv->eeprom) return -ENOMEM; eeprom_93cx6_multiread(&eeprom, 0, (__le16 __force *)priv->eeprom, words); 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->pdev->revision < ADM8211_REV_CA) priv->rf_type = ADM8211_TYPE_RFMD; else priv->rf_type = ADM8211_TYPE_AIROHA; printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n", pci_name(priv->pdev), (cr49 >> 3) & 0x7); } 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->pdev->revision < ADM8211_REV_CA) priv->bbp_type = ADM8211_TYPE_RFMD; else priv->bbp_type = ADM8211_TYPE_ADMTEK; printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n", pci_name(priv->pdev), cr49 >> 3); } if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n", pci_name(priv->pdev), priv->eeprom->country_code); chan_range = cranges[2]; } else chan_range = cranges[priv->eeprom->country_code]; printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); priv->modes[0].num_channels = chan_range.max - chan_range.min + 1; priv->modes[0].channels = priv->channels; memcpy(priv->channels, adm8211_channels, sizeof(adm8211_channels)); for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++) if (i >= chan_range.min && i <= chan_range.max) priv->channels[i - 1].flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_IBSS; 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->pdev->revision < ADM8211_REV_CA) priv->specific_bbptype = ADM8211_BBP_RFMD3000; else priv->specific_bbptype = ADM8211_BBP_ADM8011; printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n", pci_name(priv->pdev), priv->eeprom->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->pdev->revision == ADM8211_REV_BA) priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; else if (priv->pdev->revision == ADM8211_REV_CA) priv->transceiver_type = ADM8211_AL2210L; else if (priv->pdev->revision == ADM8211_REV_AB) priv->transceiver_type = ADM8211_RFMD2948; printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n", pci_name(priv->pdev), priv->eeprom->specific_rftype); 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 ieee80211_hw *dev, u32 addr, u32 data){ struct adm8211_priv *priv = dev->priv; ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR | (priv->pdev->revision < ADM8211_REV_BA ? 0 : ADM8211_WEPCTL_SEL_WEPTABLE )); ADM8211_CSR_READ(WEPCTL); msleep(1); ADM8211_CSR_WRITE(WESK, data); ADM8211_CSR_READ(WESK); msleep(1);}static void adm8211_write_sram_bytes(struct ieee80211_hw *dev, unsigned int addr, u8 *buf, unsigned int len){ struct adm8211_priv *priv = dev->priv; u32 reg = ADM8211_CSR_READ(WEPCTL); unsigned int i; if (priv->pdev->revision < 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, 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, val); } } ADM8211_CSR_WRITE(WEPCTL, reg);}static void adm8211_clear_sram(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; u32 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 int adm8211_get_stats(struct ieee80211_hw *dev, struct ieee80211_low_level_stats *stats){ struct adm8211_priv *priv = dev->priv; memcpy(stats, &priv->stats, sizeof(*stats)); return 0;}static int adm8211_get_tx_stats(struct ieee80211_hw *dev, struct ieee80211_tx_queue_stats *stats){ struct adm8211_priv *priv = dev->priv; struct ieee80211_tx_queue_stats_data *data = &stats->data[0]; data->len = priv->cur_tx - priv->dirty_tx; data->limit = priv->tx_ring_size - 2; data->count = priv->dirty_tx; return 0;}static void adm8211_interrupt_tci(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; unsigned int dirty_tx; spin_lock(&priv->lock); for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) { unsigned int entry = dirty_tx % priv->tx_ring_size; u32 status = le32_to_cpu(priv->tx_ring[entry].status); struct ieee80211_tx_status tx_status; struct adm8211_tx_ring_info *info; struct sk_buff *skb; if (status & TDES0_CONTROL_OWN || !(status & TDES0_CONTROL_DONE)) break; info = &priv->tx_buffers[entry]; skb = info->skb; /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ pci_unmap_single(priv->pdev, info->mapping, info->skb->len, PCI_DMA_TODEVICE); memset(&tx_status, 0, sizeof(tx_status)); skb_pull(skb, sizeof(struct adm8211_tx_hdr)); memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen); memcpy(&tx_status.control, &info->tx_control, sizeof(tx_status.control)); if (!(tx_status.control.flags & IEEE80211_TXCTL_NO_ACK)) { if (status & TDES0_STATUS_ES) tx_status.excessive_retries = 1; else tx_status.flags |= IEEE80211_TX_STATUS_ACK; } ieee80211_tx_status_irqsafe(dev, skb, &tx_status); info->skb = NULL; } if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) ieee80211_wake_queue(dev, 0); priv->dirty_tx = dirty_tx; spin_unlock(&priv->lock);}static void adm8211_interrupt_rci(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; unsigned int entry = priv->cur_rx % priv->rx_ring_size; u32 status; unsigned int pktlen; struct sk_buff *skb, *newskb; unsigned int limit = priv->rx_ring_size; static const u8 rate_tbl[] = {10, 20, 55, 110, 220}; u8 rssi, rate; while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) { if (!limit--) 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; pktlen = status & RDES0_STATUS_FL; if (pktlen > RX_PKT_SIZE) { if (net_ratelimit()) printk(KERN_DEBUG "%s: frame too long (%d)\n", wiphy_name(dev->wiphy), pktlen); pktlen = RX_PKT_SIZE; } if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { skb = NULL; /* old buffer will be reused */ /* TODO: update RX error stats */ /* TODO: check RDES0_STATUS_CRC*E */ } else if (pktlen < RX_COPY_BREAK) { skb = dev_alloc_skb(pktlen); if (skb) { pci_dma_sync_single_for_cpu( priv->pdev, priv->rx_buffers[entry].mapping, pktlen, PCI_DMA_FROMDEVICE); memcpy(skb_put(skb, pktlen), skb_tail_pointer(priv->rx_buffers[entry].skb), 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) { 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, skb_tail_pointer(newskb), RX_PKT_SIZE, PCI_DMA_FROMDEVICE); } else { skb = NULL; /* TODO: update rx dropped stats */ } 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 ieee80211_rx_status rx_status = {0}; if (priv->pdev->revision < ADM8211_REV_CA) rx_status.ssi = rssi; else rx_status.ssi = 100 - rssi; if (rate <= 4) rx_status.rate = rate_tbl[rate]; rx_status.channel = priv->channel; rx_status.freq = adm8211_channels[priv->channel - 1].freq; rx_status.phymode = MODE_IEEE80211B; ieee80211_rx_irqsafe(dev, skb, &rx_status); } entry = (++priv->cur_rx) % priv->rx_ring_size; } /* TODO: check LPC and update stats? */}static irqreturn_t adm8211_interrupt(int irq, void *dev_id){#define ADM8211_INT(x) \do { \ if (unlikely(stsr & ADM8211_STSR_ ## x)) \ printk(KERN_DEBUG "%s: " #x "\n", wiphy_name(dev->wiphy)); \} while (0) struct ieee80211_hw *dev = dev_id; struct adm8211_priv *priv = dev->priv; u32 stsr = ADM8211_CSR_READ(STSR); ADM8211_CSR_WRITE(STSR, stsr); if (stsr == 0xffffffff) return IRQ_HANDLED; if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) return IRQ_HANDLED; if (stsr & ADM8211_STSR_RCI) adm8211_interrupt_rci(dev); if (stsr & ADM8211_STSR_TCI) adm8211_interrupt_tci(dev); /*ADM8211_INT(LinkOn);*/ /*ADM8211_INT(LinkOff);*/ 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); return IRQ_HANDLED;#undef ADM8211_INT}#define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\static void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \ u16 addr, u32 value) { \ struct adm8211_priv *priv = dev->priv; \ unsigned int i; \ u32 reg, bitbuf; \ \ value &= v_mask; \ addr &= a_mask; \ bitbuf = (value << v_shift) | (addr << a_shift); \ \ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \ ADM8211_CSR_READ(SYNRF); \ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \ ADM8211_CSR_READ(SYNRF); \ \ if (prewrite) { \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -