p54pci.c

来自「linux 内核源代码」· C语言 代码 · 共 693 行 · 第 1/2 页

C
693
字号
/* * Linux device driver for PCI based Prism54 * * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note <jean-baptiste.note@m4x.org>, et al. * * 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. */#include <linux/init.h>#include <linux/pci.h>#include <linux/firmware.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/completion.h>#include <net/mac80211.h>#include "p54.h"#include "p54pci.h"MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");MODULE_DESCRIPTION("Prism54 PCI wireless driver");MODULE_LICENSE("GPL");MODULE_ALIAS("prism54pci");static struct pci_device_id p54p_table[] __devinitdata = {	/* Intersil PRISM Duette/Prism GT Wireless LAN adapter */	{ PCI_DEVICE(0x1260, 0x3890) },	/* 3COM 3CRWE154G72 Wireless LAN adapter */	{ PCI_DEVICE(0x10b7, 0x6001) },	/* Intersil PRISM Indigo Wireless LAN adapter */	{ PCI_DEVICE(0x1260, 0x3877) },	/* Intersil PRISM Javelin/Xbow Wireless LAN adapter */	{ PCI_DEVICE(0x1260, 0x3886) },	{ },};MODULE_DEVICE_TABLE(pci, p54p_table);static int p54p_upload_firmware(struct ieee80211_hw *dev){	struct p54p_priv *priv = dev->priv;	const struct firmware *fw_entry = NULL;	__le32 reg;	int err;	u32 *data;	u32 remains, left, device_addr;	P54P_WRITE(int_enable, 0);	P54P_READ(int_enable);	udelay(10);	reg = P54P_READ(ctrl_stat);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);	P54P_WRITE(ctrl_stat, reg);	P54P_READ(ctrl_stat);	udelay(10);	reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);	P54P_WRITE(ctrl_stat, reg);	wmb();	udelay(10);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);	P54P_WRITE(ctrl_stat, reg);	wmb();	mdelay(50);	err = request_firmware(&fw_entry, "isl3886", &priv->pdev->dev);	if (err) {		printk(KERN_ERR "%s (prism54pci): cannot find firmware "		       "(isl3886)\n", pci_name(priv->pdev));		return err;	}	p54_parse_firmware(dev, fw_entry);	data = (u32 *) fw_entry->data;	remains = fw_entry->size;	device_addr = ISL38XX_DEV_FIRMWARE_ADDR;	while (remains) {		u32 i = 0;		left = min((u32)0x1000, remains);		P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr));		P54P_READ(int_enable);		device_addr += 0x1000;		while (i < left) {			P54P_WRITE(direct_mem_win[i], *data++);			i += sizeof(u32);		}		remains -= left;		P54P_READ(int_enable);	}	release_firmware(fw_entry);	reg = P54P_READ(ctrl_stat);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);	reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);	P54P_WRITE(ctrl_stat, reg);	P54P_READ(ctrl_stat);	udelay(10);	reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);	P54P_WRITE(ctrl_stat, reg);	wmb();	udelay(10);	reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);	P54P_WRITE(ctrl_stat, reg);	wmb();	udelay(10);	return 0;}static irqreturn_t p54p_simple_interrupt(int irq, void *dev_id){	struct p54p_priv *priv = (struct p54p_priv *) dev_id;	__le32 reg;	reg = P54P_READ(int_ident);	P54P_WRITE(int_ack, reg);	if (reg & P54P_READ(int_enable))		complete(&priv->boot_comp);	return IRQ_HANDLED;}static int p54p_read_eeprom(struct ieee80211_hw *dev){	struct p54p_priv *priv = dev->priv;	int err;	struct p54_control_hdr *hdr;	void *eeprom;	dma_addr_t rx_mapping, tx_mapping;	u16 alen;	init_completion(&priv->boot_comp);	err = request_irq(priv->pdev->irq, &p54p_simple_interrupt,			  IRQF_SHARED, "prism54pci", priv);	if (err) {		printk(KERN_ERR "%s (prism54pci): failed to register IRQ handler\n",		       pci_name(priv->pdev));		return err;	}	eeprom = kmalloc(0x2010 + EEPROM_READBACK_LEN, GFP_KERNEL);	if (!eeprom) {		printk(KERN_ERR "%s (prism54pci): no memory for eeprom!\n",		       pci_name(priv->pdev));		err = -ENOMEM;		goto out;	}	memset(priv->ring_control, 0, sizeof(*priv->ring_control));	P54P_WRITE(ring_control_base, priv->ring_control_dma);	P54P_READ(ring_control_base);	udelay(10);	P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT));	P54P_READ(int_enable);	udelay(10);	P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));	if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) {		printk(KERN_ERR "%s (prism54pci): Cannot boot firmware!\n",		       pci_name(priv->pdev));		err = -EINVAL;		goto out;	}	P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE));	P54P_READ(int_enable);	hdr = eeprom + 0x2010;	p54_fill_eeprom_readback(hdr);	hdr->req_id = cpu_to_le32(priv->common.rx_start);	rx_mapping = pci_map_single(priv->pdev, eeprom,				    0x2010, PCI_DMA_FROMDEVICE);	tx_mapping = pci_map_single(priv->pdev, (void *)hdr,				    EEPROM_READBACK_LEN, PCI_DMA_TODEVICE);	priv->ring_control->rx_mgmt[0].host_addr = cpu_to_le32(rx_mapping);	priv->ring_control->rx_mgmt[0].len = cpu_to_le16(0x2010);	priv->ring_control->tx_data[0].host_addr = cpu_to_le32(tx_mapping);	priv->ring_control->tx_data[0].device_addr = hdr->req_id;	priv->ring_control->tx_data[0].len = cpu_to_le16(EEPROM_READBACK_LEN);	priv->ring_control->host_idx[2] = cpu_to_le32(1);	priv->ring_control->host_idx[1] = cpu_to_le32(1);	wmb();	mdelay(100);	P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));	wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ);	wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ);	pci_unmap_single(priv->pdev, tx_mapping,			 EEPROM_READBACK_LEN, PCI_DMA_TODEVICE);	pci_unmap_single(priv->pdev, rx_mapping,			 0x2010, PCI_DMA_FROMDEVICE);	alen = le16_to_cpu(priv->ring_control->rx_mgmt[0].len);	if (le32_to_cpu(priv->ring_control->device_idx[2]) != 1 ||	    alen < 0x10) {		printk(KERN_ERR "%s (prism54pci): Cannot read eeprom!\n",		       pci_name(priv->pdev));		err = -EINVAL;		goto out;	}	p54_parse_eeprom(dev, (u8 *)eeprom + 0x10, alen - 0x10); out:	kfree(eeprom);	P54P_WRITE(int_enable, 0);	P54P_READ(int_enable);	udelay(10);	free_irq(priv->pdev->irq, priv);	P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));	return err;}static void p54p_refill_rx_ring(struct ieee80211_hw *dev){	struct p54p_priv *priv = dev->priv;	u32 limit, host_idx, idx;	host_idx = le32_to_cpu(priv->ring_control->host_idx[0]);	limit = host_idx;	limit -= le32_to_cpu(priv->ring_control->device_idx[0]);	limit = ARRAY_SIZE(priv->ring_control->rx_data) - limit;	idx = host_idx % ARRAY_SIZE(priv->ring_control->rx_data);	while (limit-- > 1) {		struct p54p_desc *desc = &priv->ring_control->rx_data[idx];		if (!desc->host_addr) {			struct sk_buff *skb;			dma_addr_t mapping;			skb = dev_alloc_skb(MAX_RX_SIZE);			if (!skb)				break;			mapping = pci_map_single(priv->pdev,						 skb_tail_pointer(skb),						 MAX_RX_SIZE,						 PCI_DMA_FROMDEVICE);			desc->host_addr = cpu_to_le32(mapping);			desc->device_addr = 0;	// FIXME: necessary?			desc->len = cpu_to_le16(MAX_RX_SIZE);			desc->flags = 0;			priv->rx_buf[idx] = skb;		}		idx++;		host_idx++;		idx %= ARRAY_SIZE(priv->ring_control->rx_data);	}	wmb();	priv->ring_control->host_idx[0] = cpu_to_le32(host_idx);}static irqreturn_t p54p_interrupt(int irq, void *dev_id){	struct ieee80211_hw *dev = dev_id;	struct p54p_priv *priv = dev->priv;	__le32 reg;	spin_lock(&priv->lock);	reg = P54P_READ(int_ident);	if (unlikely(reg == 0xFFFFFFFF)) {		spin_unlock(&priv->lock);		return IRQ_HANDLED;	}	P54P_WRITE(int_ack, reg);	reg &= P54P_READ(int_enable);	if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) {		struct p54p_desc *desc;		u32 idx, i;		i = priv->tx_idx;		i %= ARRAY_SIZE(priv->ring_control->tx_data);		priv->tx_idx = idx = le32_to_cpu(priv->ring_control->device_idx[1]);		idx %= ARRAY_SIZE(priv->ring_control->tx_data);		while (i != idx) {			desc = &priv->ring_control->tx_data[i];			if (priv->tx_buf[i]) {				kfree(priv->tx_buf[i]);				priv->tx_buf[i] = NULL;			}			pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr),					 le16_to_cpu(desc->len), PCI_DMA_TODEVICE);			desc->host_addr = 0;			desc->device_addr = 0;			desc->len = 0;			desc->flags = 0;			i++;			i %= ARRAY_SIZE(priv->ring_control->tx_data);		}		i = priv->rx_idx;		i %= ARRAY_SIZE(priv->ring_control->rx_data);		priv->rx_idx = idx = le32_to_cpu(priv->ring_control->device_idx[0]);		idx %= ARRAY_SIZE(priv->ring_control->rx_data);		while (i != idx) {			u16 len;			struct sk_buff *skb;			desc = &priv->ring_control->rx_data[i];			len = le16_to_cpu(desc->len);			skb = priv->rx_buf[i];			skb_put(skb, len);			if (p54_rx(dev, skb)) {				pci_unmap_single(priv->pdev,						 le32_to_cpu(desc->host_addr),						 MAX_RX_SIZE, PCI_DMA_FROMDEVICE);				priv->rx_buf[i] = NULL;				desc->host_addr = 0;			} else {				skb_trim(skb, 0);				desc->len = cpu_to_le16(MAX_RX_SIZE);			}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?