* Sort functions in segments like we do on the rest of the code (eg.
hw.c). This improves readability and maintainability.
* Add myself as module author (a patch for this was submited some time
ago as part of SFLC's changes but wasn't applied)
* Declare that this driver is or 5xxx chipset series only in
MODULE_SUPPORTED_DEVICE and MODULE_DESCRIPTION.
Changes-licensed-under: 3-clause-BSD
Signed-Off-by: Nick Kossifidis <[email protected]>
---
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index c38adab..2098944 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -39,7 +39,6 @@
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
-#define ATH_PCI_VERSION "0.9.5.0-BSD"
#include <linux/version.h>
#include <linux/module.h>
@@ -103,34 +102,6 @@ static unsigned int ath5k_debug;
module_param_named(debug, ath5k_debug, uint, 0);
#endif
-/*
- * Static table of PCI id's.
- */
-static struct pci_device_id ath5k_pci_id_table[] __devinitdata = {
- { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */
- { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */
- { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 */
- { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */
- { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */
- { PCI_VDEVICE(3COM_2, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */
- { PCI_VDEVICE(3COM, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */
- { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */
- { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 },
- { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */
- { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */
- { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
-
-static void ath5k_led_event(struct ath5k_softc *, int);
-static int ath5k_reset(struct ieee80211_hw *);
-
#if AR_DEBUG
static void ath5k_printrxbuf(struct ath5k_buf *bf, int done)
{
@@ -165,93 +136,1158 @@ static inline void ath5k_dump_skb(struct sk_buff *skb, const char *prefix)
static inline void ath5k_dump_skb(struct sk_buff *skb, const char *prefix) {}
#endif
-static inline void ath5k_cleanup_txbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
+#if ATH_DEBUG_MODES
+static void ath5k_dump_modes(struct ieee80211_hw_mode *modes)
{
- BUG_ON(!bf);
- if (!bf->skb)
- return;
- pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len,
- PCI_DMA_TODEVICE);
- dev_kfree_skb(bf->skb);
- bf->skb = NULL;
+ unsigned int m, i;
+
+ for (m = 0; m < NUM_DRIVER_MODES; m++) {
+ printk(KERN_DEBUG "Mode %u: channels %d, rates %d\n", m,
+ modes[m].num_channels, modes[m].num_rates);
+ printk(KERN_DEBUG " channels:\n");
+ for (i = 0; i < modes[m].num_channels; i++)
+ printk(KERN_DEBUG " %3d %d %.4x %.4x\n",
+ modes[m].channels[i].chan,
+ modes[m].channels[i].freq,
+ modes[m].channels[i].val,
+ modes[m].channels[i].flag);
+ printk(KERN_DEBUG " rates:\n");
+ for (i = 0; i < modes[m].num_rates; i++)
+ printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
+ modes[m].rates[i].rate,
+ modes[m].rates[i].val,
+ modes[m].rates[i].flags,
+ modes[m].rates[i].val2);
+ }
}
+#else
+static inline void ath5k_dump_modes(struct ieee80211_hw_mode *modes) {}
+#endif
+
+
+/******************\
+* Internal defines *
+\******************/
+
+/* Module info */
+MODULE_AUTHOR("Jiri Slaby");
+MODULE_AUTHOR("Nick Kossifidis");
+MODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards.");
+MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION("0.1.1 (EXPERIMENTAL)");
+
+
+/* Known PCI ids */
+static struct pci_device_id ath5k_pci_id_table[] __devinitdata = {
+ { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */
+ { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */
+ { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 */
+ { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */
+ { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */
+ { PCI_VDEVICE(3COM_2, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */
+ { PCI_VDEVICE(3COM, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */
+ { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */
+ { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
+ { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */
+ { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */
+ { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
+
-static void ath5k_tasklet_reset(unsigned long data)
+/*
+ * Sysctl tables
+ */
+static int mincalibrate = 1;
+static int maxcalibrate = INT_MAX / 1000;
+static ctl_table ath5k_static_sysctls[] = {
+#if AR_DEBUG
+ {
+ .procname = "debug",
+ .mode = 0644,
+ .data = &ath5k_debug,
+ .maxlen = sizeof(ath5k_debug),
+ .proc_handler = proc_dointvec
+ },
+#endif
+ {
+ .procname = "calibrate",
+ .mode = 0644,
+ .data = &ath5k_calinterval,
+ .maxlen = sizeof(ath5k_calinterval),
+ .extra1 = &mincalibrate,
+ .extra2 = &maxcalibrate,
+ .proc_handler = proc_dointvec_minmax
+ },
+ { 0 }
+};
+static ctl_table ath5k_ath5k_table[] = {
+ {
+ .procname = "ath",
+ .mode = 0555,
+ .child = ath5k_static_sysctls
+ }, { 0 }
+};
+static ctl_table ath5k_root_table[] = {
+ {
+ .ctl_name = CTL_DEV,
+ .procname = "dev",
+ .mode = 0555,
+ .child = ath5k_ath5k_table
+ }, { 0 }
+};
+static struct ctl_table_header *ath5k_sysctl_header;
+
+
+/*
+ * Prototypes - PCI stack related functions
+ */
+static int __devinit ath5k_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id);
+static void __devexit ath5k_pci_remove(struct pci_dev *pdev);
+#ifdef CONFIG_PM
+static int ath5k_pci_suspend(struct pci_dev *pdev,
+ pm_message_t state);
+static int ath5k_pci_resume(struct pci_dev *pdev);
+#else
+#define ath5k_pci_suspend NULL
+#define ath5k_pci_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_driver ath5k_pci_drv_id = {
+ .name = "ath5k_pci",
+ .id_table = ath5k_pci_id_table,
+ .probe = ath5k_pci_probe,
+ .remove = __devexit_p(ath5k_pci_remove),
+ .suspend = ath5k_pci_suspend,
+ .resume = ath5k_pci_resume,
+};
+
+
+
+/*
+ * Prototypes - MAC 802.11 stack related functions
+ */
+static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_tx_control *ctl);
+static int ath5k_reset(struct ieee80211_hw *hw);
+static int ath5k_start(struct ieee80211_hw *hw);
+static void ath5k_stop(struct ieee80211_hw *hw);
+static int ath5k_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf);
+static void ath5k_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf);
+static int ath5k_config(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf);
+static int ath5k_config_interface(struct ieee80211_hw *hw, int if_id,
+ struct ieee80211_if_conf *conf);
+static void ath5k_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ int mc_count, struct dev_mc_list *mclist);
+static int ath5k_set_key(struct ieee80211_hw *hw,
+ enum set_key_cmd cmd,
+ const u8 *local_addr, const u8 *addr,
+ struct ieee80211_key_conf *key);
+static int ath5k_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats);
+static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *stats);
+static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
+static void ath5k_reset_tsf(struct ieee80211_hw *hw);
+static int ath5k_beacon_update(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ieee80211_tx_control *ctl);
+
+static struct ieee80211_ops ath5k_hw_ops = {
+ .tx = ath5k_tx,
+ .start = ath5k_start,
+ .stop = ath5k_stop,
+ .add_interface = ath5k_add_interface,
+ .remove_interface = ath5k_remove_interface,
+ .config = ath5k_config,
+ .config_interface = ath5k_config_interface,
+ .configure_filter = ath5k_configure_filter,
+ .set_key = ath5k_set_key,
+ .get_stats = ath5k_get_stats,
+ .conf_tx = NULL,
+ .get_tx_stats = ath5k_get_tx_stats,
+ .get_tsf = ath5k_get_tsf,
+ .reset_tsf = ath5k_reset_tsf,
+ .beacon_update = ath5k_beacon_update,
+};
+
+/*
+ * Prototypes - Internal functions
+ */
+/* Attach detach */
+static int ath5k_attach(struct pci_dev *pdev,
+ struct ieee80211_hw *hw);
+static void ath5k_detach(struct pci_dev *pdev,
+ struct ieee80211_hw *hw);
+/* Channel/mode setup */
+static inline short ath5k_ieee2mhz(short chan);
+static unsigned int ath5k_copy_rates(struct ieee80211_rate *rates,
+ const struct ath5k_rate_table *rt,
+ unsigned int max);
+static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
+ struct ieee80211_channel *channels,
+ unsigned int mode,
+ unsigned int max);
+static int ath5k_getchannels(struct ieee80211_hw *hw);
+static int ath5k_chan_set(struct ath5k_softc *sc,
+ struct ieee80211_channel *chan);
+static void ath5k_setcurmode(struct ath5k_softc *sc,
+ unsigned int mode);
+static void ath5k_mode_init(struct ath5k_softc *sc);
+/* Descriptor setup */
+static int ath5k_desc_alloc(struct ath5k_softc *sc,
+ struct pci_dev *pdev);
+static void ath5k_desc_free(struct ath5k_softc *sc,
+ struct pci_dev *pdev);
+/* Buffers setup */
+static int ath5k_rxbuf_init(struct ath5k_softc *sc,
+ struct ath5k_buf *bf);
+static int ath5k_tx_bf(struct ath5k_softc *sc,
+ struct ath5k_buf *bf,
+ struct ieee80211_tx_control *ctl);
+static inline void ath5k_cleanup_txbuf(struct ath5k_softc *sc,
+ struct ath5k_buf *bf);
+/* Queues setup */
+static struct ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc,
+ int qtype, int subtype);
+static int ath5k_beaconq_setup(struct ath5k_hw *ah);
+static int ath5k_beaconq_config(struct ath5k_softc *sc);
+static void ath5k_tx_draintxq(struct ath5k_softc *sc,
+ struct ath5k_txq *txq);
+static void ath5k_draintxq(struct ath5k_softc *sc);
+static void ath5k_tx_cleanup(struct ath5k_softc *sc);
+/* Rx handling */
+static int ath5k_startrecv(struct ath5k_softc *sc);
+static void ath5k_stoprecv(struct ath5k_softc *sc);
+static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
+ struct ath5k_desc *ds,
+ struct sk_buff *skb);
+static void ath5k_tasklet_rx(unsigned long data);
+/* Tx handling */
+static void ath5k_tx_processq(struct ath5k_softc *sc,
+ struct ath5k_txq *txq);
+static void ath5k_tasklet_tx(unsigned long data);
+/* Beacon handling */
+static int ath5k_beacon_setup(struct ath5k_softc *sc,
+ struct ath5k_buf *bf,
+ struct ieee80211_tx_control *ctl);
+static void ath5k_beacon_send(struct ath5k_softc *sc);
+static void ath5k_beacon_config(struct ath5k_softc *sc);
+static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp);
+/* Interrupt handling */
+static int ath5k_init(struct ath5k_softc *sc);
+static int ath5k_stop_locked(struct ath5k_softc *sc);
+static int ath5k_stop_hw(struct ath5k_softc *sc);
+static irqreturn_t ath5k_intr(int irq, void *dev_id);
+static void ath5k_tasklet_reset(unsigned long data);
+static inline void ath5k_update_txpow(struct ath5k_softc *sc);
+static void ath5k_calibrate(unsigned long data);
+/* LED functions */
+static void ath5k_led_off(unsigned long data);
+static void ath5k_led_blink(struct ath5k_softc *sc,
+ unsigned int on,
+ unsigned int off);
+static void ath5k_led_event(struct ath5k_softc *sc,
+ int event);
+
+
+/*
+ * Module init/exit functions
+ */
+static int __init
+init_ath5k_pci(void)
{
- struct ath5k_softc *sc = (void *)data;
+ int ret;
- ath5k_reset(sc->hw);
+ ret = pci_register_driver(&ath5k_pci_drv_id);
+ if (ret) {
+ printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
+ return ret;
+ }
+ ath5k_sysctl_header = register_sysctl_table(ath5k_root_table);
+
+ return 0;
}
-static void ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+static void __exit
+exit_ath5k_pci(void)
{
- struct ieee80211_tx_status txs = {};
- struct ath5k_buf *bf, *bf0;
- struct ath5k_desc *ds;
- struct sk_buff *skb;
+ if (ath5k_sysctl_header)
+ unregister_sysctl_table(ath5k_sysctl_header);
+ pci_unregister_driver(&ath5k_pci_drv_id);
+}
+
+module_init(init_ath5k_pci);
+module_exit(exit_ath5k_pci);
+
+
+/********************\
+* PCI Initialization *
+\********************/
+
+static const char *
+ath5k_chip_name(u8 mac_version)
+{
+ switch (mac_version) {
+ case AR5K_AR5210:
+ return "AR5210";
+ case AR5K_AR5211:
+ return "AR5211";
+ case AR5K_AR5212:
+ return "AR5212";
+ }
+ return "Unknown";
+}
+
+static int __devinit
+ath5k_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ void __iomem *mem;
+ struct ath5k_softc *sc;
+ struct ieee80211_hw *hw;
int ret;
+ u8 csz;
- spin_lock(&txq->lock);
- list_for_each_entry_safe(bf, bf0, &txq->q, list) {
- ds = bf->desc;
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't enable device\n");
+ goto err;
+ }
- /* TODO only one segment */
- pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
- sc->desc_len, PCI_DMA_FROMDEVICE);
- ret = sc->ah->ah_proc_tx_desc(sc->ah, ds);
- if (unlikely(ret == -EINPROGRESS))
- break;
- else if (unlikely(ret)) {
- printk(KERN_ERR "ath: error %d while processing "
- "queue %u\n", ret, txq->qnum);
- break;
+ /* XXX 32-bit addressing only */
+ ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (ret) {
+ dev_err(&pdev->dev, "32-bit DMA not available\n");
+ goto err_dis;
+ }
+
+ /*
+ * Cache line size is used to size and align various
+ * structures used to communicate with the hardware.
+ */
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+ if (csz == 0) {
+ /*
+ * Linux 2.4.18 (at least) writes the cache line size
+ * register as a 16-bit wide register which is wrong.
+ * We must have this setup properly for rx buffer
+ * DMA to work so force a reasonable value here if it
+ * comes up zero.
+ */
+ csz = L1_CACHE_BYTES / sizeof(u32);
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+ }
+ /*
+ * The default setting of latency timer yields poor results,
+ * set it to the value used by other systems. It may be worth
+ * tweaking this setting more.
+ */
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+ /* Enable bus mastering */
+ pci_set_master(pdev);
+
+ /*
+ * Disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state.
+ */
+ pci_write_config_byte(pdev, 0x41, 0);
+
+ ret = pci_request_region(pdev, 0, "ath5k");
+ if (ret) {
+ dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+ goto err_dis;
+ }
+
+ mem = pci_iomap(pdev, 0, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+ ret = -EIO;
+ goto err_reg;
+ }
+
+ /*
+ * Allocate hw (mac80211 main struct)
+ * and hw->priv (driver private data)
+ */
+ hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
+ if (hw == NULL) {
+ dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
+ ret = -ENOMEM;
+ goto err_map;
+ }
+
+ /* Initialize driver private data */
+ SET_IEEE80211_DEV(hw, &pdev->dev);
+ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS;
+ hw->extra_tx_headroom = 2;
+ hw->channel_change_time = 5000;
+ hw->max_rssi = 127; /* FIXME: get a real value for this. */
+ sc = hw->priv;
+ sc->hw = hw;
+ sc->pdev = pdev;
+
+ /*
+ * Mark the device as detached to avoid processing
+ * interrupts until setup is complete.
+ */
+#if AR_DEBUG
+ sc->debug = ath5k_debug;
+#endif
+ __set_bit(ATH_STAT_INVALID, sc->status);
+
+ sc->iobase = mem; /* So we can unmap it on detach */
+ sc->cachelsz = csz * sizeof(u32); /* convert to bytes */
+ sc->opmode = IEEE80211_IF_TYPE_STA;
+ mutex_init(&sc->lock);
+ spin_lock_init(&sc->rxbuflock);
+ spin_lock_init(&sc->txbuflock);
+
+ /* Set private data */
+ pci_set_drvdata(pdev, hw);
+
+ /* Enable msi for devices that support it */
+ pci_enable_msi(pdev);
+
+ /* Setup interrupt handler */
+ ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_free;
+ }
+
+ /* Initialize device */
+ sc->ah = ath5k_hw_attach(pdev->device, id->driver_data, sc, sc->iobase);
+ if (IS_ERR(sc->ah)) {
+ ret = PTR_ERR(sc->ah);
+ goto err_irq;
+ }
+
+ /* Finish private driver data initialization */
+ ret = ath5k_attach(pdev, hw);
+ if (ret)
+ goto err_ah;
+
+ dev_info(&pdev->dev, "%s chip found: mac %d.%d phy %d.%d\n",
+ ath5k_chip_name(id->driver_data), sc->ah->ah_mac_version,
+ sc->ah->ah_mac_revision, sc->ah->ah_phy_revision >> 4,
+ sc->ah->ah_phy_revision & 0xf);
+
+ /* ready to process interrupts */
+ __clear_bit(ATH_STAT_INVALID, sc->status);
+
+ return 0;
+err_ah:
+ ath5k_hw_detach(sc->ah);
+err_irq:
+ free_irq(pdev->irq, sc);
+err_free:
+ pci_disable_msi(pdev);
+ ieee80211_free_hw(hw);
+err_map:
+ pci_iounmap(pdev, mem);
+err_reg:
+ pci_release_region(pdev, 0);
+err_dis:
+ pci_disable_device(pdev);
+err:
+ return ret;
+}
+
+static void __devexit
+ath5k_pci_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_softc *sc = hw->priv;
+
+ ath5k_detach(pdev, hw);
+ ath5k_hw_detach(sc->ah);
+ free_irq(pdev->irq, sc);
+ pci_disable_msi(pdev);
+ pci_iounmap(pdev, sc->iobase);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ ieee80211_free_hw(hw);
+}
+
+#ifdef CONFIG_PM
+static int
+ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_softc *sc = hw->priv;
+
+ if (test_bit(ATH_STAT_LEDSOFT, sc->status))
+ ath5k_hw_set_gpio(sc->ah, sc->led_pin, 1);
+
+ ath5k_stop_hw(sc);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int
+ath5k_pci_resume(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ath5k_softc *sc = hw->priv;
+ int err;
+
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err)
+ return err;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_restore_state(pdev);
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state
+ */
+ pci_write_config_byte(pdev, 0x41, 0);
+
+ ath5k_init(sc);
+ if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
+ ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
+ ath5k_hw_set_gpio(sc->ah, sc->led_pin, 0);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+
+/***********************\
+* Driver Initialization *
+\***********************/
+
+static int
+ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+ struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = sc->ah;
+ u8 mac[ETH_ALEN];
+ unsigned int i;
+ int ret;
+
+ DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, pdev->device);
+
+ /*
+ * Check if the MAC has multi-rate retry support.
+ * We do this by trying to setup a fake extended
+ * descriptor. MAC's that don't have support will
+ * return false w/o doing anything. MAC's that do
+ * support it will return true w/o doing anything.
+ */
+ if (ah->ah_setup_xtx_desc(ah, NULL, 0, 0, 0, 0, 0, 0))
+ __set_bit(ATH_STAT_MRRETRY, sc->status);
+
+ /*
+ * Reset the key cache since some parts do not
+ * reset the contents on initial power up.
+ */
+ for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
+ ath5k_hw_reset_key(ah, i);
+
+ /*
+ * Collect the channel list. The 802.11 layer
+ * is resposible for filtering this list based
+ * on settings like the phy mode and regulatory
+ * domain restrictions.
+ */
+ ret = ath5k_getchannels(hw);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get channels\n");
+ goto err;
+ }
+
+ /* NB: setup here so ath5k_rate_update is happy */
+ if (test_bit(MODE_IEEE80211A, ah->ah_modes))
+ ath5k_setcurmode(sc, MODE_IEEE80211A);
+ else
+ ath5k_setcurmode(sc, MODE_IEEE80211B);
+
+ /*
+ * Allocate tx+rx descriptors and populate the lists.
+ */
+ ret = ath5k_desc_alloc(sc, pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't allocate descriptors\n");
+ goto err;
+ }
+
+ /*
+ * Allocate hardware transmit queues: one queue for
+ * beacon frames and one data queue for each QoS
+ * priority. Note that hw functions handle reseting
+ * these queues at the needed time.
+ */
+ ret = ath5k_beaconq_setup(ah);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't setup a beacon xmit queue\n");
+ goto err_desc;
+ }
+ sc->bhalq = ret;
+
+ sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+ if (IS_ERR(sc->txq)) {
+ dev_err(&pdev->dev, "can't setup xmit queue\n");
+ ret = PTR_ERR(sc->txq);
+ goto err_bhal;
+ }
+
+ tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
+ tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
+ tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
+ setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
+ setup_timer(&sc->led_tim, ath5k_led_off, (unsigned long)sc);
+
+ sc->led_on = 0; /* low true */
+ /*
+ * Auto-enable soft led processing for IBM cards and for
+ * 5211 minipci cards. Users can also manually enable/disable
+ * support with a sysctl.
+ */
+ if (pdev->device == PCI_DEVICE_ID_ATHEROS_AR5212_IBM ||
+ pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) {
+ __set_bit(ATH_STAT_LEDSOFT, sc->status);
+ sc->led_pin = 0;
+ }
+ /* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) {
+ __set_bit(ATH_STAT_LEDSOFT, sc->status);
+ sc->led_pin = 0;
+ }
+ if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
+ ath5k_hw_set_gpio_output(ah, sc->led_pin);
+ ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on);
+ }
+
+ ath5k_hw_get_lladdr(ah, mac);
+ SET_IEEE80211_PERM_ADDR(hw, mac);
+ /* All MAC address bits matter for ACKs */
+ memset(sc->bssidmask, 0xff, ETH_ALEN);
+ ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+
+ ret = ieee80211_register_hw(hw);
+ if (ret) {
+ dev_err(&pdev->dev, "can't register ieee80211 hw\n");
+ goto err_queues;
+ }
+
+ return 0;
+err_queues:
+ ath5k_tx_cleanup(sc);
+err_bhal:
+ ath5k_hw_release_tx_queue(ah, sc->bhalq);
+err_desc:
+ ath5k_desc_free(sc, pdev);
+err:
+ return ret;
+}
+
+static void
+ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+ struct ath5k_softc *sc = hw->priv;
+
+ /*
+ * NB: the order of these is important:
+ * o call the 802.11 layer before detaching ath5k_hw to
+ * insure callbacks into the driver to delete global
+ * key cache entries can be handled
+ * o reclaim the tx queue data structures after calling
+ * the 802.11 layer as we'll get called back to reclaim
+ * node state and potentially want to use them
+ * o to cleanup the tx queues the hal is called, so detach
+ * it last
+ * XXX: ??? detach ath5k_hw ???
+ * Other than that, it's straightforward...
+ */
+ ieee80211_unregister_hw(hw);
+ ath5k_desc_free(sc, pdev);
+ ath5k_tx_cleanup(sc);
+ ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
+
+ /*
+ * NB: can't reclaim these until after ieee80211_ifdetach
+ * returns because we'll get called back to reclaim node
+ * state and potentially want to use them.
+ */
+}
+
+
+
+
+/********************\
+* Channel/mode setup *
+\********************/
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static inline short
+ath5k_ieee2mhz(short chan)
+{
+ if (chan <= 14 || chan >= 27)
+ return ieee80211chan2mhz(chan);
+ else
+ return 2212 + chan * 20;
+}
+
+static unsigned int
+ath5k_copy_rates(struct ieee80211_rate *rates,
+ const struct ath5k_rate_table *rt,
+ unsigned int max)
+{
+ unsigned int i, count;
+
+ if (rt == NULL)
+ return 0;
+
+ for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) {
+ if (!rt->rates[i].valid)
+ continue;
+ rates->rate = rt->rates[i].rate_kbps / 100;
+ rates->val = rt->rates[i].rate_code;
+ rates->flags = rt->rates[i].modulation;
+ rates++;
+ count++;
+ max--;
+ }
+
+ return count;
+}
+
+static unsigned int
+ath5k_copy_channels(struct ath5k_hw *ah,
+ struct ieee80211_channel *channels,
+ unsigned int mode,
+ unsigned int max)
+{
+ static const struct { unsigned int mode, mask, chan; } map[] = {
+ [MODE_IEEE80211A] = { CHANNEL_OFDM, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_A },
+ [MODE_ATHEROS_TURBO] = { CHANNEL_OFDM|CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_T },
+ [MODE_IEEE80211B] = { CHANNEL_CCK, CHANNEL_CCK, CHANNEL_B },
+ [MODE_IEEE80211G] = { CHANNEL_OFDM, CHANNEL_OFDM, CHANNEL_G },
+ [MODE_ATHEROS_TURBOG] = { CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_TG },
+ };
+ static const struct ath5k_regchannel chans_2ghz[] =
+ IEEE80211_CHANNELS_2GHZ;
+ static const struct ath5k_regchannel chans_5ghz[] =
+ IEEE80211_CHANNELS_5GHZ;
+ const struct ath5k_regchannel *chans;
+ enum ath5k_regdom dmn;
+ unsigned int i, count, size, chfreq, all, f, ch;
+
+ if (!test_bit(mode, ah->ah_modes))
+ return 0;
+
+ all = ah->ah_regdomain == DMN_DEFAULT || CHAN_DEBUG == 1;
+
+ switch (mode) {
+ case MODE_IEEE80211A:
+ case MODE_ATHEROS_TURBO:
+ /* 1..220, but 2GHz frequencies are filtered by check_channel */
+ size = all ? 220 : ARRAY_SIZE(chans_5ghz);
+ chans = chans_5ghz;
+ dmn = ath5k_regdom2flag(ah->ah_regdomain,
+ IEEE80211_CHANNELS_5GHZ_MIN);
+ chfreq = CHANNEL_5GHZ;
+ break;
+ case MODE_IEEE80211B:
+ case MODE_IEEE80211G:
+ case MODE_ATHEROS_TURBOG:
+ size = all ? 26 : ARRAY_SIZE(chans_2ghz);
+ chans = chans_2ghz;
+ dmn = ath5k_regdom2flag(ah->ah_regdomain,
+ IEEE80211_CHANNELS_2GHZ_MIN);
+ chfreq = CHANNEL_2GHZ;
+ break;
+ default:
+ printk(KERN_WARNING "bad mode, not copying channels\n");
+ return 0;
+ }
+
+ for (i = 0, count = 0; i < size && max > 0; i++) {
+ ch = all ? i + 1 : chans[i].chan;
+ f = ath5k_ieee2mhz(ch);
+ /* Check if channel is supported by the chipset */
+ if (!ath5k_channel_ok(ah, f, chfreq))
+ continue;
+
+ /* Match regulation domain */
+ if (!all && !(IEEE80211_DMN(chans[i].domain) &
+ IEEE80211_DMN(dmn)))
+ continue;
+
+ if (!all && (chans[i].mode & map[mode].mask) != map[mode].mode)
+ continue;
+
+ /* Write channel and increment counter */
+ channels->chan = ch;
+ channels->freq = f;
+ channels->val = map[mode].chan;
+ channels++;
+ count++;
+ max--;
+ }
+
+ return count;
+}
+
+/* Only tries to register modes our EEPROM says it can support */
+#define REGISTER_MODE(m) do { \
+ if (test_bit(m, ah->ah_capabilities.cap_mode)) { \
+ ret = ath5k_register_mode(hw, m); \
+ if (ret) \
+ return ret; \
+ } \
+} while (0) \
+
+static inline int
+ath5k_register_mode(struct ieee80211_hw *hw, u8 m)
+{
+ struct ath5k_softc *sc = hw->priv;
+ struct ieee80211_hw_mode *modes = sc->modes;
+ int i, ret;
+
+ for (i = 0; i < NUM_DRIVER_MODES; i++) {
+ if (modes[i].mode != m || !modes[i].num_channels)
+ continue;
+ ret = ieee80211_register_hwmode(hw, &modes[i]);
+ if (ret) {
+ printk(KERN_ERR "can't register hwmode %u\n", m);
+ return ret;
}
+ return 0;
+ }
+ return 1;
+}
- skb = bf->skb;
- bf->skb = NULL;
- pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
- PCI_DMA_TODEVICE);
+static int
+ath5k_getchannels(struct ieee80211_hw *hw)
+{
+ struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = sc->ah;
+ struct ieee80211_hw_mode *modes = sc->modes;
+ unsigned int i, max_r, max_c;
+ int ret;
- txs.control = bf->ctl;
- txs.retry_count = ds->ds_txstat.ts_shortretry +
- ds->ds_txstat.ts_longretry / 6;
- if (unlikely(ds->ds_txstat.ts_status)) {
- sc->ll_stats.dot11ACKFailureCount++;
- if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY)
- txs.excessive_retries = 1;
- else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT)
- txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
+ BUILD_BUG_ON(ARRAY_SIZE(sc->modes) < 3);
+
+ /* The order here does not matter */
+ modes[0].mode = MODE_IEEE80211G;
+ modes[1].mode = MODE_IEEE80211B;
+ modes[2].mode = MODE_IEEE80211A;
+
+ max_r = ARRAY_SIZE(sc->rates);
+ max_c = ARRAY_SIZE(sc->channels);
+
+ for (i = 0; i < NUM_DRIVER_MODES; i++) {
+ struct ieee80211_hw_mode *mode = &modes[i];
+ const struct ath5k_rate_table *hw_rates;
+
+ if (i == 0) {
+ modes[0].rates = sc->rates;
+ modes->channels = sc->channels;
} else {
- txs.flags |= IEEE80211_TX_STATUS_ACK;
- txs.ack_signal = ds->ds_txstat.ts_rssi;
+ struct ieee80211_hw_mode *prev_mode = &modes[i-1];
+ int prev_num_r = prev_mode->num_rates;
+ int prev_num_c = prev_mode->num_channels;
+ mode->rates = &prev_mode->rates[prev_num_r];
+ mode->channels = &prev_mode->channels[prev_num_c];
}
- ieee80211_tx_status(sc->hw, skb, &txs);
- sc->tx_stats.data[txq->qnum].count++;
+ hw_rates = ath5k_hw_get_rate_table(ah, mode->mode);
+ if (!hw_rates)
+ return -EINVAL;
- spin_lock(&sc->txbuflock);
- sc->tx_stats.data[txq->qnum].len--;
- list_move_tail(&bf->list, &sc->txbuf);
- sc->txbuf_len++;
- spin_unlock(&sc->txbuflock);
+ mode->num_rates = ath5k_copy_rates(mode->rates, hw_rates,
+ max_r);
+ mode->num_channels = ath5k_copy_channels(ah, mode->channels,
+ mode->mode, max_c);
+ max_r -= mode->num_rates;
+ max_c -= mode->num_channels;
}
- if (likely(list_empty(&txq->q)))
- txq->link = NULL;
- spin_unlock(&txq->lock);
- if (sc->txbuf_len > ATH_TXBUF / 5)
- ieee80211_wake_queues(sc->hw);
+
+ /* We try to register all modes this driver supports. We don't bother
+ * with MODE_IEEE80211B for AR5212 as MODE_IEEE80211G already accounts
+ * for that as per mac80211. Then, REGISTER_MODE() will will actually
+ * check the eeprom reading for more reliable capability information.
+ * Order matters here as per mac80211's latest preference. This will
+ * all hopefullly soon go away. */
+
+ REGISTER_MODE(MODE_IEEE80211G);
+ if (ah->ah_version != AR5K_AR5212)
+ REGISTER_MODE(MODE_IEEE80211B);
+ REGISTER_MODE(MODE_IEEE80211A);
+
+ ath5k_dump_modes(modes);
+
+ return ret;
}
-static void ath5k_tasklet_tx(unsigned long data)
+/*
+ * Set/change channels. If the channel is really being changed,
+ * it's done by reseting the chip. To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath5k_init.
+ */
+static int
+ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
{
- struct ath5k_softc *sc = (void *)data;
+ struct ath5k_hw *ah = sc->ah;
+ int ret;
- ath5k_tx_processq(sc, sc->txq);
+ DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n",
+ __func__, sc->curchan->chan, sc->curchan->freq,
+ chan->chan, chan->freq);
- ath5k_led_event(sc, ATH_LED_TX);
+ if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) {
+ /*
+ * To switch channels clear any pending DMA operations;
+ * wait long enough for the RX fifo to drain, reset the
+ * hardware at the new frequency, and then re-enable
+ * the relevant bits of the h/w.
+ */
+ ath5k_hw_set_intr(ah, 0); /* disable interrupts */
+ ath5k_draintxq(sc); /* clear pending tx frames */
+ ath5k_stoprecv(sc); /* turn off frame recv */
+ ret = ath5k_hw_reset(ah, sc->opmode, chan, true);
+ if (ret) {
+ printk(KERN_ERR "%s: unable to reset channel %u "
+ "(%u Mhz)\n", __func__, chan->chan, chan->freq);
+ return ret;
+ }
+ sc->curchan = chan;
+ ath5k_update_txpow(sc);
+
+ /*
+ * Re-enable rx framework.
+ */
+ ret = ath5k_startrecv(sc);
+ if (ret) {
+ printk(KERN_ERR "%s: unable to restart recv logic\n",
+ __func__);
+ return ret;
+ }
+
+ /*
+ * Change channels and update the h/w rate map
+ * if we're switching; e.g. 11a to 11b/g.
+ *
+ * XXX needed?
+ */
+/* ath5k_chan_change(sc, chan); */
+
+ /*
+ * Re-enable interrupts.
+ */
+ ath5k_hw_set_intr(ah, sc->imask);
+ }
+
+ return 0;
}
-static int ath5k_rxbuf_init(struct ath5k_softc *sc, struct ath5k_buf *bf)
+static void
+ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
+{
+ if (unlikely(test_bit(ATH_STAT_LEDSOFT, sc->status))) {
+ /* from Atheros NDIS driver, w/ permission */
+ static const struct {
+ u16 rate; /* tx/rx 802.11 rate */
+ u16 timeOn; /* LED on time (ms) */
+ u16 timeOff; /* LED off time (ms) */
+ } blinkrates[] = {
+ { 108, 40, 10 },
+ { 96, 44, 11 },
+ { 72, 50, 13 },
+ { 48, 57, 14 },
+ { 36, 67, 16 },
+ { 24, 80, 20 },
+ { 22, 100, 25 },
+ { 18, 133, 34 },
+ { 12, 160, 40 },
+ { 10, 200, 50 },
+ { 6, 240, 58 },
+ { 4, 267, 66 },
+ { 2, 400, 100 },
+ { 0, 500, 130 }
+ };
+ const struct ath5k_rate_table *rt =
+ ath5k_hw_get_rate_table(sc->ah, mode);
+ unsigned int i, j;
+
+ BUG_ON(rt == NULL);
+
+ memset(sc->hwmap, 0, sizeof(sc->hwmap));
+ for (i = 0; i < 32; i++) {
+ u8 ix = rt->rate_code_to_index[i];
+ if (ix == 0xff) {
+ sc->hwmap[i].ledon = msecs_to_jiffies(500);
+ sc->hwmap[i].ledoff = msecs_to_jiffies(130);
+ continue;
+ }
+ sc->hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
+ if (SHPREAMBLE_FLAG(ix) || rt->rates[ix].modulation ==
+ IEEE80211_RATE_OFDM)
+ sc->hwmap[i].txflags |=
+ IEEE80211_RADIOTAP_F_SHORTPRE;
+ /* receive frames include FCS */
+ sc->hwmap[i].rxflags = sc->hwmap[i].txflags |
+ IEEE80211_RADIOTAP_F_FCS;
+ /* setup blink rate table to avoid per-packet lookup */
+ for (j = 0; j < ARRAY_SIZE(blinkrates) - 1; j++)
+ if (blinkrates[j].rate == /* XXX why 7f? */
+ (rt->rates[ix].dot11_rate&0x7f))
+ break;
+
+ sc->hwmap[i].ledon = msecs_to_jiffies(blinkrates[j].
+ timeOn);
+ sc->hwmap[i].ledoff = msecs_to_jiffies(blinkrates[j].
+ timeOff);
+ }
+ }
+
+ sc->curmode = mode;
+}
+
+static void
+ath5k_mode_init(struct ath5k_softc *sc)
+{
+ struct ath5k_hw *ah = sc->ah;
+ u32 rfilt;
+
+ /* configure rx filter */
+ rfilt = sc->filter_flags;
+ ath5k_hw_set_rx_filter(ah, rfilt);
+
+ if (ath5k_hw_hasbssidmask(ah))
+ ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+
+ /* configure operational mode */
+ ath5k_hw_set_opmode(ah);
+
+ ath5k_hw_set_mcast_filter(ah, 0, 0);
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt);
+}
+
+
+
+
+/*******************\
+* Descriptors setup *
+\*******************/
+
+static int
+ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
+{
+ struct ath5k_desc *ds;
+ struct ath5k_buf *bf;
+ dma_addr_t da;
+ unsigned int i;
+ int ret;
+
+ /* allocate descriptors */
+ sc->desc_len = sizeof(struct ath5k_desc) *
+ (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
+ sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
+ if (sc->desc == NULL) {
+ dev_err(&pdev->dev, "can't allocate descriptors\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ ds = sc->desc;
+ da = sc->desc_daddr;
+ DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%zu) -> %llx\n",
+ __func__, ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
+
+ bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
+ sizeof(struct ath5k_buf), GFP_KERNEL);
+ if (bf == NULL) {
+ dev_err(&pdev->dev, "can't allocate bufptr\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ sc->bufptr = bf;
+
+ INIT_LIST_HEAD(&sc->rxbuf);
+ for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+ bf->desc = ds;
+ bf->daddr = da;
+ list_add_tail(&bf->list, &sc->rxbuf);
+ }
+
+ INIT_LIST_HEAD(&sc->txbuf);
+ sc->txbuf_len = ATH_TXBUF;
+ for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
+ da += sizeof(*ds)) {
+ bf->desc = ds;
+ bf->daddr = da;
+ list_add_tail(&bf->list, &sc->txbuf);
+ }
+
+ /* beacon buffer */
+ bf->desc = ds;
+ bf->daddr = da;
+ sc->bbuf = bf;
+
+ return 0;
+err_free:
+ pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+err:
+ sc->desc = NULL;
+ return ret;
+}
+
+static void
+ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
+{
+ struct ath5k_buf *bf;
+
+ ath5k_cleanup_txbuf(sc, sc->bbuf);
+ list_for_each_entry(bf, &sc->txbuf, list)
+ ath5k_cleanup_txbuf(sc, bf);
+ list_for_each_entry(bf, &sc->rxbuf, list)
+ ath5k_cleanup_txbuf(sc, bf);
+
+ /* Free memory associated with all descriptors */
+ pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+
+ kfree(sc->bufptr);
+ sc->bufptr = NULL;
+}
+
+
+
+
+/***************\
+* Buffers setup *
+\***************/
+
+static int
+ath5k_rxbuf_init(struct ath5k_softc *sc, struct ath5k_buf *bf)
{
struct ath5k_hw *ah = sc->ah;
struct sk_buff *skb = bf->skb;
@@ -318,8 +1354,337 @@ static int ath5k_rxbuf_init(struct ath5k_softc *sc, struct ath5k_buf *bf)
return 0;
}
-static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
- struct ath5k_desc *ds, struct sk_buff *skb)
+static int
+ath5k_tx_bf(struct ath5k_softc *sc, struct ath5k_buf *bf,
+ struct ieee80211_tx_control *ctl)
+{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath5k_txq *txq = sc->txq;
+ struct ath5k_desc *ds = bf->desc;
+ struct sk_buff *skb = bf->skb;
+ unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
+ int ret;
+
+ flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
+ bf->ctl = *ctl;
+ /* XXX endianness */
+ bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+
+ if (ctl->flags & IEEE80211_TXCTL_NO_ACK)
+ flags |= AR5K_TXDESC_NOACK;
+
+ pktlen = skb->len + FCS_LEN;
+
+ if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) {
+ keyidx = ctl->key_idx;
+ pktlen += ctl->icv_len;
+ }
+
+ ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+ ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
+ 0xffff, ctl->tx_rate, ctl->retry_limit, keyidx, 0, flags, 0, 0);
+ if (ret)
+ goto err_unmap;
+
+ ds->ds_link = 0;
+ ds->ds_data = bf->skbaddr;
+
+ ret = ah->ah_fill_tx_desc(ah, ds, skb->len, true, true);
+ if (ret)
+ goto err_unmap;
+
+ spin_lock_bh(&txq->lock);
+ list_add_tail(&bf->list, &txq->q);
+ sc->tx_stats.data[txq->qnum].len++;
+ if (txq->link == NULL) /* is this first packet? */
+ ath5k_hw_put_tx_buf(ah, txq->qnum, bf->daddr);
+ else /* no, so only link it */
+ *txq->link = bf->daddr;
+
+ txq->link = &ds->ds_link;
+ ath5k_hw_tx_start(ah, txq->qnum);
+ spin_unlock_bh(&txq->lock);
+
+ return 0;
+err_unmap:
+ pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+ return ret;
+}
+
+static inline void
+ath5k_cleanup_txbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+ BUG_ON(!bf);
+ if (!bf->skb)
+ return;
+ pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(bf->skb);
+ bf->skb = NULL;
+}
+
+
+
+
+/**************\
+* Queues setup *
+\**************/
+
+static struct ath5k_txq *
+ath5k_txq_setup(struct ath5k_softc *sc,
+ int qtype, int subtype)
+{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath5k_txq *txq;
+ struct ath5k_txq_info qi = {
+ .tqi_subtype = subtype,
+ .tqi_aifs = AR5K_TXQ_USEDEFAULT,
+ .tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+ .tqi_cw_max = AR5K_TXQ_USEDEFAULT
+ };
+ int qnum;
+
+ /*
+ * Enable interrupts only for EOL and DESC conditions.
+ * We mark tx descriptors to receive a DESC interrupt
+ * when a tx queue gets deep; otherwise waiting for the
+ * EOL to reap descriptors. Note that this is done to
+ * reduce interrupt load and this only defers reaping
+ * descriptors, never transmitting frames. Aside from
+ * reducing interrupts this also permits more concurrency.
+ * The only potential downside is if the tx queue backs
+ * up in which case the top half of the kernel may backup
+ * due to a lack of tx descriptors.
+ */
+ qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
+ AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
+ qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
+ if (qnum < 0) {
+ /*
+ * NB: don't print a message, this happens
+ * normally on parts with too few tx queues
+ */
+ return ERR_PTR(qnum);
+ }
+ if (qnum >= ARRAY_SIZE(sc->txqs)) {
+ printk(KERN_ERR "hw qnum %u out of range, max %tu!\n",
+ qnum, ARRAY_SIZE(sc->txqs));
+ ath5k_hw_release_tx_queue(ah, qnum);
+ return ERR_PTR(-EINVAL);
+ }
+ txq = &sc->txqs[qnum];
+ if (!txq->setup) {
+ txq->qnum = qnum;
+ txq->link = NULL;
+ INIT_LIST_HEAD(&txq->q);
+ spin_lock_init(&txq->lock);
+ txq->setup = true;
+ }
+ return &sc->txqs[qnum];
+}
+
+static int
+ath5k_beaconq_setup(struct ath5k_hw *ah)
+{
+ struct ath5k_txq_info qi = {
+ .tqi_aifs = AR5K_TXQ_USEDEFAULT,
+ .tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+ .tqi_cw_max = AR5K_TXQ_USEDEFAULT,
+ /* NB: for dynamic turbo, don't enable any other interrupts */
+ .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
+ };
+
+ return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
+}
+
+static int
+ath5k_beaconq_config(struct ath5k_softc *sc)
+{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath5k_txq_info qi;
+ int ret;
+
+ ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
+ if (ret)
+ return ret;
+ if (sc->opmode == IEEE80211_IF_TYPE_AP ||
+ sc->opmode == IEEE80211_IF_TYPE_IBSS) {
+ /*
+ * Always burst out beacon and CAB traffic
+ * (aifs = cwmin = cwmax = 0)
+ */
+ qi.tqi_aifs = 0;
+ qi.tqi_cw_min = 0;
+ qi.tqi_cw_max = 0;
+ }
+
+ ret = ath5k_hw_setup_tx_queueprops(ah, sc->bhalq, &qi);
+ if (ret) {
+ printk(KERN_ERR "%s: unable to update parameters for beacon "
+ "hardware queue!\n", __func__);
+ return ret;
+ }
+
+ return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */;
+}
+
+static void
+ath5k_tx_draintxq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+{
+ struct ath5k_buf *bf, *bf0;
+
+ /*
+ * NB: this assumes output has been stopped and
+ * we do not need to block ath5k_tx_tasklet
+ */
+ spin_lock_bh(&txq->lock);
+ list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+#if AR_DEBUG
+ if (sc->debug & ATH_DEBUG_RESET)
+ ath5k_printtxbuf(bf, !sc->ah->ah_proc_tx_desc(sc->ah,
+ bf->desc));
+#endif
+ ath5k_cleanup_txbuf(sc, bf);
+
+ spin_lock_bh(&sc->txbuflock);
+ sc->tx_stats.data[txq->qnum].len--;
+ list_move_tail(&bf->list, &sc->txbuf);
+ sc->txbuf_len++;
+ spin_unlock_bh(&sc->txbuflock);
+ }
+ txq->link = NULL;
+ spin_unlock_bh(&txq->lock);
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath5k_draintxq(struct ath5k_softc *sc)
+{
+ struct ath5k_hw *ah = sc->ah;
+ int i;
+
+ /* XXX return value */
+ if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
+ /* don't touch the hardware if marked invalid */
+ (void)ath5k_hw_stop_tx_dma(ah, sc->bhalq);
+ DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__,
+ ath5k_hw_get_tx_buf(ah, sc->bhalq));
+ for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+ if (sc->txqs[i].setup) {
+ ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
+ DPRINTF(sc, ATH_DEBUG_RESET, "%s: txq [%u] %x, "
+ "link %p\n", __func__,
+ sc->txqs[i].qnum,
+ ath5k_hw_get_tx_buf(ah,
+ sc->txqs[i].qnum),
+ sc->txqs[i].link);
+ }
+ }
+ ieee80211_start_queues(sc->hw); /* XXX move to callers */
+
+ for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+ if (sc->txqs[i].setup)
+ ath5k_tx_draintxq(sc, &sc->txqs[i]);
+}
+
+static void
+ath5k_tx_cleanup(struct ath5k_softc *sc)
+{
+ struct ath5k_txq *txq = sc->txqs;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
+ if (txq->setup) {
+ ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
+ txq->setup = false;
+ }
+}
+
+
+
+
+/*************\
+* RX Handling *
+\*************/
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath5k_startrecv(struct ath5k_softc *sc)
+{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath5k_buf *bf;
+ int ret;
+
+ sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz);
+
+ DPRINTF(sc, ATH_DEBUG_RESET, "%s: cachelsz %u rxbufsize %u\n",
+ __func__, sc->cachelsz, sc->rxbufsize);
+
+ sc->rxlink = NULL;
+
+ spin_lock_bh(&sc->rxbuflock);
+ list_for_each_entry(bf, &sc->rxbuf, list) {
+ ret = ath5k_rxbuf_init(sc, bf);
+ if (ret != 0) {
+ spin_unlock_bh(&sc->rxbuflock);
+ goto err;
+ }
+ }
+ bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+ spin_unlock_bh(&sc->rxbuflock);
+
+ ath5k_hw_put_rx_buf(ah, bf->daddr);
+ ath5k_hw_start_rx(ah); /* enable recv descriptors */
+ ath5k_mode_init(sc); /* set filters, etc. */
+ ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */
+
+ return 0;
+err:
+ return ret;
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath5k_stoprecv(struct ath5k_softc *sc)
+{
+ struct ath5k_hw *ah = sc->ah;
+
+ ath5k_hw_stop_pcu_recv(ah); /* disable PCU */
+ ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */
+ ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */
+ mdelay(3); /* 3ms is long enough for 1 frame */
+#if AR_DEBUG
+ if (unlikely(sc->debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL))) {
+ struct ath5k_desc *ds;
+ struct ath5k_buf *bf;
+ int status;
+
+ printk(KERN_DEBUG "%s: rx queue %x, link %p\n", __func__,
+ ath5k_hw_get_rx_buf(ah), sc->rxlink);
+
+ spin_lock_bh(&sc->rxbuflock);
+ list_for_each_entry(bf, &sc->rxbuf, list) {
+ ds = bf->desc;
+ status = ah->ah_proc_rx_desc(ah, ds);
+ if (!status || (sc->debug & ATH_DEBUG_FATAL))
+ ath5k_printrxbuf(bf, status == 0);
+ }
+ spin_unlock_bh(&sc->rxbuflock);
+ }
+#endif
+ sc->rxlink = NULL; /* just in case */
+}
+
+static unsigned int
+ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
+ struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -343,17 +1708,8 @@ static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
return 0;
}
-static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
-{
- u64 tsf = ath5k_hw_get_tsf64(ah);
-
- if ((tsf & 0x7fff) < rstamp)
- tsf -= 0x8000;
-
- return (tsf & ~0x7fff) | rstamp;
-}
-
-static void ath5k_tasklet_rx(unsigned long data)
+static void
+ath5k_tasklet_rx(unsigned long data)
{
struct ieee80211_rx_status rxs = {};
struct sk_buff *skb;
@@ -477,10 +1833,95 @@ next:
spin_unlock(&sc->rxbuflock);
}
+
+
+
+/*************\
+* TX Handling *
+\*************/
+
+static void
+ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+{
+ struct ieee80211_tx_status txs = {};
+ struct ath5k_buf *bf, *bf0;
+ struct ath5k_desc *ds;
+ struct sk_buff *skb;
+ int ret;
+
+ spin_lock(&txq->lock);
+ list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+ ds = bf->desc;
+
+ /* TODO only one segment */
+ pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
+ sc->desc_len, PCI_DMA_FROMDEVICE);
+ ret = sc->ah->ah_proc_tx_desc(sc->ah, ds);
+ if (unlikely(ret == -EINPROGRESS))
+ break;
+ else if (unlikely(ret)) {
+ printk(KERN_ERR "ath: error %d while processing "
+ "queue %u\n", ret, txq->qnum);
+ break;
+ }
+
+ skb = bf->skb;
+ bf->skb = NULL;
+ pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
+ PCI_DMA_TODEVICE);
+
+ txs.control = bf->ctl;
+ txs.retry_count = ds->ds_txstat.ts_shortretry +
+ ds->ds_txstat.ts_longretry / 6;
+ if (unlikely(ds->ds_txstat.ts_status)) {
+ sc->ll_stats.dot11ACKFailureCount++;
+ if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY)
+ txs.excessive_retries = 1;
+ else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT)
+ txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
+ } else {
+ txs.flags |= IEEE80211_TX_STATUS_ACK;
+ txs.ack_signal = ds->ds_txstat.ts_rssi;
+ }
+
+ ieee80211_tx_status(sc->hw, skb, &txs);
+ sc->tx_stats.data[txq->qnum].count++;
+
+ spin_lock(&sc->txbuflock);
+ sc->tx_stats.data[txq->qnum].len--;
+ list_move_tail(&bf->list, &sc->txbuf);
+ sc->txbuf_len++;
+ spin_unlock(&sc->txbuflock);
+ }
+ if (likely(list_empty(&txq->q)))
+ txq->link = NULL;
+ spin_unlock(&txq->lock);
+ if (sc->txbuf_len > ATH_TXBUF / 5)
+ ieee80211_wake_queues(sc->hw);
+}
+
+static void
+ath5k_tasklet_tx(unsigned long data)
+{
+ struct ath5k_softc *sc = (void *)data;
+
+ ath5k_tx_processq(sc, sc->txq);
+
+ ath5k_led_event(sc, ATH_LED_TX);
+}
+
+
+
+
+/*****************\
+* Beacon handling *
+\*****************/
+
/*
* Setup the beacon frame for transmit.
*/
-static int ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
+static int
+ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
struct ieee80211_tx_control *ctl)
{
struct sk_buff *skb = bf->skb;
@@ -545,7 +1986,8 @@ err_unmap:
* but also from ath5k_beacon_config() in IBSS mode which in turn
* can be called from a tasklet and user context
*/
-static void ath5k_beacon_send(struct ath5k_softc *sc)
+static void
+ath5k_beacon_send(struct ath5k_softc *sc)
{
struct ath5k_buf *bf = sc->bbuf;
struct ath5k_hw *ah = sc->ah;
@@ -606,36 +2048,6 @@ static void ath5k_beacon_send(struct ath5k_softc *sc)
sc->bsent++;
}
-static int ath5k_beaconq_config(struct ath5k_softc *sc)
-{
- struct ath5k_hw *ah = sc->ah;
- struct ath5k_txq_info qi;
- int ret;
-
- ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
- if (ret)
- return ret;
- if (sc->opmode == IEEE80211_IF_TYPE_AP ||
- sc->opmode == IEEE80211_IF_TYPE_IBSS) {
- /*
- * Always burst out beacon and CAB traffic
- * (aifs = cwmin = cwmax = 0)
- */
- qi.tqi_aifs = 0;
- qi.tqi_cw_min = 0;
- qi.tqi_cw_max = 0;
- }
-
- ret = ath5k_hw_setup_tx_queueprops(ah, sc->bhalq, &qi);
- if (ret) {
- printk(KERN_ERR "%s: unable to update parameters for beacon "
- "hardware queue!\n", __func__);
- return ret;
- }
-
- return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */;
-}
-
/*
* Configure the beacon and sleep timers.
*
@@ -651,7 +2063,8 @@ static int ath5k_beaconq_config(struct ath5k_softc *sc)
* interrupt when we stop seeing beacons from the AP
* we've associated with.
*/
-static void ath5k_beacon_config(struct ath5k_softc *sc)
+static void
+ath5k_beacon_config(struct ath5k_softc *sc)
{
#define TSF_TO_TU(_h, _l) (((_h) << 22) | ((_l) >> 10))
struct ath5k_hw *ah = sc->ah;
@@ -718,70 +2131,26 @@ static void ath5k_beacon_config(struct ath5k_softc *sc)
#undef TSF_TO_TU
}
-static void ath5k_mode_init(struct ath5k_softc *sc)
+static inline
+u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
{
- struct ath5k_hw *ah = sc->ah;
- u32 rfilt;
-
- /* configure rx filter */
- rfilt = sc->filter_flags;
- ath5k_hw_set_rx_filter(ah, rfilt);
-
- if (ath5k_hw_hasbssidmask(ah))
- ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+ u64 tsf = ath5k_hw_get_tsf64(ah);
- /* configure operational mode */
- ath5k_hw_set_opmode(ah);
+ if ((tsf & 0x7fff) < rstamp)
+ tsf -= 0x8000;
- ath5k_hw_set_mcast_filter(ah, 0, 0);
- DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt);
+ return (tsf & ~0x7fff) | rstamp;
}
-/*
- * Enable the receive h/w following a reset.
- */
-static int ath5k_startrecv(struct ath5k_softc *sc)
-{
- struct ath5k_hw *ah = sc->ah;
- struct ath5k_buf *bf;
- int ret;
-
- sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz);
-
- DPRINTF(sc, ATH_DEBUG_RESET, "%s: cachelsz %u rxbufsize %u\n",
- __func__, sc->cachelsz, sc->rxbufsize);
-
- sc->rxlink = NULL;
-
- spin_lock_bh(&sc->rxbuflock);
- list_for_each_entry(bf, &sc->rxbuf, list) {
- ret = ath5k_rxbuf_init(sc, bf);
- if (ret != 0) {
- spin_unlock_bh(&sc->rxbuflock);
- goto err;
- }
- }
- bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
- spin_unlock_bh(&sc->rxbuflock);
-
- ath5k_hw_put_rx_buf(ah, bf->daddr);
- ath5k_hw_start_rx(ah); /* enable recv descriptors */
- ath5k_mode_init(sc); /* set filters, etc. */
- ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */
- return 0;
-err:
- return ret;
-}
-static inline void ath5k_update_txpow(struct ath5k_softc *sc)
-{
- ath5k_hw_set_txpower_limit(sc->ah, 0);
-}
-static int ath5k_stop_locked(struct ath5k_softc *);
+/********************\
+* Interrupt handling *
+\********************/
-static int ath5k_init(struct ath5k_softc *sc)
+static int
+ath5k_init(struct ath5k_softc *sc)
{
int ret;
@@ -842,99 +2211,8 @@ done:
return ret;
}
-/*
- * Disable the receive h/w in preparation for a reset.
- */
-static void ath5k_stoprecv(struct ath5k_softc *sc)
-{
- struct ath5k_hw *ah = sc->ah;
-
- ath5k_hw_stop_pcu_recv(ah); /* disable PCU */
- ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */
- ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */
- mdelay(3); /* 3ms is long enough for 1 frame */
-#if AR_DEBUG
- if (unlikely(sc->debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL))) {
- struct ath5k_desc *ds;
- struct ath5k_buf *bf;
- int status;
-
- printk(KERN_DEBUG "%s: rx queue %x, link %p\n", __func__,
- ath5k_hw_get_rx_buf(ah), sc->rxlink);
-
- spin_lock_bh(&sc->rxbuflock);
- list_for_each_entry(bf, &sc->rxbuf, list) {
- ds = bf->desc;
- status = ah->ah_proc_rx_desc(ah, ds);
- if (!status || (sc->debug & ATH_DEBUG_FATAL))
- ath5k_printrxbuf(bf, status == 0);
- }
- spin_unlock_bh(&sc->rxbuflock);
- }
-#endif
- sc->rxlink = NULL; /* just in case */
-}
-
-static void ath5k_tx_draintxq(struct ath5k_softc *sc, struct ath5k_txq *txq)
-{
- struct ath5k_buf *bf, *bf0;
-
- /*
- * NB: this assumes output has been stopped and
- * we do not need to block ath5k_tx_tasklet
- */
- spin_lock_bh(&txq->lock);
- list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-#if AR_DEBUG
- if (sc->debug & ATH_DEBUG_RESET)
- ath5k_printtxbuf(bf, !sc->ah->ah_proc_tx_desc(sc->ah,
- bf->desc));
-#endif
- ath5k_cleanup_txbuf(sc, bf);
-
- spin_lock_bh(&sc->txbuflock);
- sc->tx_stats.data[txq->qnum].len--;
- list_move_tail(&bf->list, &sc->txbuf);
- sc->txbuf_len++;
- spin_unlock_bh(&sc->txbuflock);
- }
- txq->link = NULL;
- spin_unlock_bh(&txq->lock);
-}
-
-/*
- * Drain the transmit queues and reclaim resources.
- */
-static void ath5k_draintxq(struct ath5k_softc *sc)
-{
- struct ath5k_hw *ah = sc->ah;
- int i;
-
- /* XXX return value */
- if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
- /* don't touch the hardware if marked invalid */
- (void)ath5k_hw_stop_tx_dma(ah, sc->bhalq);
- DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__,
- ath5k_hw_get_tx_buf(ah, sc->bhalq));
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
- if (sc->txqs[i].setup) {
- ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
- DPRINTF(sc, ATH_DEBUG_RESET, "%s: txq [%u] %x, "
- "link %p\n", __func__,
- sc->txqs[i].qnum,
- ath5k_hw_get_tx_buf(ah,
- sc->txqs[i].qnum),
- sc->txqs[i].link);
- }
- }
- ieee80211_start_queues(sc->hw); /* XXX move to callers */
-
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
- if (sc->txqs[i].setup)
- ath5k_tx_draintxq(sc, &sc->txqs[i]);
-}
-
-static int ath5k_stop_locked(struct ath5k_softc *sc)
+static int
+ath5k_stop_locked(struct ath5k_softc *sc)
{
struct ath5k_hw *ah = sc->ah;
@@ -982,7 +2260,8 @@ static int ath5k_stop_locked(struct ath5k_softc *sc)
* if another thread does a system call and the thread doing the
* stop is preempted).
*/
-static int ath5k_stop_hw(struct ath5k_softc *sc)
+static int
+ath5k_stop_hw(struct ath5k_softc *sc)
{
int ret;
@@ -1020,188 +2299,185 @@ static int ath5k_stop_hw(struct ath5k_softc *sc)
return ret;
}
-static void ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
+static irqreturn_t
+ath5k_intr(int irq, void *dev_id)
{
- if (unlikely(test_bit(ATH_STAT_LEDSOFT, sc->status))) {
- /* from Atheros NDIS driver, w/ permission */
- static const struct {
- u16 rate; /* tx/rx 802.11 rate */
- u16 timeOn; /* LED on time (ms) */
- u16 timeOff; /* LED off time (ms) */
- } blinkrates[] = {
- { 108, 40, 10 },
- { 96, 44, 11 },
- { 72, 50, 13 },
- { 48, 57, 14 },
- { 36, 67, 16 },
- { 24, 80, 20 },
- { 22, 100, 25 },
- { 18, 133, 34 },
- { 12, 160, 40 },
- { 10, 200, 50 },
- { 6, 240, 58 },
- { 4, 267, 66 },
- { 2, 400, 100 },
- { 0, 500, 130 }
- };
- const struct ath5k_rate_table *rt =
- ath5k_hw_get_rate_table(sc->ah, mode);
- unsigned int i, j;
+ struct ath5k_softc *sc = dev_id;
+ struct ath5k_hw *ah = sc->ah;
+ enum ath5k_int status;
+ unsigned int counter = 1000;
- BUG_ON(rt == NULL);
+ if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
+ !ath5k_hw_is_intr_pending(ah)))
+ return IRQ_NONE;
- memset(sc->hwmap, 0, sizeof(sc->hwmap));
- for (i = 0; i < 32; i++) {
- u8 ix = rt->rate_code_to_index[i];
- if (ix == 0xff) {
- sc->hwmap[i].ledon = msecs_to_jiffies(500);
- sc->hwmap[i].ledoff = msecs_to_jiffies(130);
- continue;
+ do {
+ /*
+ * Figure out the reason(s) for the interrupt. Note
+ * that get_isr returns a pseudo-ISR that may include
+ * bits we haven't explicitly enabled so we mask the
+ * value to insure we only process bits we requested.
+ */
+ ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
+ DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x/0x%x\n", __func__,
+ status, sc->imask);
+ status &= sc->imask; /* discard unasked for bits */
+ if (unlikely(status & AR5K_INT_FATAL)) {
+ /*
+ * Fatal errors are unrecoverable.
+ * Typically these are caused by DMA errors.
+ */
+ tasklet_schedule(&sc->restq);
+ } else if (unlikely(status & AR5K_INT_RXORN)) {
+ tasklet_schedule(&sc->restq);
+ } else {
+ if (status & AR5K_INT_SWBA) {
+ /*
+ * Software beacon alert--time to send a beacon.
+ * Handle beacon transmission directly; deferring
+ * this is too slow to meet timing constraints
+ * under load.
+ */
+ ath5k_beacon_send(sc);
+ }
+ if (status & AR5K_INT_RXEOL) {
+ /*
+ * NB: the hardware should re-read the link when
+ * RXE bit is written, but it doesn't work at
+ * least on older hardware revs.
+ */
+ sc->rxlink = NULL;
+ }
+ if (status & AR5K_INT_TXURN) {
+ /* bump tx trigger level */
+ ath5k_hw_update_tx_triglevel(ah, true);
+ }
+ if (status & AR5K_INT_RX)
+ tasklet_schedule(&sc->rxtq);
+ if (status & AR5K_INT_TX)
+ tasklet_schedule(&sc->txtq);
+ if (status & AR5K_INT_BMISS) {
+ }
+ if (status & AR5K_INT_MIB) {
+ /* TODO */
}
- sc->hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
- if (SHPREAMBLE_FLAG(ix) || rt->rates[ix].modulation ==
- IEEE80211_RATE_OFDM)
- sc->hwmap[i].txflags |=
- IEEE80211_RADIOTAP_F_SHORTPRE;
- /* receive frames include FCS */
- sc->hwmap[i].rxflags = sc->hwmap[i].txflags |
- IEEE80211_RADIOTAP_F_FCS;
- /* setup blink rate table to avoid per-packet lookup */
- for (j = 0; j < ARRAY_SIZE(blinkrates) - 1; j++)
- if (blinkrates[j].rate == /* XXX why 7f? */
- (rt->rates[ix].dot11_rate&0x7f))
- break;
-
- sc->hwmap[i].ledon = msecs_to_jiffies(blinkrates[j].
- timeOn);
- sc->hwmap[i].ledoff = msecs_to_jiffies(blinkrates[j].
- timeOff);
}
- }
+ } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0);
- sc->curmode = mode;
+ if (unlikely(!counter && net_ratelimit()))
+ printk(KERN_WARNING "ath: too many interrupts, giving up for "
+ "now\n");
+
+ return IRQ_HANDLED;
}
-/*
- * Set/change channels. If the channel is really being changed,
- * it's done by reseting the chip. To accomplish this we must
- * first cleanup any pending DMA, then restart stuff after a la
- * ath5k_init.
- */
-static int ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+static void
+ath5k_tasklet_reset(unsigned long data)
{
- struct ath5k_hw *ah = sc->ah;
- int ret;
+ struct ath5k_softc *sc = (void *)data;
- DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n",
- __func__, sc->curchan->chan, sc->curchan->freq,
- chan->chan, chan->freq);
+ ath5k_reset(sc->hw);
+}
- if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) {
- /*
- * To switch channels clear any pending DMA operations;
- * wait long enough for the RX fifo to drain, reset the
- * hardware at the new frequency, and then re-enable
- * the relevant bits of the h/w.
- */
- ath5k_hw_set_intr(ah, 0); /* disable interrupts */
- ath5k_draintxq(sc); /* clear pending tx frames */
- ath5k_stoprecv(sc); /* turn off frame recv */
- ret = ath5k_hw_reset(ah, sc->opmode, chan, true);
- if (ret) {
- printk(KERN_ERR "%s: unable to reset channel %u "
- "(%u Mhz)\n", __func__, chan->chan, chan->freq);
- return ret;
- }
- sc->curchan = chan;
- ath5k_update_txpow(sc);
+static inline void
+ath5k_update_txpow(struct ath5k_softc *sc)
+{
+ ath5k_hw_set_txpower_limit(sc->ah, 0);
+}
- /*
- * Re-enable rx framework.
- */
- ret = ath5k_startrecv(sc);
- if (ret) {
- printk(KERN_ERR "%s: unable to restart recv logic\n",
- __func__);
- return ret;
- }
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath5k_calibrate(unsigned long data)
+{
+ struct ath5k_softc *sc = (void *)data;
+ struct ath5k_hw *ah = sc->ah;
- /*
- * Change channels and update the h/w rate map
- * if we're switching; e.g. 11a to 11b/g.
- *
- * XXX needed?
- */
-/* ath5k_chan_change(sc, chan); */
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "ath: channel %u/%x\n",
+ sc->curchan->chan, sc->curchan->val);
+ if (ath5k_hw_get_rf_gain(ah) == AR5K_RFGAIN_NEED_CHANGE) {
/*
- * Re-enable interrupts.
+ * Rfgain is out of bounds, reset the chip
+ * to load new gain values.
*/
- ath5k_hw_set_intr(ah, sc->imask);
+ DPRINTF(sc, ATH_DEBUG_RESET, "calibration, resetting\n");
+ ath5k_reset(sc->hw);
}
+ if (ath5k_hw_phy_calibrate(ah, sc->curchan))
+ printk(KERN_ERR "ath: calibration of channel %u failed\n",
+ sc->curchan->chan);
- return 0;
+ mod_timer(&sc->calib_tim, round_jiffies(jiffies +
+ msecs_to_jiffies(ath5k_calinterval * 1000)));
}
-static int ath5k_tx_bf(struct ath5k_softc *sc, struct ath5k_buf *bf,
- struct ieee80211_tx_control *ctl)
-{
- struct ath5k_hw *ah = sc->ah;
- struct ath5k_txq *txq = sc->txq;
- struct ath5k_desc *ds = bf->desc;
- struct sk_buff *skb = bf->skb;
- unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
- int ret;
- flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
- bf->ctl = *ctl;
- /* XXX endianness */
- bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
- if (ctl->flags & IEEE80211_TXCTL_NO_ACK)
- flags |= AR5K_TXDESC_NOACK;
+/***************\
+* LED functions *
+\***************/
- pktlen = skb->len + FCS_LEN;
+static void
+ath5k_led_off(unsigned long data)
+{
+ struct ath5k_softc *sc = (void *)data;
- if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) {
- keyidx = ctl->key_idx;
- pktlen += ctl->icv_len;
+ if (test_bit(ATH_STAT_LEDENDBLINK, sc->status))
+ __clear_bit(ATH_STAT_LEDBLINKING, sc->status);
+ else {
+ __set_bit(ATH_STAT_LEDENDBLINK, sc->status);
+ ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on);
+ mod_timer(&sc->led_tim, jiffies + sc->led_off);
}
+}
- ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
- ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
- 0xffff, ctl->tx_rate, ctl->retry_limit, keyidx, 0, flags, 0, 0);
- if (ret)
- goto err_unmap;
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+ath5k_led_blink(struct ath5k_softc *sc, unsigned int on,
+ unsigned int off)
+{
+ DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
+ ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
+ __set_bit(ATH_STAT_LEDBLINKING, sc->status);
+ __clear_bit(ATH_STAT_LEDENDBLINK, sc->status);
+ sc->led_off = off;
+ mod_timer(&sc->led_tim, jiffies + on);
+}
- ds->ds_link = 0;
- ds->ds_data = bf->skbaddr;
+static void
+ath5k_led_event(struct ath5k_softc *sc, int event)
+{
+ if (likely(!test_bit(ATH_STAT_LEDSOFT, sc->status)))
+ return;
+ if (unlikely(test_bit(ATH_STAT_LEDBLINKING, sc->status)))
+ return; /* don't interrupt active blink */
+ switch (event) {
+ case ATH_LED_TX:
+ ath5k_led_blink(sc, sc->hwmap[sc->led_txrate].ledon,
+ sc->hwmap[sc->led_txrate].ledoff);
+ break;
+ case ATH_LED_RX:
+ ath5k_led_blink(sc, sc->hwmap[sc->led_rxrate].ledon,
+ sc->hwmap[sc->led_rxrate].ledoff);
+ break;
+ }
+}
- ret = ah->ah_fill_tx_desc(ah, ds, skb->len, true, true);
- if (ret)
- goto err_unmap;
- spin_lock_bh(&txq->lock);
- list_add_tail(&bf->list, &txq->q);
- sc->tx_stats.data[txq->qnum].len++;
- if (txq->link == NULL) /* is this first packet? */
- ath5k_hw_put_tx_buf(ah, txq->qnum, bf->daddr);
- else /* no, so only link it */
- *txq->link = bf->daddr;
- txq->link = &ds->ds_link;
- ath5k_hw_tx_start(ah, txq->qnum);
- spin_unlock_bh(&txq->lock);
- return 0;
-err_unmap:
- pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
- return ret;
-}
+/********************\
+* Mac80211 functions *
+\********************/
-static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_tx_control *ctl)
+static int
+ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_tx_control *ctl)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_buf *bf;
@@ -1265,7 +2541,8 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
return 0;
}
-static int ath5k_reset(struct ieee80211_hw *hw)
+static int
+ath5k_reset(struct ieee80211_hw *hw)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
@@ -1312,18 +2589,21 @@ err:
return ret;
}
-static int ath5k_start(struct ieee80211_hw *hw)
+static int
+ath5k_start(struct ieee80211_hw *hw)
{
return ath5k_init(hw->priv);
}
-void ath5k_stop(struct ieee80211_hw *hw)
+static void
+ath5k_stop(struct ieee80211_hw *hw)
{
ath5k_stop_hw(hw->priv);
}
-static int ath5k_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_if_init_conf *conf)
+static int
+ath5k_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
{
struct ath5k_softc *sc = hw->priv;
int ret;
@@ -1352,8 +2632,9 @@ end:
return ret;
}
-static void ath5k_remove_interface(struct ieee80211_hw *hw,
- struct ieee80211_if_init_conf *conf)
+static void
+ath5k_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
{
struct ath5k_softc *sc = hw->priv;
@@ -1366,7 +2647,9 @@ end:
mutex_unlock(&sc->lock);
}
-static int ath5k_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+static int
+ath5k_config(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf)
{
struct ath5k_softc *sc = hw->priv;
@@ -1376,8 +2659,9 @@ static int ath5k_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
return ath5k_chan_set(sc, conf->chan);
}
-static int ath5k_config_interface(struct ieee80211_hw *hw, int if_id,
- struct ieee80211_if_conf *conf)
+static int
+ath5k_config_interface(struct ieee80211_hw *hw, int if_id,
+ struct ieee80211_if_conf *conf)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
@@ -1428,10 +2712,11 @@ unlock:
* the station is otherwise quiet, or
* - when scanning
*/
-static void ath5k_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *new_flags,
- int mc_count, struct dev_mc_list *mclist)
+static void
+ath5k_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ int mc_count, struct dev_mc_list *mclist)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
@@ -1527,7 +2812,8 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
sc->filter_flags = rfilt;
}
-static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+static int
+ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
const u8 *local_addr, const u8 *addr,
struct ieee80211_key_conf *key)
{
@@ -1571,7 +2857,8 @@ unlock:
return ret;
}
-static int ath5k_get_stats(struct ieee80211_hw *hw,
+static int
+ath5k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
struct ath5k_softc *sc = hw->priv;
@@ -1581,7 +2868,8 @@ static int ath5k_get_stats(struct ieee80211_hw *hw,
return 0;
}
-static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
+static int
+ath5k_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct ath5k_softc *sc = hw->priv;
@@ -1591,22 +2879,25 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
return 0;
}
-static u64 ath5k_get_tsf(struct ieee80211_hw *hw)
+static u64
+ath5k_get_tsf(struct ieee80211_hw *hw)
{
struct ath5k_softc *sc = hw->priv;
return ath5k_hw_get_tsf64(sc->ah);
}
-static void ath5k_reset_tsf(struct ieee80211_hw *hw)
+static void
+ath5k_reset_tsf(struct ieee80211_hw *hw)
{
struct ath5k_softc *sc = hw->priv;
ath5k_hw_reset_tsf(sc->ah);
}
-static int ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_tx_control *ctl)
+static int
+ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_tx_control *ctl)
{
struct ath5k_softc *sc = hw->priv;
int ret;
@@ -1631,1001 +2922,3 @@ end:
return ret;
}
-static struct ieee80211_ops ath5k_hw_ops = {
- .tx = ath5k_tx,
- .start = ath5k_start,
- .stop = ath5k_stop,
- .add_interface = ath5k_add_interface,
- .remove_interface = ath5k_remove_interface,
- .config = ath5k_config,
- .config_interface = ath5k_config_interface,
- .configure_filter = ath5k_configure_filter,
- .set_key = ath5k_set_key,
- .get_stats = ath5k_get_stats,
- .conf_tx = NULL,
- .get_tx_stats = ath5k_get_tx_stats,
- .get_tsf = ath5k_get_tsf,
- .reset_tsf = ath5k_reset_tsf,
- .beacon_update = ath5k_beacon_update,
-};
-
-/*
- * Periodically recalibrate the PHY to account
- * for temperature/environment changes.
- */
-static void ath5k_calibrate(unsigned long data)
-{
- struct ath5k_softc *sc = (void *)data;
- struct ath5k_hw *ah = sc->ah;
-
- DPRINTF(sc, ATH_DEBUG_CALIBRATE, "ath: channel %u/%x\n",
- sc->curchan->chan, sc->curchan->val);
-
- if (ath5k_hw_get_rf_gain(ah) == AR5K_RFGAIN_NEED_CHANGE) {
- /*
- * Rfgain is out of bounds, reset the chip
- * to load new gain values.
- */
- DPRINTF(sc, ATH_DEBUG_RESET, "calibration, resetting\n");
- ath5k_reset(sc->hw);
- }
- if (ath5k_hw_phy_calibrate(ah, sc->curchan))
- printk(KERN_ERR "ath: calibration of channel %u failed\n",
- sc->curchan->chan);
-
- mod_timer(&sc->calib_tim, round_jiffies(jiffies +
- msecs_to_jiffies(ath5k_calinterval * 1000)));
-}
-
-static void ath5k_led_off(unsigned long data)
-{
- struct ath5k_softc *sc = (void *)data;
-
- if (test_bit(ATH_STAT_LEDENDBLINK, sc->status))
- __clear_bit(ATH_STAT_LEDBLINKING, sc->status);
- else {
- __set_bit(ATH_STAT_LEDENDBLINK, sc->status);
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on);
- mod_timer(&sc->led_tim, jiffies + sc->led_off);
- }
-}
-
-/*
- * Blink the LED according to the specified on/off times.
- */
-static void ath5k_led_blink(struct ath5k_softc *sc, unsigned int on,
- unsigned int off)
-{
- DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
- __set_bit(ATH_STAT_LEDBLINKING, sc->status);
- __clear_bit(ATH_STAT_LEDENDBLINK, sc->status);
- sc->led_off = off;
- mod_timer(&sc->led_tim, jiffies + on);
-}
-
-static void ath5k_led_event(struct ath5k_softc *sc, int event)
-{
- if (likely(!test_bit(ATH_STAT_LEDSOFT, sc->status)))
- return;
- if (unlikely(test_bit(ATH_STAT_LEDBLINKING, sc->status)))
- return; /* don't interrupt active blink */
- switch (event) {
- case ATH_LED_TX:
- ath5k_led_blink(sc, sc->hwmap[sc->led_txrate].ledon,
- sc->hwmap[sc->led_txrate].ledoff);
- break;
- case ATH_LED_RX:
- ath5k_led_blink(sc, sc->hwmap[sc->led_rxrate].ledon,
- sc->hwmap[sc->led_rxrate].ledoff);
- break;
- }
-}
-
-static irqreturn_t ath5k_intr(int irq, void *dev_id)
-{
- struct ath5k_softc *sc = dev_id;
- struct ath5k_hw *ah = sc->ah;
- enum ath5k_int status;
- unsigned int counter = 1000;
-
- if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
- !ath5k_hw_is_intr_pending(ah)))
- return IRQ_NONE;
-
- do {
- /*
- * Figure out the reason(s) for the interrupt. Note
- * that get_isr returns a pseudo-ISR that may include
- * bits we haven't explicitly enabled so we mask the
- * value to insure we only process bits we requested.
- */
- ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */
- DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x/0x%x\n", __func__,
- status, sc->imask);
- status &= sc->imask; /* discard unasked for bits */
- if (unlikely(status & AR5K_INT_FATAL)) {
- /*
- * Fatal errors are unrecoverable.
- * Typically these are caused by DMA errors.
- */
- tasklet_schedule(&sc->restq);
- } else if (unlikely(status & AR5K_INT_RXORN)) {
- tasklet_schedule(&sc->restq);
- } else {
- if (status & AR5K_INT_SWBA) {
- /*
- * Software beacon alert--time to send a beacon.
- * Handle beacon transmission directly; deferring
- * this is too slow to meet timing constraints
- * under load.
- */
- ath5k_beacon_send(sc);
- }
- if (status & AR5K_INT_RXEOL) {
- /*
- * NB: the hardware should re-read the link when
- * RXE bit is written, but it doesn't work at
- * least on older hardware revs.
- */
- sc->rxlink = NULL;
- }
- if (status & AR5K_INT_TXURN) {
- /* bump tx trigger level */
- ath5k_hw_update_tx_triglevel(ah, true);
- }
- if (status & AR5K_INT_RX)
- tasklet_schedule(&sc->rxtq);
- if (status & AR5K_INT_TX)
- tasklet_schedule(&sc->txtq);
- if (status & AR5K_INT_BMISS) {
- }
- if (status & AR5K_INT_MIB) {
- /* TODO */
- }
- }
- } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0);
-
- if (unlikely(!counter && net_ratelimit()))
- printk(KERN_WARNING "ath: too many interrupts, giving up for "
- "now\n");
-
- return IRQ_HANDLED;
-}
-
-/*
- * Convert IEEE channel number to MHz frequency.
- */
-static inline short ath5k_ieee2mhz(short chan)
-{
- if (chan <= 14 || chan >= 27)
- return ieee80211chan2mhz(chan);
- else
- return 2212 + chan * 20;
-}
-
-static unsigned int ath5k_copy_rates(struct ieee80211_rate *rates,
- const struct ath5k_rate_table *rt, unsigned int max)
-{
- unsigned int i, count;
-
- if (rt == NULL)
- return 0;
-
- for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) {
- if (!rt->rates[i].valid)
- continue;
- rates->rate = rt->rates[i].rate_kbps / 100;
- rates->val = rt->rates[i].rate_code;
- rates->flags = rt->rates[i].modulation;
- rates++;
- count++;
- max--;
- }
-
- return count;
-}
-
-static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
- struct ieee80211_channel *channels, unsigned int mode,
- unsigned int max)
-{
- static const struct { unsigned int mode, mask, chan; } map[] = {
- [MODE_IEEE80211A] = { CHANNEL_OFDM, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_A },
- [MODE_ATHEROS_TURBO] = { CHANNEL_OFDM|CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_T },
- [MODE_IEEE80211B] = { CHANNEL_CCK, CHANNEL_CCK, CHANNEL_B },
- [MODE_IEEE80211G] = { CHANNEL_OFDM, CHANNEL_OFDM, CHANNEL_G },
- [MODE_ATHEROS_TURBOG] = { CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_TG },
- };
- static const struct ath5k_regchannel chans_2ghz[] =
- IEEE80211_CHANNELS_2GHZ;
- static const struct ath5k_regchannel chans_5ghz[] =
- IEEE80211_CHANNELS_5GHZ;
- const struct ath5k_regchannel *chans;
- enum ath5k_regdom dmn;
- unsigned int i, count, size, chfreq, all, f, ch;
-
- if (!test_bit(mode, ah->ah_modes))
- return 0;
-
- all = ah->ah_regdomain == DMN_DEFAULT || CHAN_DEBUG == 1;
-
- switch (mode) {
- case MODE_IEEE80211A:
- case MODE_ATHEROS_TURBO:
- /* 1..220, but 2GHz frequencies are filtered by check_channel */
- size = all ? 220 : ARRAY_SIZE(chans_5ghz);
- chans = chans_5ghz;
- dmn = ath5k_regdom2flag(ah->ah_regdomain,
- IEEE80211_CHANNELS_5GHZ_MIN);
- chfreq = CHANNEL_5GHZ;
- break;
- case MODE_IEEE80211B:
- case MODE_IEEE80211G:
- case MODE_ATHEROS_TURBOG:
- size = all ? 26 : ARRAY_SIZE(chans_2ghz);
- chans = chans_2ghz;
- dmn = ath5k_regdom2flag(ah->ah_regdomain,
- IEEE80211_CHANNELS_2GHZ_MIN);
- chfreq = CHANNEL_2GHZ;
- break;
- default:
- printk(KERN_WARNING "bad mode, not copying channels\n");
- return 0;
- }
-
- for (i = 0, count = 0; i < size && max > 0; i++) {
- ch = all ? i + 1 : chans[i].chan;
- f = ath5k_ieee2mhz(ch);
- /* Check if channel is supported by the chipset */
- if (!ath5k_channel_ok(ah, f, chfreq))
- continue;
-
- /* Match regulation domain */
- if (!all && !(IEEE80211_DMN(chans[i].domain) &
- IEEE80211_DMN(dmn)))
- continue;
-
- if (!all && (chans[i].mode & map[mode].mask) != map[mode].mode)
- continue;
-
- /* Write channel and increment counter */
- channels->chan = ch;
- channels->freq = f;
- channels->val = map[mode].chan;
- channels++;
- count++;
- max--;
- }
-
- return count;
-}
-
-#if ATH_DEBUG_MODES
-static void ath5k_dump_modes(struct ieee80211_hw_mode *modes)
-{
- unsigned int m, i;
-
- for (m = 0; m < NUM_DRIVER_MODES; m++) {
- printk(KERN_DEBUG "Mode %u: channels %d, rates %d\n", m,
- modes[m].num_channels, modes[m].num_rates);
- printk(KERN_DEBUG " channels:\n");
- for (i = 0; i < modes[m].num_channels; i++)
- printk(KERN_DEBUG " %3d %d %.4x %.4x\n",
- modes[m].channels[i].chan,
- modes[m].channels[i].freq,
- modes[m].channels[i].val,
- modes[m].channels[i].flag);
- printk(KERN_DEBUG " rates:\n");
- for (i = 0; i < modes[m].num_rates; i++)
- printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
- modes[m].rates[i].rate,
- modes[m].rates[i].val,
- modes[m].rates[i].flags,
- modes[m].rates[i].val2);
- }
-}
-#else
-static inline void ath5k_dump_modes(struct ieee80211_hw_mode *modes) {}
-#endif
-
-static inline int ath5k_register_mode(struct ieee80211_hw *hw, u8 m)
-{
- struct ath5k_softc *sc = hw->priv;
- struct ieee80211_hw_mode *modes = sc->modes;
- int i, ret;
-
- for (i = 0; i < NUM_DRIVER_MODES; i++) {
- if (modes[i].mode != m || !modes[i].num_channels)
- continue;
- ret = ieee80211_register_hwmode(hw, &modes[i]);
- if (ret) {
- printk(KERN_ERR "can't register hwmode %u\n", m);
- return ret;
- }
- return 0;
- }
- return 1;
-}
-
-/* Only tries to register modes our EEPROM says it can support */
-#define REGISTER_MODE(m) do { \
- if (test_bit(m, ah->ah_capabilities.cap_mode)) { \
- ret = ath5k_register_mode(hw, m); \
- if (ret) \
- return ret; \
- } \
-} while (0) \
-
-static int ath5k_getchannels(struct ieee80211_hw *hw)
-{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
- struct ieee80211_hw_mode *modes = sc->modes;
- unsigned int i, max_r, max_c;
- int ret;
-
- BUILD_BUG_ON(ARRAY_SIZE(sc->modes) < 3);
-
- /* The order here does not matter */
- modes[0].mode = MODE_IEEE80211G;
- modes[1].mode = MODE_IEEE80211B;
- modes[2].mode = MODE_IEEE80211A;
-
- max_r = ARRAY_SIZE(sc->rates);
- max_c = ARRAY_SIZE(sc->channels);
-
- for (i = 0; i < NUM_DRIVER_MODES; i++) {
- struct ieee80211_hw_mode *mode = &modes[i];
- const struct ath5k_rate_table *hw_rates;
-
- if (i == 0) {
- modes[0].rates = sc->rates;
- modes->channels = sc->channels;
- } else {
- struct ieee80211_hw_mode *prev_mode = &modes[i-1];
- int prev_num_r = prev_mode->num_rates;
- int prev_num_c = prev_mode->num_channels;
- mode->rates = &prev_mode->rates[prev_num_r];
- mode->channels = &prev_mode->channels[prev_num_c];
- }
-
- hw_rates = ath5k_hw_get_rate_table(ah, mode->mode);
- if (!hw_rates)
- return -EINVAL;
-
- mode->num_rates = ath5k_copy_rates(mode->rates, hw_rates,
- max_r);
- mode->num_channels = ath5k_copy_channels(ah, mode->channels,
- mode->mode, max_c);
- max_r -= mode->num_rates;
- max_c -= mode->num_channels;
- }
-
- /* We try to register all modes this driver supports. We don't bother
- * with MODE_IEEE80211B for AR5212 as MODE_IEEE80211G already accounts
- * for that as per mac80211. Then, REGISTER_MODE() will will actually
- * check the eeprom reading for more reliable capability information.
- * Order matters here as per mac80211's latest preference. This will
- * all hopefullly soon go away. */
-
- REGISTER_MODE(MODE_IEEE80211G);
- if (ah->ah_version != AR5K_AR5212)
- REGISTER_MODE(MODE_IEEE80211B);
- REGISTER_MODE(MODE_IEEE80211A);
-
- ath5k_dump_modes(modes);
-
- return ret;
-}
-
-static int ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
-{
- struct ath5k_desc *ds;
- struct ath5k_buf *bf;
- dma_addr_t da;
- unsigned int i;
- int ret;
-
- /* allocate descriptors */
- sc->desc_len = sizeof(struct ath5k_desc) *
- (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
- sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
- if (sc->desc == NULL) {
- dev_err(&pdev->dev, "can't allocate descriptors\n");
- ret = -ENOMEM;
- goto err;
- }
- ds = sc->desc;
- da = sc->desc_daddr;
- DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%zu) -> %llx\n",
- __func__, ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
-
- bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
- sizeof(struct ath5k_buf), GFP_KERNEL);
- if (bf == NULL) {
- dev_err(&pdev->dev, "can't allocate bufptr\n");
- ret = -ENOMEM;
- goto err_free;
- }
- sc->bufptr = bf;
-
- INIT_LIST_HEAD(&sc->rxbuf);
- for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
- bf->desc = ds;
- bf->daddr = da;
- list_add_tail(&bf->list, &sc->rxbuf);
- }
-
- INIT_LIST_HEAD(&sc->txbuf);
- sc->txbuf_len = ATH_TXBUF;
- for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
- da += sizeof(*ds)) {
- bf->desc = ds;
- bf->daddr = da;
- list_add_tail(&bf->list, &sc->txbuf);
- }
-
- /* beacon buffer */
- bf->desc = ds;
- bf->daddr = da;
- sc->bbuf = bf;
-
- return 0;
-err_free:
- pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-err:
- sc->desc = NULL;
- return ret;
-}
-
-static void ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
-{
- struct ath5k_buf *bf;
-
- ath5k_cleanup_txbuf(sc, sc->bbuf);
- list_for_each_entry(bf, &sc->txbuf, list)
- ath5k_cleanup_txbuf(sc, bf);
- list_for_each_entry(bf, &sc->rxbuf, list)
- ath5k_cleanup_txbuf(sc, bf);
-
- /* Free memory associated with all descriptors */
- pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-
- kfree(sc->bufptr);
- sc->bufptr = NULL;
-}
-
-static int ath5k_beaconq_setup(struct ath5k_hw *ah)
-{
- struct ath5k_txq_info qi = {
- .tqi_aifs = AR5K_TXQ_USEDEFAULT,
- .tqi_cw_min = AR5K_TXQ_USEDEFAULT,
- .tqi_cw_max = AR5K_TXQ_USEDEFAULT,
- /* NB: for dynamic turbo, don't enable any other interrupts */
- .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
- };
-
- return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
-}
-
-static struct ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc, int qtype,
- int subtype)
-{
- struct ath5k_hw *ah = sc->ah;
- struct ath5k_txq *txq;
- struct ath5k_txq_info qi = {
- .tqi_subtype = subtype,
- .tqi_aifs = AR5K_TXQ_USEDEFAULT,
- .tqi_cw_min = AR5K_TXQ_USEDEFAULT,
- .tqi_cw_max = AR5K_TXQ_USEDEFAULT
- };
- int qnum;
-
- /*
- * Enable interrupts only for EOL and DESC conditions.
- * We mark tx descriptors to receive a DESC interrupt
- * when a tx queue gets deep; otherwise waiting for the
- * EOL to reap descriptors. Note that this is done to
- * reduce interrupt load and this only defers reaping
- * descriptors, never transmitting frames. Aside from
- * reducing interrupts this also permits more concurrency.
- * The only potential downside is if the tx queue backs
- * up in which case the top half of the kernel may backup
- * due to a lack of tx descriptors.
- */
- qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
- AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
- qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
- if (qnum < 0) {
- /*
- * NB: don't print a message, this happens
- * normally on parts with too few tx queues
- */
- return ERR_PTR(qnum);
- }
- if (qnum >= ARRAY_SIZE(sc->txqs)) {
- printk(KERN_ERR "hw qnum %u out of range, max %tu!\n",
- qnum, ARRAY_SIZE(sc->txqs));
- ath5k_hw_release_tx_queue(ah, qnum);
- return ERR_PTR(-EINVAL);
- }
- txq = &sc->txqs[qnum];
- if (!txq->setup) {
- txq->qnum = qnum;
- txq->link = NULL;
- INIT_LIST_HEAD(&txq->q);
- spin_lock_init(&txq->lock);
- txq->setup = true;
- }
- return &sc->txqs[qnum];
-}
-
-static void ath5k_tx_cleanup(struct ath5k_softc *sc)
-{
- struct ath5k_txq *txq = sc->txqs;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
- if (txq->setup) {
- ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
- txq->setup = false;
- }
-}
-
-static int ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
-{
- struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
- u8 mac[ETH_ALEN];
- unsigned int i;
- int ret;
-
- DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, pdev->device);
-
- /*
- * Check if the MAC has multi-rate retry support.
- * We do this by trying to setup a fake extended
- * descriptor. MAC's that don't have support will
- * return false w/o doing anything. MAC's that do
- * support it will return true w/o doing anything.
- */
- if (ah->ah_setup_xtx_desc(ah, NULL, 0, 0, 0, 0, 0, 0))
- __set_bit(ATH_STAT_MRRETRY, sc->status);
-
- /*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up.
- */
- for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
- ath5k_hw_reset_key(ah, i);
-
- /*
- * Collect the channel list. The 802.11 layer
- * is resposible for filtering this list based
- * on settings like the phy mode and regulatory
- * domain restrictions.
- */
- ret = ath5k_getchannels(hw);
- if (ret) {
- dev_err(&pdev->dev, "can't get channels\n");
- goto err;
- }
-
- /* NB: setup here so ath5k_rate_update is happy */
- if (test_bit(MODE_IEEE80211A, ah->ah_modes))
- ath5k_setcurmode(sc, MODE_IEEE80211A);
- else
- ath5k_setcurmode(sc, MODE_IEEE80211B);
-
- /*
- * Allocate tx+rx descriptors and populate the lists.
- */
- ret = ath5k_desc_alloc(sc, pdev);
- if (ret) {
- dev_err(&pdev->dev, "can't allocate descriptors\n");
- goto err;
- }
-
- /*
- * Allocate hardware transmit queues: one queue for
- * beacon frames and one data queue for each QoS
- * priority. Note that hw functions handle reseting
- * these queues at the needed time.
- */
- ret = ath5k_beaconq_setup(ah);
- if (ret < 0) {
- dev_err(&pdev->dev, "can't setup a beacon xmit queue\n");
- goto err_desc;
- }
- sc->bhalq = ret;
-
- sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
- if (IS_ERR(sc->txq)) {
- dev_err(&pdev->dev, "can't setup xmit queue\n");
- ret = PTR_ERR(sc->txq);
- goto err_bhal;
- }
-
- tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
- tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
- tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
- setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
- setup_timer(&sc->led_tim, ath5k_led_off, (unsigned long)sc);
-
- sc->led_on = 0; /* low true */
- /*
- * Auto-enable soft led processing for IBM cards and for
- * 5211 minipci cards. Users can also manually enable/disable
- * support with a sysctl.
- */
- if (pdev->device == PCI_DEVICE_ID_ATHEROS_AR5212_IBM ||
- pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) {
- __set_bit(ATH_STAT_LEDSOFT, sc->status);
- sc->led_pin = 0;
- }
- /* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */
- if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) {
- __set_bit(ATH_STAT_LEDSOFT, sc->status);
- sc->led_pin = 0;
- }
- if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
- ath5k_hw_set_gpio_output(ah, sc->led_pin);
- ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on);
- }
-
- ath5k_hw_get_lladdr(ah, mac);
- SET_IEEE80211_PERM_ADDR(hw, mac);
- /* All MAC address bits matter for ACKs */
- memset(sc->bssidmask, 0xff, ETH_ALEN);
- ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
-
- ret = ieee80211_register_hw(hw);
- if (ret) {
- dev_err(&pdev->dev, "can't register ieee80211 hw\n");
- goto err_queues;
- }
-
- return 0;
-err_queues:
- ath5k_tx_cleanup(sc);
-err_bhal:
- ath5k_hw_release_tx_queue(ah, sc->bhalq);
-err_desc:
- ath5k_desc_free(sc, pdev);
-err:
- return ret;
-}
-
-static void ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
-{
- struct ath5k_softc *sc = hw->priv;
-
- /*
- * NB: the order of these is important:
- * o call the 802.11 layer before detaching ath5k_hw to
- * insure callbacks into the driver to delete global
- * key cache entries can be handled
- * o reclaim the tx queue data structures after calling
- * the 802.11 layer as we'll get called back to reclaim
- * node state and potentially want to use them
- * o to cleanup the tx queues the hal is called, so detach
- * it last
- * XXX: ??? detach ath5k_hw ???
- * Other than that, it's straightforward...
- */
- ieee80211_unregister_hw(hw);
- ath5k_desc_free(sc, pdev);
- ath5k_tx_cleanup(sc);
- ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
-
- /*
- * NB: can't reclaim these until after ieee80211_ifdetach
- * returns because we'll get called back to reclaim node
- * state and potentially want to use them.
- */
-}
-
-static const char *ath5k_chip_name(u8 mac_version)
-{
- switch (mac_version) {
- case AR5K_AR5210:
- return "AR5210";
- case AR5K_AR5211:
- return "AR5211";
- case AR5K_AR5212:
- return "AR5212";
- }
- return "Unknown";
-}
-
-static int __devinit ath5k_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- void __iomem *mem;
- struct ath5k_softc *sc;
- struct ieee80211_hw *hw;
- int ret;
- u8 csz;
-
- ret = pci_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "can't enable device\n");
- goto err;
- }
-
- /* XXX 32-bit addressing only */
- ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
- if (ret) {
- dev_err(&pdev->dev, "32-bit DMA not available\n");
- goto err_dis;
- }
-
- /*
- * Cache line size is used to size and align various
- * structures used to communicate with the hardware.
- */
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
- if (csz == 0) {
- /*
- * Linux 2.4.18 (at least) writes the cache line size
- * register as a 16-bit wide register which is wrong.
- * We must have this setup properly for rx buffer
- * DMA to work so force a reasonable value here if it
- * comes up zero.
- */
- csz = L1_CACHE_BYTES / sizeof(u32);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
- }
- /*
- * The default setting of latency timer yields poor results,
- * set it to the value used by other systems. It may be worth
- * tweaking this setting more.
- */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
-
- pci_set_master(pdev);
-
- /*
- * Disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state.
- */
- pci_write_config_byte(pdev, 0x41, 0);
-
- ret = pci_request_region(pdev, 0, "ath");
- if (ret) {
- dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
- goto err_dis;
- }
-
- mem = pci_iomap(pdev, 0, 0);
- if (!mem) {
- dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
- ret = -EIO;
- goto err_reg;
- }
-
- hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
- if (hw == NULL) {
- dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
- ret = -ENOMEM;
- goto err_map;
- }
-
- SET_IEEE80211_DEV(hw, &pdev->dev);
- hw->flags = IEEE80211_HW_RX_INCLUDES_FCS;
- hw->extra_tx_headroom = 2;
- hw->channel_change_time = 5000;
- hw->max_rssi = 127; /* FIXME: get a real value for this. */
- sc = hw->priv;
- sc->hw = hw;
- sc->pdev = pdev;
-
- /*
- * Mark the device as detached to avoid processing
- * interrupts until setup is complete.
- */
-#if AR_DEBUG
- sc->debug = ath5k_debug;
-#endif
- __set_bit(ATH_STAT_INVALID, sc->status);
- sc->iobase = mem;
- sc->cachelsz = csz * sizeof(u32); /* convert to bytes */
- sc->opmode = IEEE80211_IF_TYPE_STA;
- mutex_init(&sc->lock);
- spin_lock_init(&sc->rxbuflock);
- spin_lock_init(&sc->txbuflock);
-
- pci_set_drvdata(pdev, hw);
-
- pci_enable_msi(pdev);
-
- ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
- if (ret) {
- dev_err(&pdev->dev, "request_irq failed\n");
- goto err_free;
- }
-
- sc->ah = ath5k_hw_attach(pdev->device, id->driver_data, sc, sc->iobase);
- if (IS_ERR(sc->ah)) {
- ret = PTR_ERR(sc->ah);
- goto err_irq;
- }
-
- ret = ath5k_attach(pdev, hw);
- if (ret)
- goto err_ah;
-
- dev_info(&pdev->dev, "%s chip found: mac %d.%d phy %d.%d\n",
- ath5k_chip_name(id->driver_data), sc->ah->ah_mac_version,
- sc->ah->ah_mac_revision, sc->ah->ah_phy_revision >> 4,
- sc->ah->ah_phy_revision & 0xf);
-
- /* ready to process interrupts */
- __clear_bit(ATH_STAT_INVALID, sc->status);
-
- return 0;
-err_ah:
- ath5k_hw_detach(sc->ah);
-err_irq:
- free_irq(pdev->irq, sc);
-err_free:
- pci_disable_msi(pdev);
- ieee80211_free_hw(hw);
-err_map:
- pci_iounmap(pdev, mem);
-err_reg:
- pci_release_region(pdev, 0);
-err_dis:
- pci_disable_device(pdev);
-err:
- return ret;
-}
-
-static void __devexit ath5k_pci_remove(struct pci_dev *pdev)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pdev);
- struct ath5k_softc *sc = hw->priv;
-
- ath5k_detach(pdev, hw);
- ath5k_hw_detach(sc->ah);
- free_irq(pdev->irq, sc);
- pci_disable_msi(pdev);
- pci_iounmap(pdev, sc->iobase);
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- ieee80211_free_hw(hw);
-}
-
-#ifdef CONFIG_PM
-static int ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pdev);
- struct ath5k_softc *sc = hw->priv;
-
- if (test_bit(ATH_STAT_LEDSOFT, sc->status))
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, 1);
-
- ath5k_stop_hw(sc);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
- return 0;
-}
-
-static int ath5k_pci_resume(struct pci_dev *pdev)
-{
- struct ieee80211_hw *hw = pci_get_drvdata(pdev);
- struct ath5k_softc *sc = hw->priv;
- int err;
-
- err = pci_set_power_state(pdev, PCI_D0);
- if (err)
- return err;
-
- err = pci_enable_device(pdev);
- if (err)
- return err;
-
- pci_restore_state(pdev);
- /*
- * Suspend/Resume resets the PCI configuration space, so we have to
- * re-disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state
- */
- pci_write_config_byte(pdev, 0x41, 0);
-
- ath5k_init(sc);
- if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
- ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
- ath5k_hw_set_gpio(sc->ah, sc->led_pin, 0);
- }
-
- return 0;
-}
-#else
-#define ath5k_pci_suspend NULL
-#define ath5k_pci_resume NULL
-#endif /* CONFIG_PM */
-
-static struct pci_driver ath5k_pci_drv_id = {
- .name = "ath5k_pci",
- .id_table = ath5k_pci_id_table,
- .probe = ath5k_pci_probe,
- .remove = __devexit_p(ath5k_pci_remove),
- .suspend = ath5k_pci_suspend,
- .resume = ath5k_pci_resume,
-};
-
-static int mincalibrate = 1;
-static int maxcalibrate = INT_MAX / 1000;
-#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */
-
-static ctl_table ath5k_static_sysctls[] = {
-#if AR_DEBUG
- {
- .procname = "debug",
- .mode = 0644,
- .data = &ath5k_debug,
- .maxlen = sizeof(ath5k_debug),
- .proc_handler = proc_dointvec
- },
-#endif
- {
- .procname = "calibrate",
- .mode = 0644,
- .data = &ath5k_calinterval,
- .maxlen = sizeof(ath5k_calinterval),
- .extra1 = &mincalibrate,
- .extra2 = &maxcalibrate,
- .proc_handler = proc_dointvec_minmax
- },
- { 0 }
-};
-static ctl_table ath5k_ath5k_table[] = {
- {
- .procname = "ath",
- .mode = 0555,
- .child = ath5k_static_sysctls
- }, { 0 }
-};
-static ctl_table ath5k_root_table[] = {
- {
- .ctl_name = CTL_DEV,
- .procname = "dev",
- .mode = 0555,
- .child = ath5k_ath5k_table
- }, { 0 }
-};
-static struct ctl_table_header *ath5k_sysctl_header;
-
-static int __init init_ath5k_pci(void)
-{
- int ret;
-
- ret = pci_register_driver(&ath5k_pci_drv_id);
- if (ret) {
- printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
- return ret;
- }
- ath5k_sysctl_header = register_sysctl_table(ath5k_root_table);
-
- return 0;
-}
-
-static void __exit exit_ath5k_pci(void)
-{
- if (ath5k_sysctl_header)
- unregister_sysctl_table(ath5k_sysctl_header);
- pci_unregister_driver(&ath5k_pci_drv_id);
-}
-
-module_init(init_ath5k_pci);
-module_exit(exit_ath5k_pci);
-
-MODULE_AUTHOR("Jiri Slaby");
-MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards.");
-MODULE_SUPPORTED_DEVICE("Atheros WLAN cards");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION(ATH_PCI_VERSION " (EXPERIMENTAL)");
On Sat, Oct 27, 2007 at 02:58:58AM +0300, Nick Kossifidis wrote:
> Do you mind if i also send the rest of them on the same series (not
> only cleanups but code changes also) ?
That seems fine to me.
Thanks,
John
--
John W. Linville
[email protected]
2007/10/25, John W. Linville <[email protected]>:
> On Sun, Oct 21, 2007 at 01:36:30AM +0000, Nick Kossifidis wrote:
> > * Sort functions in segments like we do on the rest of the code (eg.
> > hw.c). This improves readability and maintainability.
> >
> > * Add myself as module author (a patch for this was submited some time
> > ago as part of SFLC's changes but wasn't applied)
> >
> > * Declare that this driver is or 5xxx chipset series only in
> > MODULE_SUPPORTED_DEVICE and MODULE_DESCRIPTION.
>
> This patch and the next one do not apply. Further, after manually
> fixing-up this patch it removes a bunch of functions that are still
> used.
>
> Would you mind re-diffing and trying again?
>
> Thanks,
>
> John
ACK ;-)
--
GPG ID: 0xD21DB2DB
As you read this post global entropy rises. Have Fun ;-)
Nick
2007/10/26, Nick Kossifidis <[email protected]>:
> 2007/10/25, John W. Linville <[email protected]>:
> > On Sun, Oct 21, 2007 at 01:36:30AM +0000, Nick Kossifidis wrote:
> > > * Sort functions in segments like we do on the rest of the code (eg.
> > > hw.c). This improves readability and maintainability.
> > >
> > > * Add myself as module author (a patch for this was submited some time
> > > ago as part of SFLC's changes but wasn't applied)
> > >
> > > * Declare that this driver is or 5xxx chipset series only in
> > > MODULE_SUPPORTED_DEVICE and MODULE_DESCRIPTION.
> >
> > This patch and the next one do not apply. Further, after manually
> > fixing-up this patch it removes a bunch of functions that are still
> > used.
> >
> > Would you mind re-diffing and trying again?
> >
> > Thanks,
> >
> > John
>
> ACK ;-)
>
Do you mind if i also send the rest of them on the same series (not
only cleanups but code changes also) ?
--
GPG ID: 0xD21DB2DB
As you read this post global entropy rises. Have Fun ;-)
Nick
On Sun, Oct 21, 2007 at 01:36:30AM +0000, Nick Kossifidis wrote:
> * Sort functions in segments like we do on the rest of the code (eg.
> hw.c). This improves readability and maintainability.
>
> * Add myself as module author (a patch for this was submited some time
> ago as part of SFLC's changes but wasn't applied)
>
> * Declare that this driver is or 5xxx chipset series only in
> MODULE_SUPPORTED_DEVICE and MODULE_DESCRIPTION.
This patch and the next one do not apply. Further, after manually
fixing-up this patch it removes a bunch of functions that are still
used.
Would you mind re-diffing and trying again?
Thanks,
John
--
John W. Linville
[email protected]