⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 adm8211.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -