📄 ar6000_cs.c
字号:
/* * Copyright (c) 2004-2006 Atheros Communications Inc. * All rights reserved. * * It also includes code from the Linux PCMCIA package, (C) David Hinds. * Wireless Network driver for Atheros AR6001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * */#include <linux/config.h>#ifdef __IN_PCMCIA_PACKAGE__#include <pcmcia/k_compat.h>#endif /* __IN_PCMCIA_PACKAGE__ */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/wireless.h>#include <linux/interrupt.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/system.h>#include "ar6000_cs_internal.h"/********************************************************************//* Module stuff *//********************************************************************/MODULE_DESCRIPTION("Driver for Atheros PCMCIA WLAN Card");#define CS_CHECK(fn, ret) \do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)/* * The dev_info variable is the "key" that is used to match up this * device driver with appropriate cards, through the card * configuration database. */static dev_info_t dev_info = "ar6000_cs";/* * A linked list of "instances" of the device. Each actual PCMCIA * card corresponds to one device instance, and is described by one * dev_link_t structure (defined in ds.h). */static dev_link_t *dev_list;/* List of upper driver instances */static CFFUNCTION *drv_list;static struct rw_semaphore dev_lock;static struct rw_semaphore drv_lock;static struct work_struct hotPlugTask;#ifdef DEBUGA_UINT32 debugbusdrv=0;enum { ATH_LOG_SEND = 0x0001, ATH_LOG_RECV = 0x0002, ATH_LOG_SYNC = 0x0004, ATH_LOG_DUMP = 0x0008, ATH_LOG_INF = 0x0010, ATH_LOG_TRC = 0x0020, ATH_LOG_WARN = 0x0040, ATH_LOG_ERR = 0x0080, ATH_LOG_ANY = 0xFFFF,};#define BUSDRV_DEBUG_PRINTF(flag, args...) do { \ if (debugbusdrv) { \ printk(KERN_ALERT args); \ } \} while (0)#endif //DEBUG/********************************************************************//* Function prototypes *//********************************************************************/static void ar6000_cs_config(dev_link_t * link);static void ar6000_cs_release(dev_link_t * link);static int ar6000_cs_event(event_t event, int priority, event_callback_args_t * args);static dev_link_t *ar6000_cs_attach(void);static void ar6000_cs_detach(dev_link_t *);/********************************************************************* API exposed to Upper layer.********************************************************************//* Go thru the list of devices maintained in this layer and if it* matches the upper layer module, call the module's probe function.* Also pass a device's instance as an opaque reference to the upper* layer module.*/CF_STATUS CF_RegisterFunction(PCFFUNCTION pFunction) { struct dev_link_t *ptr = NULL; CF_DEVICE *pCfDevice = NULL; PCF_PNP_INFO pnpPtr = NULL; CF_STATUS status = CF_STATUS_SUCCESS; //Insert the upper layer driver ctx in the list maintained in this layer. insert_drv_list(pFunction); // Acquire Lock down_read(&dev_lock); //traverse the dev_link_t list for ( ptr=dev_list; (ptr); ptr=ptr->next ) { /* check if ptr->priv->pCfDevice.pId matches that provided by the * upper layer If so insert the pFunction ctx into that devices * function list. and call that devices probe function & return. */ pCfDevice = (CF_DEVICE *)&(((struct ar6000_pccard *)(ptr->priv))->CfDevice); // Go thru the list of PNP Ids & if any of them match, call probe(). /* Check for NULL Manf-Code to traverse the Pnp List that is * null terminated. */ for ( pnpPtr = pFunction->pIds; pnpPtr->CF_ManufacturerCode != 0 ; \ pnpPtr++ ) { BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "CFRegisterFunc b4 manfid comp %04x, %04x\n", pCfDevice->pId.CF_ManufacturerID, pnpPtr->CF_ManufacturerID); if( pCfDevice->pId.CF_ManufacturerID == pnpPtr->CF_ManufacturerID ) { pCfDevice->pFunction = pFunction; if ( !((*pFunction->pProbe)(pFunction, pCfDevice)) ) { /* The device is not successfuly probed by this * fuction driver. disassociate them. */ pCfDevice->pFunction = NULL; status = CF_STATUS_ERROR; } status = CF_STATUS_SUCCESS; break; } } } up_read(&dev_lock); return status;}CF_STATUS CF_UnregisterFunction(PCFFUNCTION pFunction) { /* Release the device structure if a card is present or else * remove the function instance from the FuncList */ struct dev_link_t *ptr = NULL; CF_DEVICE *pCfDevice = NULL; PCF_PNP_INFO pnpPtr = NULL; down_read(&dev_lock); //traverse the dev_link_t list for ( ptr=dev_list; (ptr); ptr=ptr->next ) { /* check if ptr->priv->pCfDevice.pId matches that provided by the * upper layer If so remove the association between the device and the * upper layer driver. */ pCfDevice = (CF_DEVICE *)&(((struct ar6000_pccard *)(ptr->priv))->CfDevice); for ( pnpPtr = pFunction->pIds; pnpPtr->CF_ManufacturerCode != 0 ; \ pnpPtr++ ) { BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "CF_UnRegisterFunc manfid comp %04x, %04x\n", pCfDevice->pId.CF_ManufacturerID, pFunction->pIds->CF_ManufacturerID); if( pCfDevice->pId.CF_ManufacturerID == pnpPtr->CF_ManufacturerID) { pCfDevice->pFunction = NULL; break; } } } up_read(&dev_lock); remove_drv_list(pFunction); return CF_STATUS_SUCCESS;}static CF_STATUS ar6000_cs_read_byte(CF_DEVICE *pDev, PCFREQUEST pReq) { A_UINT32 len = pReq->length; A_UCHAR *buff = pReq->pDataBuffer; A_UINT32 ctr=0; A_UCHAR *port = ((A_UCHAR *)pDev->mem_start) + pReq->address; for (ctr=0;ctr<len;ctr++) { *buff = readb(port); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP, "R: data: %x, address: %p\n",*buff,port); buff++; if (!(pReq->Flags & CFREQ_FLAGS_FIXED_ADDRESS)) port += 1; } return CF_STATUS_SUCCESS;}static CF_STATUS ar6000_cs_write_byte(CF_DEVICE *pDev, PCFREQUEST pReq) { A_UINT32 ctr = 0; A_UINT32 len = pReq->length; A_UCHAR *buff = pReq->pDataBuffer; A_UCHAR *port = ((A_UCHAR *)pDev->mem_start) + pReq->address; for (ctr=0;ctr<len;ctr++) { BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP, "W: data: %x, address: %p\n",*buff,port); writeb(*buff, port); buff++; if (!(pReq->Flags & CFREQ_FLAGS_FIXED_ADDRESS)) port += 1; } return CF_STATUS_SUCCESS;}static CF_STATUS ar6000_cs_read_word(CF_DEVICE *pDev, PCFREQUEST pReq) { A_UINT32 len = pReq->length; A_UINT16 *buff = pReq->pDataBuffer; A_UINT32 ctr=0; A_UCHAR *port = ((A_UCHAR *)pDev->mem_start) + pReq->address; for (ctr=0;(ctr+1)<len;) { *buff = readw(port); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP,"R: data: %x, address: %p\n",*buff,port); buff++; if (!(pReq->Flags & CFREQ_FLAGS_FIXED_ADDRESS)) port += 2; ctr += 2; } //Read the last byte if ( ctr < len ) { *((unsigned char *)buff) = readb(port); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP,"R: data: %x, address: %p\n",*((unsigned char *)buff),port); } return CF_STATUS_SUCCESS;}static CF_STATUS ar6000_cs_write_word(CF_DEVICE *pDev, PCFREQUEST pReq) { A_UINT32 ctr = 0; A_UINT32 len = pReq->length; A_UINT16 *buff = pReq->pDataBuffer; A_UCHAR *port = ((A_UCHAR *)pDev->mem_start) + pReq->address; for (ctr=0;(ctr+1)<len;) { BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP,"W: data: %x, address: %p\n",*buff,port); writew(*buff, port); buff++; if (!(pReq->Flags & CFREQ_FLAGS_FIXED_ADDRESS)) port += 2; ctr += 2; } // Write the last byte if ( ctr < len ) { BUSDRV_DEBUG_PRINTF(ATH_DEBUG_DUMP,"W: data: %x, address: %p\n",*((unsigned char *)buff),port); writeb(*((unsigned char *)buff), port); } return CF_STATUS_SUCCESS;}CF_STATUS CF_BusRequest_Word(PCFDEVICE pDev, PCFREQUEST pReq){ CF_STATUS status; if ( pReq->Flags & CFREQ_FLAGS_DATA_WRITE ) { status = ar6000_cs_write_word((CF_DEVICE *)pDev, pReq); } else { status = ar6000_cs_read_word((CF_DEVICE *)pDev, pReq); } return status;}CF_STATUS CF_BusRequest_Byte(PCFDEVICE pDev, PCFREQUEST pReq){ CF_STATUS status; if ( pReq->Flags & CFREQ_FLAGS_DATA_WRITE ) { status = ar6000_cs_write_byte((CF_DEVICE *)pDev, pReq); } else { status = ar6000_cs_read_byte((CF_DEVICE *)pDev, pReq); } return status;}void CF_SetIrqHandler(PCFDEVICE pDev, pIsrHandler pFn1, pDsrHandler pFn2, void * pContext) { ((CF_DEVICE *)pDev)->pIrqFunction = (pFn1); ((CF_DEVICE *)pDev)->IrqContext = (void *)(pContext); if (pFn2) { tasklet_init(&(((CF_DEVICE *)pDev)->tasklet),pFn2,(unsigned long)pContext); }}/********************************************************************//* PCMCIA stuff *//********************************************************************//* For 2.4 kernels, cs_error is not exported. providing our own...*/static voidar6000_cs_error(client_handle_t handle, int func, int ret){ error_info_t err = { func, ret }; pcmcia_report_error(handle, &err);}/* Device List manipulation routines */static void insert_dev_list(struct ar6000_pccard *dev){ dev_link_t *temp=NULL; down_write(&dev_lock); //Check for empty dev list. if(!dev_list) { dev_list = &dev->link; } else { //traverse to the end of the list. for(temp=dev_list;(temp->next);temp=temp->next); temp->next = &dev->link; } up_write(&dev_lock); return;}static struct dev_link_t * remove_dev_list(struct ar6000_pccard *dev){ struct dev_link_t *curr,*prev; curr = prev = NULL; down_write(&dev_lock); for(curr=prev=dev_list;(curr);prev=curr,curr=curr->next) { if(curr->priv == dev) { if(curr!=prev) prev->next = curr->next; else dev_list = curr->next; break; } } up_write(&dev_lock); return curr;}/* Upper driver instance list manipulation routines*/static void insert_drv_list(PCFFUNCTION pFunction) { CFFUNCTION *temp=NULL; down_write(&drv_lock); //Check for empty dev list. if(!drv_list) { drv_list = pFunction; } else { //traverse to the end of the list. for(temp=drv_list;(temp->next);temp=temp->next); temp->next = pFunction; } pFunction->next = NULL; up_write(&drv_lock); return;}static PCFFUNCTION remove_drv_list(PCFFUNCTION pFunction){ CFFUNCTION *curr,*prev; curr = prev = NULL; down_write(&drv_lock); for(curr=prev=drv_list;(curr);prev=curr,curr=curr->next) { if(curr == pFunction) { if(curr!=prev) prev->next = curr->next; else drv_list = curr->next; break; } } up_write(&drv_lock); return curr;}/* * Create an instance of the card and register with Card Services. */static dev_link_t *ar6000_cs_attach(void){ struct ar6000_pccard *info; dev_link_t *link; client_reg_t client_reg; A_UINT32 ret; BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Enter - ar6000_cs_attach\n"); /* Create new device */ info = A_MALLOC(sizeof(struct ar6000_pccard)); if (!info) return NULL; A_MEMZERO(info, sizeof(*info)); link = &info->link; link->priv = info; /* Initialize the CF device structure */ info->CfDevice.backPtr = link; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; /* Insert into the global devlist */ insert_dev_list(info); /* Register with Card Services */ client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &ar6000_cs_event; client_reg.Version = 0x0210; /* FIXME: what does this mean? */ client_reg.event_callback_args.client_data = link; ret = pcmcia_register_client(&link->handle, &client_reg); if (ret != CS_SUCCESS) { ar6000_cs_error(link->handle, RegisterClient, ret); BUSDRV_DEBUG_PRINTF(ATH_LOG_ERR, "pcmcia register failed\n"); ar6000_cs_detach(link); return NULL; } BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Exit - ar6000_cs_attach\n"); return link;} /* ar6000_cs_attach *//* * Deregister with card services & free the device structure. */static voidar6000_cs_detach(dev_link_t * link){ CF_DEVICE *pCfDevice = NULL; BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Enter - ar6000_cs_detach\n"); /* Call the pRemove function of the top level driver */ pCfDevice = (CF_DEVICE *)&(((struct ar6000_pccard *)(link->priv))->CfDevice); if (pCfDevice->pFunction) { (*pCfDevice->pFunction->pRemove)(pCfDevice->pFunction, pCfDevice); pCfDevice->pFunction = NULL; } if (link->state & DEV_CONFIG) ar6000_cs_release(link); /* Unregister with Card Services */ if (link->handle) pcmcia_deregister_client(link->handle); /* Unlink device structure, and free it */ BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "ar6000_cs: detach: link=%p link->dev=%p\n", link, link->dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -