📄 pci.c
字号:
/** * Airgo MIMO wireless driver * * Copyright (c) 2007-2008 Li YanBo <dreamfly281@gmail.com> * Modified by C. McPherson * * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin * 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/etherdevice.h>#include <linux/pci.h>#include <linux/delay.h>#include "agnx.h"#include "debug.h"#include "xmit.h"#include "phy.h"#include "net/mac80211.h"MODULE_AUTHOR("Li YanBo <dreamfly281@gmail.com>");MODULE_DESCRIPTION("Airgo MIMO PCI wireless driver");MODULE_LICENSE("GPL");static struct pci_device_id agnx_pci_id_tbl[] __devinitdata = { { PCI_DEVICE(0x17cb, 0x0001) }, /* Beklin F5d8010, Netgear WGM511 etc */ { PCI_DEVICE(0x17cb, 0x0002) }, /* Netgear Wpnt511 */ { 0 }};MODULE_DEVICE_TABLE(pci, agnx_pci_id_tbl);static inline void agnx_interrupt_ack(struct agnx_priv *priv, u32 *reason){ void __iomem *ctl = priv->ctl; u32 reg; if ( *reason & AGNX_STAT_RX ) { /* Mark complete RX */ reg = ioread32(ctl + AGNX_CIR_RXCTL); reg |= 0x4; iowrite32(reg, ctl + AGNX_CIR_RXCTL); /* disable Rx interrupt */ } if ( *reason & AGNX_STAT_TX ) { reg = ioread32(ctl + AGNX_CIR_TXDCTL); if (reg & 0x4) { iowrite32(reg, ctl + AGNX_CIR_TXDCTL); *reason |= AGNX_STAT_TXD; } reg = ioread32(ctl + AGNX_CIR_TXMCTL); if (reg & 0x4) { iowrite32(reg, ctl + AGNX_CIR_TXMCTL); *reason |= AGNX_STAT_TXM; } } if ( *reason & AGNX_STAT_X ) {/* reg = ioread32(ctl + AGNX_INT_STAT); *//* iowrite32(reg, ctl + AGNX_INT_STAT); *//* /\* FIXME reinit interrupt mask *\/ *//* reg = 0xc390bf9 & ~IRQ_TX_BEACON; *//* reg &= ~IRQ_TX_DISABLE; *//* iowrite32(reg, ctl + AGNX_INT_MASK); *//* iowrite32(0x800, ctl + AGNX_CIR_BLKCTL); */ }} /* agnx_interrupt_ack */static irqreturn_t agnx_interrupt_handler(int irq, void *dev_id){ struct ieee80211_hw *dev = dev_id; struct agnx_priv *priv = dev->priv; void __iomem *ctl = priv->ctl; irqreturn_t ret = IRQ_NONE; u32 irq_reason; spin_lock(&priv->lock);// printk(KERN_ERR PFX "Get a interrupt %s\n", __func__); if (priv->init_status != AGNX_START) goto out; /* FiXME Here has no lock, Is this will lead to race? */ irq_reason = ioread32(ctl + AGNX_CIR_BLKCTL); if (!(irq_reason & 0x7)) goto out; ret = IRQ_HANDLED; priv->irq_status = ioread32(ctl + AGNX_INT_STAT);// printk(PFX "Interrupt reason is 0x%x\n", irq_reason); /* Make sure the txm and txd flags don't conflict with other unknown interrupt flag, maybe is not necessary */ irq_reason &= 0xF; disable_rx_interrupt(priv); /* TODO Make sure the card finished initialized */ agnx_interrupt_ack(priv, &irq_reason); if ( irq_reason & AGNX_STAT_RX ) handle_rx_irq(priv); if ( irq_reason & AGNX_STAT_TXD ) handle_txd_irq(priv); if ( irq_reason & AGNX_STAT_TXM ) handle_txm_irq(priv); if ( irq_reason & AGNX_STAT_X ) handle_other_irq(priv); enable_rx_interrupt(priv);out: spin_unlock(&priv->lock); return ret;} /* agnx_interrupt_handler *//* FIXME */static int agnx_tx(struct ieee80211_hw *dev, struct sk_buff *skb){ AGNX_TRACE; return _agnx_tx(dev->priv, skb);} /* agnx_tx */static int agnx_get_mac_address(struct agnx_priv *priv){ void __iomem *ctl = priv->ctl; u32 reg; AGNX_TRACE; /* Attention! directly read the MAC or other date from EEPROM will lead to cardbus(WGM511) lock up when write to PM PLL register */ reg = agnx_read32(ctl, 0x3544); udelay(40); reg = agnx_read32(ctl, 0x354c); udelay(50); /* Get the mac address */ reg = agnx_read32(ctl, 0x3544); udelay(40); /* HACK */ reg = cpu_to_le32(reg); priv->mac_addr[0] = ((u8 *)®)[2]; priv->mac_addr[1] = ((u8 *)®)[3]; reg = agnx_read32(ctl, 0x3548); udelay(50); *((u32 *)(priv->mac_addr + 2)) = cpu_to_le32(reg); if (!is_valid_ether_addr(priv->mac_addr)) { DECLARE_MAC_BUF(mbuf); printk(KERN_WARNING PFX "read mac %s\n", print_mac(mbuf, priv->mac_addr)); printk(KERN_WARNING PFX "Invalid hwaddr! Using random hwaddr\n"); random_ether_addr(priv->mac_addr); } return 0;} /* agnx_get_mac_address */static int agnx_alloc_rings(struct agnx_priv *priv){ unsigned int len; AGNX_TRACE; /* Allocate RX/TXM/TXD rings info */ priv->rx.size = AGNX_RX_RING_SIZE; priv->txm.size = AGNX_TXM_RING_SIZE; priv->txd.size = AGNX_TXD_RING_SIZE; len = priv->rx.size + priv->txm.size + priv->txd.size;// priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_KERNEL); priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_ATOMIC); if (!priv->rx.info) return -ENOMEM; priv->txm.info = priv->rx.info + priv->rx.size; priv->txd.info = priv->txm.info + priv->txm.size; /* Allocate RX/TXM/TXD descriptors */ priv->rx.desc = pci_alloc_consistent(priv->pdev, sizeof(struct agnx_desc) * len, &priv->rx.dma); if (!priv->rx.desc) { kfree(priv->rx.info); return -ENOMEM; } priv->txm.desc = priv->rx.desc + priv->rx.size; priv->txm.dma = priv->rx.dma + sizeof(struct agnx_desc) * priv->rx.size; priv->txd.desc = priv->txm.desc + priv->txm.size; priv->txd.dma = priv->txm.dma + sizeof(struct agnx_desc) * priv->txm.size; return 0;} /* agnx_alloc_rings */static void rings_free(struct agnx_priv *priv){ unsigned int len = priv->rx.size + priv->txm.size + priv->txd.size; unsigned long flags; AGNX_TRACE; spin_lock_irqsave(&priv->lock, flags); kfree(priv->rx.info); pci_free_consistent(priv->pdev, sizeof(struct agnx_desc) * len, priv->rx.desc, priv->rx.dma); spin_unlock_irqrestore(&priv->lock, flags);}#ifdef CONFIG_AGNX_PERIODIC_WORKstatic void agnx_periodic_work_handler(struct work_struct *work){ struct agnx_priv *priv = container_of(work, struct agnx_priv, periodic_work.work);// unsigned long flags; unsigned long delay; /* fixme: using mutex?? */// spin_lock_irqsave(&priv->lock, flags); /* TODO Recalibrate*/// calibrate_oscillator(priv);// antenna_calibrate(priv);// agnx_send_packet(priv, 997); /* FIXME *//* if (debug == 3) *//* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); *//* else */ delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY);// delay = round_jiffies(HZ * 15); queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay);// spin_unlock_irqrestore(&priv->lock, flags);}#endifstatic int agnx_start(struct ieee80211_hw *dev){ struct agnx_priv *priv = dev->priv; int err = 0; AGNX_TRACE; err = agnx_alloc_rings(priv); if (err) { printk(KERN_ERR PFX "Can't alloc RX/TXM/TXD rings\n"); goto out; } err = request_irq(priv->pdev->irq, &agnx_interrupt_handler, IRQF_SHARED, "agnx_pci", dev); if (err) { printk(KERN_ERR PFX "Failed to register IRQ handler\n"); rings_free(priv); goto out; }// mdelay(500); might_sleep(); agnx_hw_init(priv);// mdelay(500); might_sleep(); priv->init_status = AGNX_START;/* INIT_DELAYED_WORK(&priv->periodic_work, agnx_periodic_work_handler); *//* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); *//* queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); */out: return err;} /* agnx_start */static void agnx_stop(struct ieee80211_hw *dev){ struct agnx_priv *priv = dev->priv; AGNX_TRACE; priv->init_status = AGNX_STOP; /* make sure hardware will not generate irq */ agnx_hw_reset(priv); free_irq(priv->pdev->irq, dev); flush_workqueue(priv->hw->workqueue); /* cancel_delayed_work_sync(&priv->periodic_work);*/ unfill_rings(priv); rings_free(priv);}static int agnx_config(struct ieee80211_hw *dev, u32 changed){ struct agnx_priv *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); AGNX_TRACE; spin_lock(&priv->lock); /* FIXME need priv lock? */ if (channel != priv->channel) { priv->channel = channel; agnx_set_channel(priv, priv->channel); } spin_unlock(&priv->lock); return 0;}static int agnx_config_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_if_conf *conf){ struct agnx_priv *priv = dev->priv; void __iomem *ctl = priv->ctl; AGNX_TRACE; spin_lock(&priv->lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -