diff options
Diffstat (limited to 'drivers/sdio/stack/busdriver/sdio_function.c')
-rw-r--r-- | drivers/sdio/stack/busdriver/sdio_function.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/drivers/sdio/stack/busdriver/sdio_function.c b/drivers/sdio/stack/busdriver/sdio_function.c new file mode 100644 index 00000000000..78b8e17999a --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_function.c @@ -0,0 +1,715 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_function.c + +@abstract: OS independent bus driver support for function drivers + +@notes: This file supports the interface between SDIO function drivers and the bus driver. + +@notice: Copyright (c), 2004-2005 Atheros Communications, Inc. + + + * + * 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. + * + * Portions of this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (�Simplified + * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define MODULE_NAME SDBUSDRIVER +#include <linux/sdio/ctsystem.h> +#include <linux/sdio/sdio_busdriver.h> +#include <linux/sdio/sdio_lib.h> +#include "_busdriver.h" + +static SDIO_STATUS ProbeForDevice(PSDFUNCTION pFunction); + +#ifdef CT_MAN_CODE_CHECK +static UINT16 ManCodeCheck = CT_MAN_CODE_CHECK; +#endif + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Register a function driver with the bus driver. + + @function name: SDIO_RegisterFunction + @prototype: SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction) + @category: PD_Reference + @input: pFunction - the function definition structure. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when succesful. + + @notes: Each function driver must register with the bus driver once upon loading. + The calling function must be prepared to receive a Probe callback before + this function returns. This will occur when an perpheral device is already + pluugged in that is supported by this function. + The function driver should unregister itself when exiting. + The bus driver checks for possible function drivers to support a device + in reverse registration order. + + @example: Registering a function driver: + //list of devices supported by this function driver + static SD_PNP_INFO Ids[] = { + {.SDIO_ManufacturerID = 0xaa55, + .SDIO_ManufacturerCode = 0x5555, + .SDIO_FunctionNo = 1}, + {} //list is null termintaed + }; + static GENERIC_FUNCTION_CONTEXT FunctionContext = { + .Function.pName = "sdio_generic", //name of the device + .Function.Version = CT_SDIO_STACK_VERSION_CODE, // set stack version + .Function.MaxDevices = 1, //maximum number of devices supported by this driver + .Function.NumDevices = 0, //current number of devices, always zero to start + .Function.pIds = Ids, //the list of devices supported by this device + .Function.pProbe = Probe, //pointer to the function drivers Probe function + // that will be called when a possibly supported device + // is inserted. + .Function.pRemove = Remove, //pointer to the function drivers Remove function + / that will be called when a device is removed. + .Function.pContext = &FunctionContext, //data value that will be passed into Probe and + // Remove callbacks. + }; + SDIO_STATUS status; + status = SDIO_RegisterFunction(&FunctionContext.Function) + if (!SDIO_SUCCESS(status)) { + ...failed to register + } + + @see also: SDIO_UnregisterFunction + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_RegisterFunction(PSDFUNCTION pFunction) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + +#ifdef CT_MAN_CODE_CHECK + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver: _SDIO_RegisterFunction: WARNING, this version is locked to Memory cards and SDIO cards with JEDEC IDs of: 0x%X\n", + ManCodeCheck)); +#else + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: _SDIO_RegisterFunction\n")); +#endif + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: Function Driver Stack Version: %d.%d \n", + GET_SDIO_STACK_VERSION_MAJOR(pFunction),GET_SDIO_STACK_VERSION_MINOR(pFunction))); + + if (!CHECK_FUNCTION_DRIVER_VERSION(pFunction)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Function Major Version Mismatch (hcd = %d, bus driver = %d)\n", + GET_SDIO_STACK_VERSION_MAJOR(pFunction), CT_SDIO_STACK_VERSION_MAJOR(g_Version))); + return SDIO_STATUS_INVALID_PARAMETER; + } + + + /* sanity check the driver */ + if ((pFunction == NULL) || + (pFunction->pProbe == NULL) || + (pFunction->pIds == NULL)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterFunction, invalid registration data\n")); + return SDIO_STATUS_INVALID_PARAMETER; + } + /* protect the function list and add the function */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + SignalInitialize(&pFunction->CleanupReqSig); + SDLIST_INIT(&pFunction->DeviceList); + SDListAdd(&pBusContext->FunctionList, &pFunction->SDList); + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + + /* see if we have devices for this new function driver */ + ProbeForDevice(pFunction); + + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterFunction, error exit 0x%X\n", status)); + return status; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Unregister a function driver with the bus driver. + + @function name: SDIO_UnregisterFunction + @prototype: SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction) + @category: PD_Reference + + @input: pFunction - the function definition structure. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when succesful. + + @notes: Each function driver must unregister from the bus driver when the function driver + exits. + A function driver must disconnect from any interrupts before calling this function. + + @example: Unregistering a function driver: + SDIO_UnregisterFunction(&FunctionContext.Function); + + @see also: SDIO_RegisterFunction + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_UnregisterFunction(PSDFUNCTION pFunction) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDDEVICE pDevice; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: _SDIO_UnregisterFunction\n")); + + /* protect the function list and synchronize with Probe() and Remove()*/ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* remove this function from the function list */ + SDListRemove(&pFunction->SDList); + /* now remove this function as the handler for any of its devices */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pFunction->DeviceList, pDevice, SDDEVICE,FuncListLink) { + if (pDevice->pFunction == pFunction) { + /* notify removal */ + NotifyDeviceRemove(pDevice); + } + }SDITERATE_END; + + SignalDelete(&pFunction->CleanupReqSig); + + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: _SDIO_UnregisterFunction\n")); + return status; + +cleanup: + DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: _SDIO_UnregisterFunction, error exit 0x%X\n", status)); + return status; +} + +/* documentation headers only for Probe and Remove */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: This function is called by the Busdriver when a device is inserted that can be supported by this function driver. + + @function name: Probe + @prototype: BOOL (*pProbe)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice) + @category: PD_Reference + + @input: pFunction - the function definition structure that was passed to Busdriver + via the SDIO_RegisterFunction. + @input: pDevice - the description of the newly inserted device. + + @output: none + + @return: TRUE - this function driver will suport this device + FALSE - this function driver will not support this device + + @notes: The Busdriver calls the Probe function of a function driver to inform it that device is + available for the function driver to control. The function driver should initialize the + device and be pepared to acceopt any interrupts from the device before returning. + + @example: Example of typical Probe function callback: + static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + ...get the our context info passed into the SDIO_RegisterFunction + PSDXXX_DRIVER_CONTEXT pFunctionContext = + (PSDXXX_DRIVER_CONTEXT)pFunction->pContext; + SDIO_STATUS status; + //test the identification of this device and ensure we want to support it + // we can test based on class, or use more specific tests on SDIO_ManufacturerID, etc. + if (pDevice->pId[0].SDIO_FunctionClass == XXX) { + DBG_PRINT(SDDBG_TRACE, ("SDIO XXX Function: Probe - card matched (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + ... + + @see also: SDIO_RegisterFunction + @see also: Remove + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +BOOL FilterPnpInfo(PSDDEVICE pDevice) +{ +#ifdef CT_MAN_CODE_CHECK + if (pDevice->pId[0].CardFlags & CARD_SDIO) { + if (pDevice->pId[0].SDIO_ManufacturerCode != ManCodeCheck) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Card with JEDEC ID:0x%X , not Allowed! Driver check halted. " + "Please Contact sales@codetelligence.com.\n", + pDevice->pId[0].SDIO_ManufacturerCode)); + return FALSE; + } + } + return TRUE; +#else + return TRUE; +#endif +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: This function is called by the Busdriver when a device controlled by this function + function driver is removed. + + @function name: Remove + @prototype: void (*pRemove)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice) + @category: PD_Reference + + @input: pFunction - the function definition structure that was passed to Busdriver + via the SDIO_RegisterFunction. + @input: pDevice - the description of the device being removed. + + @output: none + + @return: none + + @notes: The Busdriver calls the Remove function of a function driver to inform it that device it + was supporting has been removed. The device has already been removed, so no further I/O + to the device can be performed. + + @example: Example of typical Remove function callback: + void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + // get the our context info passed into the SDIO_RegisterFunction + PSDXXX_DRIVER_CONTEXT pFunctionContext = + (PSDXXX_DRIVER_CONTEXT)pFunction->pContext; + ...free any acquired resources. + + @see also: SDIO_RegisterFunction + @see also: Probe + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/* + * ProbeForFunction - look for a function driver to handle this card + * +*/ +SDIO_STATUS ProbeForFunction(PSDDEVICE pDevice, PSDHCD pHcd) { + SDIO_STATUS status; + PSDLIST pList; + PSDFUNCTION pFunction; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: ProbeForFunction\n")); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction - Dump of Device PNP Data: \n")); + DBG_PRINT(SDDBG_TRACE, (" Card Flags 0x%X \n", pDevice->pId[0].CardFlags)); + if (pDevice->pId[0].CardFlags & CARD_SDIO) { + DBG_PRINT(SDDBG_TRACE, (" SDIO MANF: 0x%X \n", pDevice->pId[0].SDIO_ManufacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANFCODE: 0x%X \n", pDevice->pId[0].SDIO_ManufacturerCode)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncNo: %d \n", pDevice->pId[0].SDIO_FunctionNo)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncClass: %d \n", pDevice->pId[0].SDIO_FunctionClass)); + } + if (pDevice->pId[0].CardFlags & (CARD_MMC | CARD_SD)) { + DBG_PRINT(SDDBG_TRACE, (" SDMMC MANFID: 0x%X \n",pDevice->pId[0].SDMMC_ManfacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC OEMID: 0x%X \n",pDevice->pId[0].SDMMC_OEMApplicationID)); + } + + if (!FilterPnpInfo(pDevice)) { + status = SDIO_STATUS_SUCCESS; + goto cleanup; + } + + /* protect the function list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + + /* protect against ProbeForDevice */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->DeviceListSem)))) { + /* release the function list semaphore we just took */ + SemaphorePost(&pBusContext->FunctionListSem); + goto cleanup; + } + + if (pDevice->pFunction != NULL) { + /* device already has a function driver handling it */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction, device already has function\n")); + /* release function list */ + SemaphorePost(&pBusContext->DeviceListSem); + /* release function list */ + SemaphorePost(&pBusContext->FunctionListSem); + /* just return success */ + status = SDIO_STATUS_SUCCESS; + goto cleanup; + } + + /* release device list */ + SemaphorePost(&pBusContext->DeviceListSem); + + /* walk functions looking for one that can handle this device */ + SDITERATE_OVER_LIST(&pBusContext->FunctionList, pList) { + pFunction = CONTAINING_STRUCT(pList, SDFUNCTION, SDList); + if (pFunction->NumDevices >= pFunction->MaxDevices) { + /* function can't support any more devices */ + continue; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction - checking: %s \n", + pFunction->pName)); + + /* see if this function handles this device */ + if (IsPotentialIdMatch(pDevice->pId, pFunction->pIds)) { + if (!FilterPnpInfo(pDevice)) { + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction -Got Match, probing: %s \n", + pFunction->pName)); + /* we need to setup with the OS bus driver before the probe, so probe can + do OS operations. */ + OS_InitializeDevice(pDevice, pFunction); + if (!SDIO_SUCCESS(OS_AddDevice(pDevice, pFunction))) { + break; + } + /* close enough match, ask the function driver if it supports us */ + if (pFunction->pProbe(pFunction, pDevice)) { + /* she accepted the device, add to list */ + pDevice->pFunction = pFunction; + SDListAdd(&pFunction->DeviceList, &pDevice->FuncListLink); + pFunction->NumDevices++; + break; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: %s did not claim the device \n", + pFunction->pName)); + /* didn't take this device */ + OS_RemoveDevice(pDevice); + } + + } + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: ProbeForFunction\n")); + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: ProbeForFunction, error exit 0x%X\n", status)); + return status; +} + +/* + * ProbeForDevice - look for a device that this function driver supports + * +*/ +static SDIO_STATUS ProbeForDevice(PSDFUNCTION pFunction) { + SDIO_STATUS status; + PSDLIST pList; + PSDDEVICE pDevice; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice\n")); + if (pFunction->NumDevices >= pFunction->MaxDevices) { + /* function can't support any more devices */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, too many devices in function\n")); + return SDIO_STATUS_SUCCESS; + } + + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* walk device list */ + SDITERATE_OVER_LIST(&pBusContext->DeviceList, pList) { + pDevice = CONTAINING_STRUCT(pList, SDDEVICE, SDList); + if (pDevice->pFunction != NULL) { + /* device already has a function driver handling it */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, device already has function\n")); + continue; + } + /* see if this function handles this device */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, matching ID:%d %d class:%d\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_FunctionNo, + pDevice->pId[0].SDIO_FunctionClass)); + if (IsPotentialIdMatch(pDevice->pId, pFunction->pIds)) { + if (!FilterPnpInfo(pDevice)) { + break; + } + /* we need to setup with the OS bus driver before the probe, so probe can + do OS operations. */ + OS_InitializeDevice(pDevice, pFunction); + if (!SDIO_SUCCESS(OS_AddDevice(pDevice, pFunction))) { + break; + } + /* close enough match, ask the function driver if it supports us */ + if (pFunction->pProbe(pFunction, pDevice)) { + /* she accepted the device, add to list */ + pDevice->pFunction = pFunction; + SDListAdd(&pFunction->DeviceList, &pDevice->FuncListLink); + pFunction->NumDevices++; + break; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: %s did not claim the device \n", + pFunction->pName)); + /* didn't take this device */ + OS_RemoveDevice(pDevice); + } + } + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: ProbeForDevice, error exit 0x%X\n", status)); + return status; +} + +#if 0 +static void DumpPnpEntry(PSD_PNP_INFO pInfo) +{ + DBG_PRINT(SDDBG_TRACE, ("Function PnpInfo Dump: \n")); + DBG_PRINT(SDDBG_TRACE, (" Card Flags 0x%X \n", pInfo->CardFlags)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANF: 0x%X \n", pInfo->SDIO_ManufacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANFCODE: 0x%X \n", pInfo->SDIO_ManufacturerCode)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncNo: %d \n", pInfo->SDIO_FunctionNo)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncClass: %d \n", pInfo->SDIO_FunctionClass)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC MANFID: 0x%X \n", pInfo->SDMMC_ManfacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC OEMID: 0x%X \n", pInfo->SDMMC_OEMApplicationID)); +} +#endif +/* + * IsPotentialIdMatch - test for potential device match + * +*/ +BOOL IsPotentialIdMatch(PSD_PNP_INFO pIdsDev, PSD_PNP_INFO pIdsFuncList) { + PSD_PNP_INFO pTFn; + BOOL match = FALSE; + + for (pTFn = pIdsFuncList;!IS_LAST_SDPNPINFO_ENTRY(pTFn);pTFn++) { + //DumpPnpEntry(pTFn); + /* check specific SDIO Card manufacturer ID, Code and Function number */ + if ((pIdsDev->SDIO_ManufacturerID != 0) && + (pTFn->SDIO_ManufacturerID != 0) && + (pIdsDev->SDIO_ManufacturerID == pTFn->SDIO_ManufacturerID) && + (pIdsDev->SDIO_ManufacturerCode == pTFn->SDIO_ManufacturerCode) && + ((pIdsDev->SDIO_FunctionNo == pTFn->SDIO_FunctionNo) || + (pTFn->SDIO_FunctionNo == 0)) ) { + match = TRUE; + break; + } + /* check generic function class */ + if ((pIdsDev->SDIO_FunctionClass != 0) && + (pTFn->SDIO_FunctionClass != 0) && + (pIdsDev->SDIO_FunctionClass == pTFn->SDIO_FunctionClass)) { + match = TRUE; + break; + } + /* check specific SDMMC MANFID and APPLICATION ID, NOTE SANDISK + * uses a MANFID of zero! */ + if ((pTFn->SDMMC_OEMApplicationID != 0) && + (pIdsDev->SDMMC_ManfacturerID == pTFn->SDMMC_ManfacturerID) && + (pIdsDev->SDMMC_OEMApplicationID == pTFn->SDMMC_OEMApplicationID)) { + match = TRUE; + break; + } + + /* check generic SD Card */ + if ((pIdsDev->CardFlags & CARD_SD) && + (pTFn->CardFlags & CARD_SD)){ + match = TRUE; + break; + } + + /* check generic MMC Card */ + if ((pIdsDev->CardFlags & CARD_MMC) && + (pTFn->CardFlags & CARD_MMC)){ + match = TRUE; + break; + } + + /* check raw Card */ + if ((pIdsDev->CardFlags & CARD_RAW) && + (pTFn->CardFlags & CARD_RAW)){ + match = TRUE; + break; + } + } + + return match; +} + +/* + * NotifyDeviceRemove - tell function driver on this device that the device is being removed + * +*/ +SDIO_STATUS NotifyDeviceRemove(PSDDEVICE pDevice) { + SDIO_STATUS status; + SDREQUESTQUEUE cancelQueue; + PSDREQUEST pReq; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + InitializeRequestQueue(&cancelQueue); + + if ((pDevice->pFunction != NULL) && + (pDevice->pFunction->pRemove != NULL)){ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: removing device 0x%X\n", (INT)pDevice)); + /* fail any outstanding requests for this device */ + /* acquire lock for request queue */ + status = _AcquireHcdLock(pDevice->pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* mark the function to block any more requests comming down */ + pDevice->pFunction->Flags |= SDFUNCTION_FLAG_REMOVING; + /* walk through HCD queue and remove this function's requests */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pDevice->pHcd->RequestQueue.Queue, pReq, SDREQUEST, SDList) { + if (pReq->pFunction == pDevice->pFunction) { + /* cancel this request, as this device or function is being removed */ + /* note that these request are getting completed out of order */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: canceling req 0x%X\n", (UINT)pReq)); + pReq->Status = SDIO_STATUS_CANCELED; + /* remove it from the HCD queue */ + SDListRemove(&pReq->SDList); + /* add it to the cancel queue */ + QueueRequest(&cancelQueue, pReq); + } + }SDITERATE_END; + + status = _ReleaseHcdLock(pDevice->pHcd); + + /* now empty the cancel queue if anything is in there */ + while (TRUE) { + pReq = DequeueRequest(&cancelQueue); + if (NULL == pReq) { + break; + } + /* complete the request */ + DoRequestCompletion(pReq, pDevice->pHcd); + } + /* re-acquire the lock to deal with the current request */ + status = _AcquireHcdLock(pDevice->pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* now deal with the current request */ + pReq = GET_CURRENT_REQUEST(pDevice->pHcd); + if ((pReq !=NULL) && (pReq->pFunction == pDevice->pFunction) && (pReq->pFunction != NULL)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: Outstanding Req 0x%X on HCD: 0x%X.. waiting...\n", + (UINT)pReq, (UINT)pDevice->pHcd)); + /* the outstanding request on this device is for the function being removed */ + pReq->Flags |= SDREQ_FLAGS_CANCELED; + /* wait for this request to get completed normally */ + status = _ReleaseHcdLock(pDevice->pHcd); + SignalWait(&pDevice->pFunction->CleanupReqSig); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: Outstanding HCD Req 0x%X completed \n", (UINT)pReq)); + } else { + /* release lock */ + status = _ReleaseHcdLock(pDevice->pHcd); + } + + /* synchronize with ISR SYNC Handlers */ + status = SemaphorePendInterruptable(&pBusContext->DeviceListSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* call this devices Remove function */ + pDevice->pFunction->pRemove(pDevice->pFunction,pDevice); + pDevice->pFunction->NumDevices--; + /* make sure the sync handler is NULLed out */ + pDevice->pIrqFunction = NULL; + SemaphorePost(&pBusContext->DeviceListSem); + + OS_RemoveDevice(pDevice); + /* detach this device from the function list it belongs to */ + SDListRemove(&pDevice->FuncListLink); + pDevice->pFunction->Flags &= ~SDFUNCTION_FLAG_REMOVING; + pDevice->pFunction = NULL; + } + return SDIO_STATUS_SUCCESS; +} + + +/* + * RemoveHcdFunctions - remove all functions attached to an HCD + * +*/ +SDIO_STATUS RemoveHcdFunctions(PSDHCD pHcd) { + SDIO_STATUS status; + PSDLIST pList; + PSDFUNCTION pFunction; + PSDDEVICE pDevice; + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: RemoveHcdFunctions\n")); + + /* walk through the functions and remove the ones associated with this HCD */ + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePend(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* mark that card is being removed */ + pHcd->CardProperties.CardState |= CARD_STATE_REMOVED; + SDITERATE_OVER_LIST(&pBusContext->FunctionList, pList) { + pFunction = CONTAINING_STRUCT(pList, SDFUNCTION, SDList); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: scanning function 0x%X, %s\n", (INT)pFunction, + (pFunction == NULL)?"NULL":pFunction->pName)); + + /* walk the devices on this function and look for a match */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pFunction->DeviceList, pDevice, SDDEVICE,FuncListLink) { + if (pDevice->pHcd == pHcd) { + /* match, remove it */ + NotifyDeviceRemove(pDevice); + } + SDITERATE_END; + SDITERATE_END; + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: RemoveHcdFunctions\n")); + return SDIO_STATUS_SUCCESS; + +cleanup: + DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: RemoveHcdFunctions, error exit 0x%X\n", status)); + return status; +} + +/* + * RemoveAllFunctions - remove all functions attached + * +*/ +SDIO_STATUS RemoveAllFunctions() +{ + SDIO_STATUS status; + PSDLIST pList; + PSDHCD pHcd; + + /* walk through the HCDs */ + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePend(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + SDITERATE_OVER_LIST(&pBusContext->HcdList, pList) { + pHcd = CONTAINING_STRUCT(pList, SDHCD, SDList); + /* remove the functions */ + RemoveHcdFunctions(pHcd); + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + return SDIO_STATUS_SUCCESS; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: RemoveAllFunctions, error exit 0x%X\n", status)); + return status; +} + |