www.pudn.com > sdio-2.6.18-full.rar > 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#ifdef __IN_PCMCIA_PACKAGE__ #include #endif /* __IN_PCMCIA_PACKAGE__ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 DEBUG A_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 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 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) 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) 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 void ar6000_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 void ar6000_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); remove_dev_list(link->priv); A_FREE(link->priv); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Exit - ar6000_cs_detach\n"); } /* ar6000_cs_detach */ static irqreturn_t ar6000_interrupt(A_UINT32 irq, void *dev_id, struct pt_regs *regs) { struct ar6000_pccard *card = (struct ar6000_pccard *)dev_id; A_BOOL callDSR; //Call the Interrupt handler registered by the upper layer if (card->CfDevice.pFunction && card->CfDevice.pIrqFunction) (*(card->CfDevice.pIrqFunction))(card->CfDevice.IrqContext, &callDSR); /* Schedule a tasklet & return */ if (card->CfDevice.pFunction && card->CfDevice.pIrqFunction && callDSR) tasklet_hi_schedule(&card->CfDevice.tasklet); return IRQ_HANDLED; } #ifdef POLLED_MODE static void ar6000_interrupt_wrapper(unsigned long dev_id) { struct ar6000_pccard *card = (struct ar6000_pccard *)dev_id; //Call the Interrupt handler registered by the upper layer if (card->CfDevice.pIrqFunction) (*(card->CfDevice.pIrqFunction))(card->CfDevice.IrqContext); card->poll_timer.expires = jiffies + HZ; add_timer(&card->poll_timer); } #endif static A_UINT32 mem_speed = 0; /* * ar6000_cs_config() is scheduled to run after a CARD_INSERTION * event is received, to configure the PCMCIA socket, and to make the * device available to the system. */ static void ar6000_cs_config(dev_link_t *link) { client_handle_t handle = link->handle; struct ar6000_pccard *card = link->priv; A_UINT32 last_fn, last_ret; A_UCHAR buf[64]; config_info_t conf; cisinfo_t info; tuple_t tuple; cisparse_t parse; win_req_t req; memreq_t mem; A_UINT32 status; BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Enter ar6000_cs_config\n"); CS_CHECK(ValidateCIS, pcmcia_validate_cis(handle, &info)); /* * This reads the card's CONFIG tuple to find its * configuration registers. */ tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "link->conf.ConfigBase: %04x, \ link->conf.Present: %0x\n", link->conf.ConfigBase, link->conf.Present); /*Read the card's manf-id, manf-code, func no & func class * and initialize the device structure */ tuple.DesiredTuple = CISTPL_MANFID; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); card->CfDevice.pId.CF_ManufacturerID = parse.manfid.card; card->CfDevice.pId.CF_ManufacturerCode = parse.manfid.manf; /*TODO: change this tuple.DesiredTuple = CISTPL_FUNCNO; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); card->CfDevice.pId.CF_FunctionNo = parse.manfid.manf; */ /* Configure card */ link->state |= DEV_CONFIG; /* Look up the current Vcc */ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); link->conf.Vcc = conf.Vcc; /* * In this loop, we scan the CIS for configuration table * entries, each of which describes a valid card * configuration, including voltage, IO window, memory window, * and interrupt settings. * * We make no assumptions about the card to be configured: we * use just the information available in the CIS. In an ideal * world, this would work for any PCMCIA card, but it requires * a complete and accurate CIS. In practice, a driver usually * "knows" most of these things without consulting the CIS, * and most client drivers will only use the CIS to fill in * implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_cftable_entry_t dflt = { .index = 0 }; if (pcmcia_get_tuple_data(handle, &tuple) != 0 || pcmcia_parse_tuple(handle, &tuple, &parse) != 0) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) goto next_entry; link->conf.ConfigIndex = cfg->index; /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) { link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; } /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ /* if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { if (!ignore_cis_vcc) goto next_entry; } } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) { if(!ignore_cis_vcc) goto next_entry; } } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vpp1 = link->conf.Vpp2 = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vpp1 = link->conf.Vpp2 = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; */ /* Do we need to allocate an interrupt? */ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) link->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ link->io.NumPorts1 = link->io.NumPorts2 = 0; if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "io->nwin: %d, io->win[0].base: %d, io->win[0].len: %d\n", io->nwin, io->win[0].base, io->win[0].len); link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; if (!(io->flags & CISTPL_IO_8BIT)) link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; if (!(io->flags & CISTPL_IO_16BIT)) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; link->io.BasePort1 = io->win[0].base; link->io.NumPorts1 = io->win[0].len; if (io->nwin > 1) { link->io.Attributes2 = link->io.Attributes1; link->io.BasePort2 = io->win[1].base; link->io.NumPorts2 = io->win[1].len; } /* This reserves IO space but doesn't actually enable it */ if (pcmcia_request_io(link->handle, &link->io) != 0) goto next_entry; } break; next_entry: if (link->io.NumPorts1) pcmcia_release_io(link->handle, &link->io); last_ret = pcmcia_get_next_tuple(handle, &tuple); if (last_ret == CS_NO_MORE_ITEMS) { BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "GetNextTuple(). No matching CIS configuration, " "maybe you need the ignore_cis_vcc=1 parameter.\n"); goto cs_failed; } } /* * Allocate an interrupt line. Note that this does not assign * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) { link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = ar6000_interrupt; link->irq.Instance = card; #ifdef POLLED_MODE init_timer(&card->poll_timer); card->poll_timer.function = ar6000_interrupt_wrapper; card->poll_timer.data = (unsigned long)card; card->poll_timer.expires = jiffies + HZ; add_timer(&card->poll_timer); #else CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); card->CfDevice.irq = link->irq.AssignedIRQ; #endif } CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); /* * Allocate a small memory window. Note that the dev_link_t * structure provides space for one window handle -- if your * device needs several windows, you'll need to keep track of * the handles in your private data structure, link->priv. */ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; req.Base = req.Size = 0; req.AccessSpeed = mem_speed; status = pcmcia_request_window(&link->handle, &req, &link->win); if(status != CS_SUCCESS) { #ifdef KERNEL_2_4 ar6000_cs_error(link->handle, RequestWindow, status); #else cs_error(link->handle, RequestWindow, status); #endif goto cs_failed; } card->CfDevice.mem_start = (u_long)ioremap(req.Base, req.Size); card->CfDevice.mem_end = card->CfDevice.mem_start + req.Size; mem.CardOffset = 0; mem.Page = 0; status = pcmcia_map_mem_page(link->win, &mem); if(status != CS_SUCCESS) { #ifdef KERNEL_2_4 ar6000_cs_error(link->handle, MapMemPage, status); #else cs_error(link->handle, MapMemPage, status); goto cs_failed; #endif } card->node.major = card->node.minor = 0; /* At this point, the dev_node_t structure(s) needs to be * initialized and arranged in a linked list at link->dev. */ link->dev = &card->node; /* link->dev being non-NULL is also used to indicate that the net_device has been registered */ link->state &= ~DEV_CONFIG_PENDING; /* Finally, report what we've done */ BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "%s: index 0x%02x: Vcc %d.%d", card->node.dev_name, link->conf.ConfigIndex, link->conf.Vcc / 10, link->conf.Vcc % 10); if (link->conf.Vpp1) BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, ", Vpp %d.%d", link->conf.Vpp1 / 10, link->conf.Vpp1 % 10); if (link->conf.Attributes & CONF_ENABLE_IRQ) BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, ", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, ", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); if (link->io.NumPorts2) BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, " & 0x%04x-0x%04x", link->io.BasePort2, link->io.BasePort2 + link->io.NumPorts2 - 1); BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "\n"); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Exiting ar6000_cs_config\n"); return; cs_failed: ar6000_cs_error(link->handle, last_fn, last_ret); ar6000_cs_release(link); } /* ar6000_cs_config */ /* * Release the IO/irq resources assigned to this device. */ static void ar6000_cs_release(dev_link_t *link) { pcmcia_release_configuration(link->handle); if (link->io.NumPorts1) pcmcia_release_io(link->handle, &link->io); if (link->irq.AssignedIRQ) pcmcia_release_irq(link->handle, &link->irq); link->state &= ~DEV_CONFIG; } /* ar6000_cs_release */ static void HotPlugHdlr(void *data) { struct ar6000_pccard *card = dev_list->priv; PCF_PNP_INFO pnpPtr = NULL; CF_DEVICE *pCfDevice = &card->CfDevice; A_BOOL isProbed = FALSE; PCFFUNCTION pFunction = NULL; /* The above code assumes only 1 card is present. In the case of * multiple cards, we need to get the data ptr to a Queue and from that * Queue get the dev link. */ /* Hotplug */ for (pFunction = drv_list;(pFunction);pFunction = pFunction->next) { /*Determine the CfFunction that has the same PNP info * & call its probe function. */ for ( pnpPtr = pFunction->pIds; pnpPtr->CF_ManufacturerCode != 0 ; \ pnpPtr++ ) { BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "ar6000_cs_event manfid comp %04x, %04x\n", pCfDevice->pId.CF_ManufacturerID, pnpPtr->CF_ManufacturerID); if( pCfDevice->pId.CF_ManufacturerID == pnpPtr->CF_ManufacturerID ) { pCfDevice->pFunction = pFunction; /* delay here for the card to stabilize. BMI Comm. * times out otherwise. */ A_MDELAY(1000); if ( !((*pFunction->pProbe)(pFunction, pCfDevice)) ) { /* The device is not successfuly probed by this * fuction driver. disassociate them. */ pCfDevice->pFunction = NULL; } isProbed = TRUE; break; } } if (isProbed) break; } } /* * The card status event handler. Mostly, this schedules other stuff * to run after an event is received. */ static int ar6000_cs_event(event_t event, int priority, event_callback_args_t * args) { dev_link_t *link = args->client_data; A_UINT32 err = 0; BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "AR6000_CS Event Handler Enter\n"); switch (event) { case CS_EVENT_CARD_REMOVAL: BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Card Removal Event\n"); link->state &= ~DEV_PRESENT; break; case CS_EVENT_CARD_INSERTION: BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Card Insertion Event\n"); link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; ar6000_cs_config(link); /* Indicate device available to upper drv in a work task Asynchronously */ if (drv_list) schedule_work(&hotPlugTask); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: break; } BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "AR6000_CS Event Handler Exit\n"); return err; } /* ar6000_cs_event */ /********************************************************************/ /* Module initialization */ /********************************************************************/ /* Can't be declared "const" or the whole __initdata section will * become const */ static char version[] __initdata = "ar6000_cs.c 1.0"; #ifndef KERNEL_2_4 static struct pcmcia_driver ar6000_driver = { .owner = THIS_MODULE, .drv = { .name = "ar6000_cs", }, .attach = ar6000_cs_attach, .detach = ar6000_cs_detach, }; #endif static int __init init_ar6000_cs(void) { BUSDRV_DEBUG_PRINTF(ATH_LOG_INF, "%s\n", version); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Enter - init_ar6000_cs"); /* Init Mutex's */ init_rwsem(&dev_lock); init_rwsem(&drv_lock); /* We dont pass the data ptr now. Will be useful when multiple * devices are supported to pass a Queue to this */ INIT_WORK(&hotPlugTask, HotPlugHdlr, NULL); #ifdef KERNEL_2_4 return register_pccard_driver("ar6000_cs", &ar6000_cs_attach, &ar6000_cs_detach); #else return pcmcia_register_driver(&ar6000_driver); #endif BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Exit - init_ar6000_cs"); } static void __exit exit_ar6000_cs(void) { BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Enter - exit_ar6000_cs"); #ifdef KERNEL_2_4 unregister_pccard_driver("ar6000_cs"); #else pcmcia_unregister_driver(&ar6000_driver); #endif //down_read(&dev_lock); while (dev_list != NULL) { if (dev_list->state & DEV_CONFIG) ar6000_cs_release(dev_list); ar6000_cs_detach(dev_list); } //up_read(&dev_lock); BUSDRV_DEBUG_PRINTF(ATH_DEBUG_TRC, "Exit - exit_ar6000_cs"); } module_init(init_ar6000_cs); module_exit(exit_ar6000_cs); #ifdef KERNEL_2_4 MODULE_PARM(debugbusdrv, "i"); #else module_param(debugbusdrv, int, 0644); #endif EXPORT_SYMBOL(CF_RegisterFunction); EXPORT_SYMBOL(CF_UnregisterFunction); EXPORT_SYMBOL(CF_SetIrqHandler); EXPORT_SYMBOL(CF_BusRequest_Word); EXPORT_SYMBOL(CF_BusRequest_Byte);