Archived
14
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/drivers/staging/rt2860/2860_main_dev.c
Adam McDaniel ed291e8051 Staging: rt2860: Ported v1.7.1.1 changes into v1.8.0.0, becoming v1.8.1.1
Staging: rt2860: Ported v1.7.1.1 changes into v1.8.0.0, becoming v1.8.1.1

When RaLink released rt2860 v1.7.0.0, it lacked proper support for both WEP
and WPA/WPA2 encryption. Either was possible, but the module had to be
compiled to support only one or the other, never both.

Since the EeePC was the most common device with this hardware (and these
users were complaining to RaLink that WPA/WPA2 encryption didn't work)
RaLink released a fix as an "eeepc-specific" version of this driver, v1.7.1.1

Unfortunately, when v1.8.0.0 was released, this WPA/WPA2 fix was never
included.

What complicates things further is that RaLink has no interest in
continuing work on this Linux driver for their hardware.

This commit ports the changes introduced in v1.7.1.1 into the v1.8.0.0
release, upgrading the kernel's module to v1.8.1.1

Signed-off-by: Adam McDaniel <adam@array.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-03 14:53:31 -07:00

1344 lines
34 KiB
C

/*
*************************************************************************
* 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);
#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 //
//
// 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,
remove: __devexit_p(rt2860_remove_one),
#ifdef CONFIG_PM
suspend: rt2860_suspend,
resume: rt2860_resume,
#endif
};
#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 //
static INT __init rt2860_init_module(VOID)
{
return pci_register_driver(&rt2860_driver);
}
//
// 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
free_netdev(net_dev);
}
//
// 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
rt2860_interrupt(int irq, void *dev_instance)
{
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;
BOOLEAN bOldValue;
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.
bOldValue = pAd->bPCIclkOff;
pAd->bPCIclkOff = FALSE;
{
RTMP_IO_READ32(pAd, INT_SOURCE_CSR, &IntSource.word);
RTMP_IO_WRITE32(pAd, INT_SOURCE_CSR, IntSource.word); // write 1 to clear
}
pAd->bPCIclkOff = bOldValue;
// 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))
{
return IRQ_HANDLED;
}
//
// Handle interrupt, walk through all bits
// Should start from highest priority interrupt
// The priority can be adjust by altering processing if statement
//
// 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);
printk("snowpin - IntSource.word == 0xffffffff\n");
return IRQ_HANDLED;
}
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 //
return IRQ_HANDLED;
}
/*
========================================================================
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;
print_name = pci_dev ? pci_name(pci_dev) : "rt2860";
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);
}