diff options
Diffstat (limited to 'drivers/staging/rt2860/2860_main_dev.c')
-rw-r--r-- | drivers/staging/rt2860/2860_main_dev.c | 1377 |
1 files changed, 1377 insertions, 0 deletions
diff --git a/drivers/staging/rt2860/2860_main_dev.c b/drivers/staging/rt2860/2860_main_dev.c new file mode 100644 index 00000000000..08ca81f43cc --- /dev/null +++ b/drivers/staging/rt2860/2860_main_dev.c @@ -0,0 +1,1377 @@ +/* + ************************************************************************* + * Ralink Tech Inc. + * 5F., No.36, Taiyuan St., Jhubei City, + * Hsinchu County 302, + * Taiwan, R.O.C. + * + * (c) Copyright 2002-2007, Ralink Technology, Inc. + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + ************************************************************************* + + Module Name: + 2870_main_dev.c + + Abstract: + Create and register network interface. + + Revision History: + Who When What + -------- ---------- ---------------------------------------------- +*/ + +#include "rt_config.h" + + +#ifdef MULTIPLE_CARD_SUPPORT +// record whether the card in the card list is used in the card file +extern UINT8 MC_CardUsed[]; +#endif // MULTIPLE_CARD_SUPPORT // + + +extern INT __devinit rt28xx_probe(IN void *_dev_p, IN void *_dev_id_p, + IN UINT argc, OUT PRTMP_ADAPTER *ppAd); + +static void rx_done_tasklet(unsigned long data); +static void mgmt_dma_done_tasklet(unsigned long data); +static void ac0_dma_done_tasklet(unsigned long data); +static void ac1_dma_done_tasklet(unsigned long data); +static void ac2_dma_done_tasklet(unsigned long data); +static void ac3_dma_done_tasklet(unsigned long data); +static void hcca_dma_done_tasklet(unsigned long data); +static void fifo_statistic_full_tasklet(unsigned long data); + + +/*---------------------------------------------------------------------*/ +/* Symbol & Macro Definitions */ +/*---------------------------------------------------------------------*/ +#define RT2860_INT_RX_DLY (1<<0) // bit 0 +#define RT2860_INT_TX_DLY (1<<1) // bit 1 +#define RT2860_INT_RX_DONE (1<<2) // bit 2 +#define RT2860_INT_AC0_DMA_DONE (1<<3) // bit 3 +#define RT2860_INT_AC1_DMA_DONE (1<<4) // bit 4 +#define RT2860_INT_AC2_DMA_DONE (1<<5) // bit 5 +#define RT2860_INT_AC3_DMA_DONE (1<<6) // bit 6 +#define RT2860_INT_HCCA_DMA_DONE (1<<7) // bit 7 +#define RT2860_INT_MGMT_DONE (1<<8) // bit 8 + +#define INT_RX RT2860_INT_RX_DONE + +#define INT_AC0_DLY (RT2860_INT_AC0_DMA_DONE) //| RT2860_INT_TX_DLY) +#define INT_AC1_DLY (RT2860_INT_AC1_DMA_DONE) //| RT2860_INT_TX_DLY) +#define INT_AC2_DLY (RT2860_INT_AC2_DMA_DONE) //| RT2860_INT_TX_DLY) +#define INT_AC3_DLY (RT2860_INT_AC3_DMA_DONE) //| RT2860_INT_TX_DLY) +#define INT_HCCA_DLY (RT2860_INT_HCCA_DMA_DONE) //| RT2860_INT_TX_DLY) +#define INT_MGMT_DLY RT2860_INT_MGMT_DONE + +/*---------------------------------------------------------------------*/ +/* Prototypes of Functions Used */ +/*---------------------------------------------------------------------*/ +/* function declarations */ +static INT __devinit rt2860_init_one (struct pci_dev *pci_dev, const struct pci_device_id *ent); +static VOID __devexit rt2860_remove_one(struct pci_dev *pci_dev); +static INT __devinit rt2860_probe(struct pci_dev *pci_dev, const struct pci_device_id *ent); +void init_thread_task(PRTMP_ADAPTER pAd); +static void __exit rt2860_cleanup_module(void); +static int __init rt2860_init_module(void); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +#ifdef CONFIG_PM +static int rt2860_suspend(struct pci_dev *pci_dev, pm_message_t state); +static int rt2860_resume(struct pci_dev *pci_dev); +#endif // CONFIG_PM // +#endif + + +// +// Ralink PCI device table, include all supported chipsets +// +static struct pci_device_id rt2860_pci_tbl[] __devinitdata = +{ + {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCI_DEVICE_ID)}, //RT28602.4G + {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCIe_DEVICE_ID)}, + {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2760_PCI_DEVICE_ID)}, + {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2790_PCIe_DEVICE_ID)}, + {PCI_DEVICE(VEN_AWT_PCI_VENDOR_ID, VEN_AWT_PCIe_DEVICE_ID)}, + {0,} // terminate list +}; + +MODULE_DEVICE_TABLE(pci, rt2860_pci_tbl); +#ifdef CONFIG_STA_SUPPORT +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +MODULE_VERSION(STA_DRIVER_VERSION); +#endif +#endif // CONFIG_STA_SUPPORT // + + +// +// Our PCI driver structure +// +static struct pci_driver rt2860_driver = +{ + name: "rt2860", + id_table: rt2860_pci_tbl, + probe: rt2860_init_one, +#if LINUX_VERSION_CODE >= 0x20412 + remove: __devexit_p(rt2860_remove_one), +#else + remove: __devexit(rt2860_remove_one), +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +#ifdef CONFIG_PM + suspend: rt2860_suspend, + resume: rt2860_resume, +#endif +#endif +}; + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +#ifdef CONFIG_PM + +VOID RT2860RejectPendingPackets( + IN PRTMP_ADAPTER pAd) +{ + // clear PS packets + // clear TxSw packets +} + +static int rt2860_suspend( + struct pci_dev *pci_dev, + pm_message_t state) +{ + struct net_device *net_dev = pci_get_drvdata(pci_dev); + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)NULL; + INT32 retval; + + + DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_suspend()\n")); + + if (net_dev == NULL) + { + DBGPRINT(RT_DEBUG_ERROR, ("net_dev == NULL!\n")); + } + else + { + pAd = net_dev->ml_priv; + + /* we can not use IFF_UP because ra0 down but ra1 up */ + /* and 1 suspend/resume function for 1 module, not for each interface */ + /* so Linux will call suspend/resume function once */ + if (VIRTUAL_IF_NUM(pAd) > 0) + { + // avoid users do suspend after interface is down + + // stop interface + netif_carrier_off(net_dev); + netif_stop_queue(net_dev); + + // mark device as removed from system and therefore no longer available + netif_device_detach(net_dev); + + // mark halt flag + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS); + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF); + + // take down the device + rt28xx_close((PNET_DEV)net_dev); + + RT_MOD_DEC_USE_COUNT(); + } + } + + // reference to http://vovo2000.com/type-lab/linux/kernel-api/linux-kernel-api.html + // enable device to generate PME# when suspended + // pci_choose_state(): Choose the power state of a PCI device to be suspended + retval = pci_enable_wake(pci_dev, pci_choose_state(pci_dev, state), 1); + // save the PCI configuration space of a device before suspending + pci_save_state(pci_dev); + // disable PCI device after use + pci_disable_device(pci_dev); + + retval = pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); + + DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_suspend()\n")); + return retval; +} + +static int rt2860_resume( + struct pci_dev *pci_dev) +{ + struct net_device *net_dev = pci_get_drvdata(pci_dev); + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)NULL; + INT32 retval; + + + // set the power state of a PCI device + // PCI has 4 power states, DO (normal) ~ D3(less power) + // in include/linux/pci.h, you can find that + // #define PCI_D0 ((pci_power_t __force) 0) + // #define PCI_D1 ((pci_power_t __force) 1) + // #define PCI_D2 ((pci_power_t __force) 2) + // #define PCI_D3hot ((pci_power_t __force) 3) + // #define PCI_D3cold ((pci_power_t __force) 4) + // #define PCI_UNKNOWN ((pci_power_t __force) 5) + // #define PCI_POWER_ERROR ((pci_power_t __force) -1) + retval = pci_set_power_state(pci_dev, PCI_D0); + + // restore the saved state of a PCI device + pci_restore_state(pci_dev); + + // initialize device before it's used by a driver + if (pci_enable_device(pci_dev)) + { + printk("pci enable fail!\n"); + return 0; + } + + DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_resume()\n")); + + if (net_dev == NULL) + { + DBGPRINT(RT_DEBUG_ERROR, ("net_dev == NULL!\n")); + } + else + pAd = net_dev->ml_priv; + + if (pAd != NULL) + { + /* we can not use IFF_UP because ra0 down but ra1 up */ + /* and 1 suspend/resume function for 1 module, not for each interface */ + /* so Linux will call suspend/resume function once */ + if (VIRTUAL_IF_NUM(pAd) > 0) + { + // mark device as attached from system and restart if needed + netif_device_attach(net_dev); + + if (rt28xx_open((PNET_DEV)net_dev) != 0) + { + // open fail + DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_resume()\n")); + return 0; + } + + // increase MODULE use count + RT_MOD_INC_USE_COUNT(); + + RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS); + RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF); + + netif_start_queue(net_dev); + netif_carrier_on(net_dev); + netif_wake_queue(net_dev); + } + } + + DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_resume()\n")); + return 0; +} +#endif // CONFIG_PM // +#endif + + +static INT __init rt2860_init_module(VOID) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + return pci_register_driver(&rt2860_driver); +#else + return pci_module_init(&rt2860_driver); +#endif +} + + +// +// Driver module unload function +// +static VOID __exit rt2860_cleanup_module(VOID) +{ + pci_unregister_driver(&rt2860_driver); +} + +module_init(rt2860_init_module); +module_exit(rt2860_cleanup_module); + + +static INT __devinit rt2860_init_one ( + IN struct pci_dev *pci_dev, + IN const struct pci_device_id *ent) +{ + INT rc; + + DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_init_one\n")); + + // wake up and enable device + if (pci_enable_device (pci_dev)) + { + rc = -EIO; + } + else + { + rc = rt2860_probe(pci_dev, ent); + } + + DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_init_one\n")); + return rc; +} + + +static VOID __devexit rt2860_remove_one( + IN struct pci_dev *pci_dev) +{ + struct net_device *net_dev = pci_get_drvdata(pci_dev); + RTMP_ADAPTER *pAd = net_dev->ml_priv; + + DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_remove_one\n")); + + if (pAd != NULL) + { +#ifdef MULTIPLE_CARD_SUPPORT + if ((pAd->MC_RowID >= 0) && (pAd->MC_RowID <= MAX_NUM_OF_MULTIPLE_CARD)) + MC_CardUsed[pAd->MC_RowID] = 0; // not clear MAC address +#endif // MULTIPLE_CARD_SUPPORT // + + + + + // Unregister network device + unregister_netdev(net_dev); + + // Unmap CSR base address + iounmap((char *)(net_dev->base_addr)); + + RTMPFreeAdapter(pAd); + + // release memory region + release_mem_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + } + else + { + // Unregister network device + unregister_netdev(net_dev); + + // Unmap CSR base address + iounmap((char *)(net_dev->base_addr)); + + // release memory region + release_mem_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + } + + // Free pre-allocated net_device memory +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + free_netdev(net_dev); +#else + kfree(net_dev); +#endif +} + +// +// PCI device probe & initialization function +// +static INT __devinit rt2860_probe( + IN struct pci_dev *pci_dev, + IN const struct pci_device_id *ent) +{ + PRTMP_ADAPTER pAd; + INT rv = 0; + + rv = (INT)rt28xx_probe((void *)pci_dev, (void *)ent, 0, &pAd); + OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE); + return rv; +} + + +void init_thread_task(IN PRTMP_ADAPTER pAd) +{ + POS_COOKIE pObj; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + tasklet_init(&pObj->rx_done_task, rx_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->mgmt_dma_done_task, mgmt_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->ac0_dma_done_task, ac0_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->ac1_dma_done_task, ac1_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->ac2_dma_done_task, ac2_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->ac3_dma_done_task, ac3_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->hcca_dma_done_task, hcca_dma_done_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->tbtt_task, tbtt_tasklet, (unsigned long)pAd); + tasklet_init(&pObj->fifo_statistic_full_task, fifo_statistic_full_tasklet, (unsigned long)pAd); +} + +void kill_thread_task(IN PRTMP_ADAPTER pAd) +{ + POS_COOKIE pObj; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + tasklet_kill(&pObj->rx_done_task); + tasklet_kill(&pObj->mgmt_dma_done_task); + tasklet_kill(&pObj->ac0_dma_done_task); + tasklet_kill(&pObj->ac1_dma_done_task); + tasklet_kill(&pObj->ac2_dma_done_task); + tasklet_kill(&pObj->ac3_dma_done_task); + tasklet_kill(&pObj->hcca_dma_done_task); + tasklet_kill(&pObj->tbtt_task); + tasklet_kill(&pObj->fifo_statistic_full_task); +} + + +static void rt2860_int_enable(PRTMP_ADAPTER pAd, unsigned int mode) +{ + u32 regValue; + + pAd->int_disable_mask &= ~(mode); + regValue = pAd->int_enable_reg & ~(pAd->int_disable_mask); + RTMP_IO_WRITE32(pAd, INT_MASK_CSR, regValue); // 1:enable + + if (regValue != 0) + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_INTERRUPT_ACTIVE); +} + + +static void rt2860_int_disable(PRTMP_ADAPTER pAd, unsigned int mode) +{ + u32 regValue; + + pAd->int_disable_mask |= mode; + regValue = pAd->int_enable_reg & ~(pAd->int_disable_mask); + RTMP_IO_WRITE32(pAd, INT_MASK_CSR, regValue); // 0: disable + + if (regValue == 0) + { + RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_INTERRUPT_ACTIVE); + } +} + +static void mgmt_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + IntSource.word = 0; + IntSource.field.MgmtDmaDone = 1; + pAd->int_pending &= ~INT_MGMT_DLY; + + RTMPHandleMgmtRingDmaDoneInterrupt(pAd); + + // if you use RTMP_SEM_LOCK, sometimes kernel will hang up, no any + // bug report output + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid lose of interrupts + */ + if (pAd->int_pending & INT_MGMT_DLY) + { + tasklet_hi_schedule(&pObj->mgmt_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_MGMT_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + +static void rx_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + BOOLEAN bReschedule = 0; + POS_COOKIE pObj; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + pAd->int_pending &= ~(INT_RX); +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + bReschedule = STARxDoneInterruptHandle(pAd, 0); +#endif // CONFIG_STA_SUPPORT // + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid rotting packet + */ + if (pAd->int_pending & INT_RX || bReschedule) + { + tasklet_hi_schedule(&pObj->rx_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable RxINT again */ + rt2860_int_enable(pAd, INT_RX); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + +} + +void fifo_statistic_full_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + POS_COOKIE pObj; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + pAd->int_pending &= ~(FifoStaFullInt); + NICUpdateFifoStaCounters(pAd); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid rotting packet + */ + if (pAd->int_pending & FifoStaFullInt) + { + tasklet_hi_schedule(&pObj->fifo_statistic_full_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable RxINT again */ + + rt2860_int_enable(pAd, FifoStaFullInt); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + +} + +static void hcca_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + + IntSource.word = 0; + IntSource.field.HccaDmaDone = 1; + pAd->int_pending &= ~INT_HCCA_DLY; + + RTMPHandleTxRingDmaDoneInterrupt(pAd, IntSource); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid lose of interrupts + */ + if (pAd->int_pending & INT_HCCA_DLY) + { + tasklet_hi_schedule(&pObj->hcca_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_HCCA_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + +static void ac3_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + BOOLEAN bReschedule = 0; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + IntSource.word = 0; + IntSource.field.Ac3DmaDone = 1; + pAd->int_pending &= ~INT_AC3_DLY; + + bReschedule = RTMPHandleTxRingDmaDoneInterrupt(pAd, IntSource); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid lose of interrupts + */ + if ((pAd->int_pending & INT_AC3_DLY) || bReschedule) + { + tasklet_hi_schedule(&pObj->ac3_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_AC3_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + +static void ac2_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + BOOLEAN bReschedule = 0; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + IntSource.word = 0; + IntSource.field.Ac2DmaDone = 1; + pAd->int_pending &= ~INT_AC2_DLY; + + bReschedule = RTMPHandleTxRingDmaDoneInterrupt(pAd, IntSource); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + + /* + * double check to avoid lose of interrupts + */ + if ((pAd->int_pending & INT_AC2_DLY) || bReschedule) + { + tasklet_hi_schedule(&pObj->ac2_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_AC2_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + +static void ac1_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + BOOLEAN bReschedule = 0; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + IntSource.word = 0; + IntSource.field.Ac1DmaDone = 1; + pAd->int_pending &= ~INT_AC1_DLY; + + bReschedule = RTMPHandleTxRingDmaDoneInterrupt(pAd, IntSource); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid lose of interrupts + */ + if ((pAd->int_pending & INT_AC1_DLY) || bReschedule) + { + tasklet_hi_schedule(&pObj->ac1_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_AC1_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + +static void ac0_dma_done_tasklet(unsigned long data) +{ + unsigned long flags; + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + BOOLEAN bReschedule = 0; + + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + IntSource.word = 0; + IntSource.field.Ac0DmaDone = 1; + pAd->int_pending &= ~INT_AC0_DLY; + + bReschedule = RTMPHandleTxRingDmaDoneInterrupt(pAd, IntSource); + + RTMP_INT_LOCK(&pAd->irq_lock, flags); + /* + * double check to avoid lose of interrupts + */ + if ((pAd->int_pending & INT_AC0_DLY) || bReschedule) + { + tasklet_hi_schedule(&pObj->ac0_dma_done_task); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); + return; + } + + /* enable TxDataInt again */ + rt2860_int_enable(pAd, INT_AC0_DLY); + RTMP_INT_UNLOCK(&pAd->irq_lock, flags); +} + + +int print_int_count; + +IRQ_HANDLE_TYPE +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) +rt2860_interrupt(int irq, void *dev_instance) +#else +rt2860_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +#endif +{ + struct net_device *net_dev = (struct net_device *) dev_instance; + PRTMP_ADAPTER pAd = net_dev->ml_priv; + INT_SOURCE_CSR_STRUC IntSource; + POS_COOKIE pObj; + + pObj = (POS_COOKIE) pAd->OS_Cookie; + + + /* Note 03312008: we can not return here before + RTMP_IO_READ32(pAd, INT_SOURCE_CSR, &IntSource.word); + RTMP_IO_WRITE32(pAd, INT_SOURCE_CSR, IntSource.word); + Or kernel will panic after ifconfig ra0 down sometimes */ + + + // + // Inital the Interrupt source. + // + IntSource.word = 0x00000000L; +// McuIntSource.word = 0x00000000L; + + // + // Get the interrupt sources & saved to local variable + // + //RTMP_IO_READ32(pAd, where, &McuIntSource.word); + //RTMP_IO_WRITE32(pAd, , McuIntSource.word); + + // + // Flag fOP_STATUS_DOZE On, means ASIC put to sleep, elase means ASICK WakeUp + // And at the same time, clock maybe turned off that say there is no DMA service. + // when ASIC get to sleep. + // To prevent system hang on power saving. + // We need to check it before handle the INT_SOURCE_CSR, ASIC must be wake up. + // + // RT2661 => when ASIC is sleeping, MAC register cannot be read and written. + // RT2860 => when ASIC is sleeping, MAC register can be read and written. + + { + RTMP_IO_READ32(pAd, INT_SOURCE_CSR, &IntSource.word); + RTMP_IO_WRITE32(pAd, INT_SOURCE_CSR, IntSource.word); // write 1 to clear + } + + // Do nothing if Reset in progress + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RESET_IN_PROGRESS) || + RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS)) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + return IRQ_HANDLED; +#else + return; +#endif + } + + // + // Handle interrupt, walk through all bits + // Should start from highest priority interrupt + // The priority can be adjust by altering processing if statement + // + + pAd->bPCIclkOff = FALSE; + + // If required spinlock, each interrupt service routine has to acquire + // and release itself. + // + + // Do nothing if NIC doesn't exist + if (IntSource.word == 0xffffffff) + { + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST | fRTMP_ADAPTER_HALT_IN_PROGRESS); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + return IRQ_HANDLED; +#else + return; +#endif + } + + if (IntSource.word & TxCoherent) + { + DBGPRINT(RT_DEBUG_ERROR, (">>>TxCoherent<<<\n")); + RTMPHandleRxCoherentInterrupt(pAd); + } + + if (IntSource.word & RxCoherent) + { + DBGPRINT(RT_DEBUG_ERROR, (">>>RxCoherent<<<\n")); + RTMPHandleRxCoherentInterrupt(pAd); + } + + if (IntSource.word & FifoStaFullInt) + { +#if 1 + if ((pAd->int_disable_mask & FifoStaFullInt) == 0) + { + /* mask FifoStaFullInt */ + rt2860_int_disable(pAd, FifoStaFullInt); + tasklet_hi_schedule(&pObj->fifo_statistic_full_task); + } + pAd->int_pending |= FifoStaFullInt; +#else + NICUpdateFifoStaCounters(pAd); +#endif + } + + if (IntSource.word & INT_MGMT_DLY) + { + if ((pAd->int_disable_mask & INT_MGMT_DLY) ==0 ) + { + rt2860_int_disable(pAd, INT_MGMT_DLY); + tasklet_hi_schedule(&pObj->mgmt_dma_done_task); + } + pAd->int_pending |= INT_MGMT_DLY ; + } + + if (IntSource.word & INT_RX) + { + if ((pAd->int_disable_mask & INT_RX) == 0) + { + /* mask RxINT */ + rt2860_int_disable(pAd, INT_RX); + tasklet_hi_schedule(&pObj->rx_done_task); + } + pAd->int_pending |= INT_RX; + } + + if (IntSource.word & INT_HCCA_DLY) + { + + if ((pAd->int_disable_mask & INT_HCCA_DLY) == 0) + { + /* mask TxDataInt */ + rt2860_int_disable(pAd, INT_HCCA_DLY); + tasklet_hi_schedule(&pObj->hcca_dma_done_task); + } + pAd->int_pending |= INT_HCCA_DLY; + } + + if (IntSource.word & INT_AC3_DLY) + { + + if ((pAd->int_disable_mask & INT_AC3_DLY) == 0) + { + /* mask TxDataInt */ + rt2860_int_disable(pAd, INT_AC3_DLY); + tasklet_hi_schedule(&pObj->ac3_dma_done_task); + } + pAd->int_pending |= INT_AC3_DLY; + } + + if (IntSource.word & INT_AC2_DLY) + { + + if ((pAd->int_disable_mask & INT_AC2_DLY) == 0) + { + /* mask TxDataInt */ + rt2860_int_disable(pAd, INT_AC2_DLY); + tasklet_hi_schedule(&pObj->ac2_dma_done_task); + } + pAd->int_pending |= INT_AC2_DLY; + } + + if (IntSource.word & INT_AC1_DLY) + { + + pAd->int_pending |= INT_AC1_DLY; + + if ((pAd->int_disable_mask & INT_AC1_DLY) == 0) + { + /* mask TxDataInt */ + rt2860_int_disable(pAd, INT_AC1_DLY); + tasklet_hi_schedule(&pObj->ac1_dma_done_task); + } + + } + + if (IntSource.word & INT_AC0_DLY) + { + pAd->int_pending |= INT_AC0_DLY; + + if ((pAd->int_disable_mask & INT_AC0_DLY) == 0) + { + /* mask TxDataInt */ + rt2860_int_disable(pAd, INT_AC0_DLY); + tasklet_hi_schedule(&pObj->ac0_dma_done_task); + } + + } + + if (IntSource.word & PreTBTTInt) + { + RTMPHandlePreTBTTInterrupt(pAd); + } + + if (IntSource.word & TBTTInt) + { + RTMPHandleTBTTInterrupt(pAd); + } + + + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + { + if (IntSource.word & AutoWakeupInt) + RTMPHandleTwakeupInterrupt(pAd); + } +#endif // CONFIG_STA_SUPPORT // + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + return IRQ_HANDLED; +#endif + +} + +/* +======================================================================== +Routine Description: + Check the chipset vendor/product ID. + +Arguments: + _dev_p Point to the PCI or USB device + +Return Value: + TRUE Check ok + FALSE Check fail + +Note: +======================================================================== +*/ +BOOLEAN RT28XXChipsetCheck( + IN void *_dev_p) +{ + /* always TRUE */ + return TRUE; +} + + +/* +======================================================================== +Routine Description: + Init net device structure. + +Arguments: + _dev_p Point to the PCI or USB device + *net_dev Point to the net device + *pAd the raxx interface data pointer + +Return Value: + TRUE Init ok + FALSE Init fail + +Note: +======================================================================== +*/ +BOOLEAN RT28XXNetDevInit( + IN void *_dev_p, + IN struct net_device *net_dev, + IN RTMP_ADAPTER *pAd) +{ + struct pci_dev *pci_dev = (struct pci_dev *)_dev_p; + const CHAR *print_name; + ULONG csr_addr; + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + print_name = pci_dev ? pci_name(pci_dev) : "rt2860"; +#else + print_name = pci_dev ? pci_dev->slot_name : "rt2860"; +#endif // LINUX_VERSION_CODE // + + net_dev->base_addr = 0; + net_dev->irq = 0; + + if (pci_request_regions(pci_dev, print_name)) + goto err_out_free_netdev; + + // interrupt IRQ number + net_dev->irq = pci_dev->irq; + + // map physical address to virtual address for accessing register + csr_addr = (unsigned long) ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + + if (!csr_addr) + { + DBGPRINT(RT_DEBUG_ERROR, + ("ioremap failed for device %s, region 0x%lX @ 0x%lX\n", + print_name, (ULONG)pci_resource_len(pci_dev, 0), + (ULONG)pci_resource_start(pci_dev, 0))); + goto err_out_free_res; + } + + // Save CSR virtual address and irq to device structure + net_dev->base_addr = csr_addr; + pAd->CSRBaseAddress = (PUCHAR)net_dev->base_addr; + + // Set DMA master + pci_set_master(pci_dev); + + net_dev->priv_flags = INT_MAIN; + + DBGPRINT(RT_DEBUG_TRACE, ("%s: at 0x%lx, VA 0x%lx, IRQ %d. \n", + net_dev->name, (ULONG)pci_resource_start(pci_dev, 0), + (ULONG)csr_addr, pci_dev->irq)); + return TRUE; + + + /* --------------------------- ERROR HANDLE --------------------------- */ +err_out_free_res: + pci_release_regions(pci_dev); +err_out_free_netdev: + /* free netdev in caller, not here */ + return FALSE; +} + + +/* +======================================================================== +Routine Description: + Init net device structure. + +Arguments: + _dev_p Point to the PCI or USB device + *pAd the raxx interface data pointer + +Return Value: + TRUE Config ok + FALSE Config fail + +Note: +======================================================================== +*/ +BOOLEAN RT28XXProbePostConfig( + IN void *_dev_p, + IN RTMP_ADAPTER *pAd, + IN INT32 argc) +{ + /* no use */ + return TRUE; +} + + +/* +======================================================================== +Routine Description: + Disable DMA. + +Arguments: + *pAd the raxx interface data pointer + +Return Value: + None + +Note: +======================================================================== +*/ +VOID RT28XXDMADisable( + IN RTMP_ADAPTER *pAd) +{ + WPDMA_GLO_CFG_STRUC GloCfg; + + + RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word); + GloCfg.word &= 0xff0; + GloCfg.field.EnTXWriteBackDDONE =1; + RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word); +} + + +/* +======================================================================== +Routine Description: + Enable DMA. + +Arguments: + *pAd the raxx interface data pointer + +Return Value: + None + +Note: +======================================================================== +*/ +VOID RT28XXDMAEnable( + IN RTMP_ADAPTER *pAd) +{ + WPDMA_GLO_CFG_STRUC GloCfg; + int i = 0; + + RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0x4); + do + { + RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word); + if ((GloCfg.field.TxDMABusy == 0) && (GloCfg.field.RxDMABusy == 0)) + break; + + DBGPRINT(RT_DEBUG_TRACE, ("==> DMABusy\n")); + RTMPusecDelay(1000); + i++; + }while ( i <200); + + RTMPusecDelay(50); + + GloCfg.field.EnTXWriteBackDDONE = 1; + GloCfg.field.WPDMABurstSIZE = 2; + GloCfg.field.EnableRxDMA = 1; + GloCfg.field.EnableTxDMA = 1; + + DBGPRINT(RT_DEBUG_TRACE, ("<== WRITE DMA offset 0x208 = 0x%x\n", GloCfg.word)); + RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word); + +} + +/* +======================================================================== +Routine Description: + Write Beacon buffer to Asic. + +Arguments: + *pAd the raxx interface data pointer + +Return Value: + None + +Note: +======================================================================== +*/ +VOID RT28xx_UpdateBeaconToAsic( + IN RTMP_ADAPTER *pAd, + IN INT apidx, + IN ULONG FrameLen, + IN ULONG UpdatePos) +{ + ULONG CapInfoPos = 0; + UCHAR *ptr, *ptr_update, *ptr_capinfo; + UINT i; + BOOLEAN bBcnReq = FALSE; + UCHAR bcn_idx = 0; + + { + DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __func__)); + return; + } + + if (bBcnReq == FALSE) + { + /* when the ra interface is down, do not send its beacon frame */ + /* clear all zero */ + for(i=0; i<TXWI_SIZE; i+=4) + RTMP_IO_WRITE32(pAd, pAd->BeaconOffset[bcn_idx] + i, 0x00); + } + else + { + ptr = (PUCHAR)&pAd->BeaconTxWI; +#ifdef RT_BIG_ENDIAN + RTMPWIEndianChange(ptr, TYPE_TXWI); +#endif + for (i=0; i<TXWI_SIZE; i+=4) // 16-byte TXWI field + { + UINT32 longptr = *ptr + (*(ptr+1)<<8) + (*(ptr+2)<<16) + (*(ptr+3)<<24); + RTMP_IO_WRITE32(pAd, pAd->BeaconOffset[bcn_idx] + i, longptr); + ptr += 4; + } + + // Update CapabilityInfo in Beacon + for (i = CapInfoPos; i < (CapInfoPos+2); i++) + { + RTMP_IO_WRITE8(pAd, pAd->BeaconOffset[bcn_idx] + TXWI_SIZE + i, *ptr_capinfo); + ptr_capinfo ++; + } + + if (FrameLen > UpdatePos) + { + for (i= UpdatePos; i< (FrameLen); i++) + { + RTMP_IO_WRITE8(pAd, pAd->BeaconOffset[bcn_idx] + TXWI_SIZE + i, *ptr_update); + ptr_update ++; + } + } + + } + +} + +#ifdef CONFIG_STA_SUPPORT +VOID RTMPInitPCIeLinkCtrlValue( + IN PRTMP_ADAPTER pAd) +{ +} + +VOID RTMPFindHostPCIDev( + IN PRTMP_ADAPTER pAd) +{ +} + +/* + ======================================================================== + + Routine Description: + + Arguments: + Level = RESTORE_HALT : Restore PCI host and Ralink PCIe Link Control field to its default value. + Level = Other Value : Restore from dot11 power save or radio off status. And force PCI host Link Control fields to 0x1 + + ======================================================================== +*/ +VOID RTMPPCIeLinkCtrlValueRestore( + IN PRTMP_ADAPTER pAd, + IN UCHAR Level) +{ +} + +/* + ======================================================================== + + Routine Description: + + Arguments: + Max : limit Host PCI and Ralink PCIe device's LINK CONTROL field's value. + Because now frequently set our device to mode 1 or mode 3 will cause problem. + + ======================================================================== +*/ +VOID RTMPPCIeLinkCtrlSetting( + IN PRTMP_ADAPTER pAd, + IN USHORT Max) +{ +} +#endif // CONFIG_STA_SUPPORT // + +VOID rt2860_stop(struct net_device *net_dev) +{ + PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)NULL; + if (net_dev == NULL) + { + DBGPRINT(RT_DEBUG_ERROR, ("net_dev == NULL!\n")); + } + else + pAd = net_dev->ml_priv; + + if (pAd != NULL) + { + // stop interface + netif_carrier_off(net_dev); + netif_stop_queue(net_dev); + + // mark device as removed from system and therefore no longer available + netif_device_detach(net_dev); + + // mark halt flag + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS); + RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF); + + // take down the device + rt28xx_close((PNET_DEV)net_dev); + RT_MOD_DEC_USE_COUNT(); + } + return; +} + +/* + * invaild or writeback cache + * and convert virtual address to physical address + */ +dma_addr_t linux_pci_map_single(void *handle, void *ptr, size_t size, int sd_idx, int direction) +{ + PRTMP_ADAPTER pAd; + POS_COOKIE pObj; + + /* + ------ Porting Information ------ + > For Tx Alloc: + mgmt packets => sd_idx = 0 + SwIdx: pAd->MgmtRing.TxCpuIdx + pTxD : pAd->MgmtRing.Cell[SwIdx].AllocVa; + + data packets => sd_idx = 1 + TxIdx : pAd->TxRing[pTxBlk->QueIdx].TxCpuIdx + QueIdx: pTxBlk->QueIdx + pTxD : pAd->TxRing[pTxBlk->QueIdx].Cell[TxIdx].AllocVa; + + > For Rx Alloc: + sd_idx = -1 + */ + + pAd = (PRTMP_ADAPTER)handle; + pObj = (POS_COOKIE)pAd->OS_Cookie; + + if (sd_idx == 1) + { + PTX_BLK pTxBlk; + pTxBlk = (PTX_BLK)ptr; + return pci_map_single(pObj->pci_dev, pTxBlk->pSrcBufData, pTxBlk->SrcBufLen, direction); + } + else + { + return pci_map_single(pObj->pci_dev, ptr, size, direction); + } + +} + +void linux_pci_unmap_single(void *handle, dma_addr_t dma_addr, size_t size, int direction) +{ + PRTMP_ADAPTER pAd; + POS_COOKIE pObj; + + pAd=(PRTMP_ADAPTER)handle; + pObj = (POS_COOKIE)pAd->OS_Cookie; + + pci_unmap_single(pObj->pci_dev, dma_addr, size, direction); + +} + |