www.pudn.com > sdio-2.6.18-full.rar > sdio_memory_os.c
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @file: sdio_memory_os.c @abstract: Linux implementation module for SDIO MMC and SD nenory cards driver #notes: includes module load and unload functions @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 o 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 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* debug level for this module*/ #define DBG_DECLARE 3; #include#include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) #include #endif #include #include #include #include #include #include "sdio_memory.h" #include "sdio_memory_linux.h" #define DESCRIPTION "SDIO MMC/SD memory card Driver" #define AUTHOR "Atheros Communications, Inc." /* debug print parameter */ module_param(debuglevel, int, 0644); MODULE_PARM_DESC(debuglevel, "debuglevel 0-7, controls debug prints"); BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice); void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice); static SDIO_STATUS CreateDisk(PSDIO_MEMORY_CONTEXT pDriverContext, PSDIO_MEMORY_INSTANCE pInstance); static void DiskRequest(request_queue_t *pQueue); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) static void DiskRequestDma(request_queue_t *pQueue); static char* ReplaceChar(char* pStr, char Find, char Replace); static int CheckDeviceChange(struct gendisk *disk); #else /* 2.4 */ static int CheckDeviceChange(kdev_t kdev); static int Revalidate(kdev_t i_rdev); #endif static int DeviceIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static void DeleteDisk(PSDMEMORY_CONFIG pInstance); static int Open(struct inode *inode, struct file *filp); static int Release(struct inode *inode, struct file *filp); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* 2.4, only support 1 device */ #define MEM_SHIFT 3 static int temp_major; #define MAJOR_NR temp_major #define DEVICE_NR(device) (MINOR(device) >> MEM_SHIFT) #define DEVICE_NAME SDIO_MEMORY_BASE #define DEVICE_NO_RANDOM #define DEVICE_REQUEST DiskRequest #include //??static char GenDiskFlags = GENHD_FL_REMOVABLE; static char GenDiskFlags = 0; static char GenDiskMajorName[16]; static int GenSize[SDIO_MEMORY_MAX_PARTITIONS]; static int GenSize_size[SDIO_MEMORY_MAX_PARTITIONS]; static int GenHardsects[SDIO_MEMORY_MAX_PARTITIONS]; static int GenMaxsects[SDIO_MEMORY_MAX_PARTITIONS]; #ifdef CONFIG_DEVFS static devfs_handle_t devfs_handle; #endif #endif /* devices we support, null terminated */ static SD_PNP_INFO Ids[] = { //??{.SDMMC_ManfacturerID = 0x00, /* specific MMC card */ //?? .SDMMC_OEMApplicationID = 0x0002}, {.CardFlags = CARD_SD}, /* all SD cards */ {.CardFlags = CARD_MMC}, /* all MMC cards */ {} }; /* driver wide context data */ SDIO_MEMORY_CONTEXT DriverContext = { .Driver.Major = SDIO_MEMORY_MAJOR, .Function.pName = "sdio_mem", .Function.Version = CT_SDIO_STACK_VERSION_CODE, #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) .Function.MaxDevices = SDIO_MEMORY_MAX_DEVICES, #else .Function.MaxDevices = 1, #endif .Function.NumDevices = 0, .Function.pIds = Ids, .Function.pProbe = Probe, .Function.pRemove = Remove, .Function.pSuspend = NULL, .Function.pResume = NULL, .Function.pWake = NULL, .Function.pContext = &DriverContext, }; /* supported I/O operations*/ static struct block_device_operations driver_ops = { .owner = THIS_MODULE, .open = Open, .release = Release, .ioctl = DeviceIoctl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) .media_changed = CheckDeviceChange, #else /* 2.4 */ .check_media_change = CheckDeviceChange, .revalidate = Revalidate, #endif }; /* per scatter-gather request */ typedef struct _REQUEST_CONTEXT { PSDIO_MEMORY_INSTANCE pInstance; /* this device */ struct request * pOSRequest; /* the request we are working on */ UINT SectorsToTransfer; /* the number of sectors transferred */ UINT SGcount; /* number of scatter-gather entries */ SDDMA_DESCRIPTOR SGlist[0]; /* variable length of scatter gather entries */ }REQUEST_CONTEXT, *PREQUEST_CONTEXT; /* * Probe - a device potentially for us */ BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { PSDIO_MEMORY_CONTEXT pDriverContext = (PSDIO_MEMORY_CONTEXT)pFunction->pContext; BOOL accept = FALSE; PSDIO_MEMORY_INSTANCE pNewInstance = NULL; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - enter\n")); /* make sure we can handle this device type */ if ((pDevice->pId[0].CardFlags & CARD_SD) && (pDevice->pId[0].SDIO_FunctionNo == 0)) { /* we check against a zero SDIO function number to make sure this is not an SDIO function * on a combo card which will also have CARD_SD set as well. SDIO functions start * with 1*/ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - SD Card Type Match (MANF:0x%X, OEMID:0x%X) \n", pDevice->pId[0].SDMMC_ManfacturerID, pDevice->pId[0].SDMMC_OEMApplicationID)); } else if (pDevice->pId[0].CardFlags & CARD_MMC) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - MMC Card Type Match (MANF:0x%X, OEMID:0x%X) \n", pDevice->pId[0].SDMMC_ManfacturerID, pDevice->pId[0].SDMMC_OEMApplicationID)); } else { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - not ours \n")); return FALSE; } do { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - creating instance \n")); pNewInstance = CreateDeviceInstance(pDriverContext, pDevice); if (NULL == pNewInstance) { break; } atomic_set(&pNewInstance->Config.OpenCount, 0); /* add it to the list */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - adding instance \n")); AddDeviceInstance(pDriverContext, pNewInstance); /* get the card info */ GetCardCSD(pDevice, pNewInstance, (pDevice->pId[0].CardFlags & CARD_MMC)); /* create an OS disk for this device */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - creating disk \n")); if (!SDIO_SUCCESS(CreateDisk(pDriverContext, pNewInstance))) { /* failed */ break; } accept = TRUE; break; } while (FALSE); if (!accept && (pNewInstance != NULL)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Probe - deleting instance \n")); DeleteInstance(pDriverContext, pNewInstance); } return accept; } /* * Remove - our device is being removed */ void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) { PSDIO_MEMORY_CONTEXT pDriverContext = (PSDIO_MEMORY_CONTEXT)pFunction->pContext; PSDIO_MEMORY_INSTANCE pInstance; DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: Remove\n")); pInstance = FindInstance(pDriverContext, pDevice); if (pInstance != NULL) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function:: Removing instance: 0x%X From Remove() \n", (INT)pInstance)); DeleteDisk(&pInstance->Config); DeleteInstance(pDriverContext, pInstance); } else { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function:: could not find matching instance! \n")); } DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: Remove\n")); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* 2.4 */ static struct gendisk* alloc_disk(int minor) { struct gendisk* pDisk; int len; pDisk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); if (pDisk == NULL) { return NULL; } memset(pDisk, 0, sizeof(struct gendisk)); len = (minor-1) * sizeof(struct hd_struct *); pDisk->part = kmalloc(len, GFP_KERNEL); if (pDisk->part == NULL) { kfree(pDisk); return NULL; } memset(pDisk->part, 0, len); pDisk->max_p = minor; pDisk->minor_shift = MEM_SHIFT; //?????partition inits? return pDisk; } static int add_disk(PSDIO_MEMORY_INSTANCE pInstance, struct gendisk *pDisk) { struct hd_struct* pPartitions; /* create the partitions */ pPartitions = kmalloc(SDIO_MEMORY_MAX_PARTITIONS * sizeof(struct hd_struct), GFP_KERNEL); if (pPartitions == NULL) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: add_disk, no memory\n")); return -ENOMEM;; } memset(pPartitions , 0, SDIO_MEMORY_MAX_PARTITIONS * sizeof(struct hd_struct)); pPartitions[0].nr_sects = (GenSize[0] * 1024) / pInstance->FileSysBlockSize; pDisk->part = pPartitions; pDisk->nr_real = 1; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: register_disk, nr_sects: %d, GenSize[0]: %d \n", (UINT)pPartitions[0].nr_sects, (UINT)GenSize[0])); register_disk(pDisk, 0, SDIO_MEMORY_MAX_PARTITIONS, pDisk->fops, pPartitions[0].nr_sects); return 0; } #endif /* * CreateDisk - create the disk that represenst this device */ static SDIO_STATUS CreateDisk(PSDIO_MEMORY_CONTEXT pDriverContext, PSDIO_MEMORY_INSTANCE pInstance) { DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: CreateDisk\n")); pInstance->Config.pGenDisk = alloc_disk(SDIO_MEMORY_MAX_PARTITIONS); if (pInstance->Config.pGenDisk == NULL) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: CreateDisk - cannot allocate disk\n")); return SDIO_STATUS_NO_RESOURCES; } spin_lock_init(&pInstance->Config.RequestLock); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) if ((SDGET_DMA_DESCRIPTION(pInstance->pDevice) != NULL) && ((SDGET_DMA_DESCRIPTION(pInstance->pDevice)->Flags & SDDMA_DESCRIPTION_FLAG_DMA) || (SDGET_DMA_DESCRIPTION(pInstance->pDevice)->Flags & SDDMA_DESCRIPTION_FLAG_SGDMA))){ pInstance->Config.pRequestQueue = blk_init_queue(DiskRequestDma, &pInstance->Config.RequestLock); } else { pInstance->Config.pRequestQueue = blk_init_queue(DiskRequest, &pInstance->Config.RequestLock); } #else /* 2.4 */ pInstance->Config.pRequestQueue = BLK_DEFAULT_QUEUE(pDriverContext->Driver.Major); blk_init_queue(pInstance->Config.pRequestQueue, DiskRequest); #endif if (pInstance->Config.pRequestQueue == NULL) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: CreateDisk - cannot allocate queue\n")); /* get rid of our reference from alloc_disk() */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) put_disk(pInstance->Config.pGenDisk); #endif return SDIO_STATUS_NO_RESOURCES; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) /* not sure if we need to set this, limit the number of blocks per transfer */ blk_queue_max_sectors(pInstance->Config.pRequestQueue, pInstance->pDevice->pHcd->CardProperties.OperBlockCountLimit); #endif DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: block size %d\n", pInstance->BlockSize)); /* force the file system to use at least a 512 block, seems to break when smaller */ pInstance->FileSysBlockSize = (pInstance->BlockSize < 512) ? 512 : pInstance->BlockSize; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: setting block size %d\n", pInstance->FileSysBlockSize)); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) blk_queue_hardsect_size(pInstance->Config.pRequestQueue, pInstance->FileSysBlockSize); if (SDGET_DMA_DESCRIPTION(pInstance->pDevice) != NULL) { PSDDMA_DESCRIPTION pDmaDescrp = SDGET_DMA_DESCRIPTION(pInstance->pDevice); if ((pDmaDescrp->Flags & SDDMA_DESCRIPTION_FLAG_DMA) || (pDmaDescrp->Flags & SDDMA_DESCRIPTION_FLAG_SGDMA)){ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: setting up DMA\n")); /* setup the DMA parameters */ blk_queue_dma_alignment(pInstance->Config.pRequestQueue, pDmaDescrp->AddressAlignment); blk_queue_bounce_limit(pInstance->Config.pRequestQueue, pDmaDescrp->Mask); blk_queue_max_segment_size(pInstance->Config.pRequestQueue, pDmaDescrp->MaxBytesPerDescriptor); /* setup the DMA scatter gather parameters */ blk_queue_max_phys_segments(pInstance->Config.pRequestQueue, pDmaDescrp->MaxDescriptors); blk_queue_max_hw_segments(pInstance->Config.pRequestQueue, pDmaDescrp->MaxDescriptors); } } pInstance->Config.pGenDisk->major = pDriverContext->Driver.Major; /* minor number is incremented for each slot plus the max number of partitions on a disk */ pInstance->Config.pGenDisk->first_minor = SDIO_MEMORY_MAX_PARTITIONS * pInstance->pDevice->pHcd->SlotNumber; pInstance->Config.pGenDisk->fops = &driver_ops; pInstance->Config.pGenDisk->private_data = pInstance; pInstance->Config.pGenDisk->flags= GENHD_FL_REMOVABLE; sprintf(pInstance->Config.pGenDisk->disk_name, SDIO_MEMORY_BASE "%d", pDriverContext->Function.NumDevices); #ifdef CONFIG_DEVFS snprintf(pInstance->Config.pGenDisk->devfs_name,sizeof(pInstance->Config.pGenDisk->devfs_name), "sdmem_%s", (SD_GET_OS_DEVICE(pInstance->pDevice))->bus_id); /* remove any colons */ ReplaceChar(pInstance->Config.pGenDisk->devfs_name, ':', '_'); pInstance->Config.pGenDisk->driverfs_dev = SD_GET_OS_DEVICE(pInstance->pDevice); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: CreateDisk: devfs_name: %s, 0x%X\n", pInstance->Config.pGenDisk->devfs_name, (INT)pInstance->Config.pGenDisk->driverfs_dev)); #endif set_capacity(pInstance->Config.pGenDisk, pInstance->Size/pInstance->FileSysBlockSize * 1024); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: CreateDisk: size %d (Size %d, FileSysBlockSize %d)\n", pInstance->Size/pInstance->FileSysBlockSize * 1024, (INT)pInstance->Size, (INT)pInstance->FileSysBlockSize)); pInstance->Config.pGenDisk->queue = pInstance->Config.pRequestQueue ; /* is it read only ? */ set_disk_ro(pInstance->Config.pGenDisk, pInstance->WriteProtected); add_disk(pInstance->Config.pGenDisk); DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: CreateDisk major: %d, minors: %d, first_minor: %d\n", pInstance->Config.pGenDisk->major, pInstance->Config.pGenDisk->minors, pInstance->Config.pGenDisk->first_minor)); #else /* 2.4 */ pInstance->Config.pGenDisk->major = pDriverContext->Driver.Major; pInstance->Config.pGenDisk->fops = &driver_ops; pInstance->Config.pGenDisk->flags= &GenDiskFlags; sprintf(GenDiskMajorName, SDIO_MEMORY_BASE "%d", pDriverContext->Function.NumDevices); pInstance->Config.pGenDisk->major_name = GenDiskMajorName; { int ii; for (ii = 0; ii < SDIO_MEMORY_MAX_PARTITIONS; ii++) { //?? GenSize[ii] = pInstance->Size; /* * 1024, already in kbytes*/ GenSize_size[ii] = pInstance->FileSysBlockSize; GenHardsects[ii] = pInstance->FileSysBlockSize; GenMaxsects[ii] = pInstance->pDevice->pHcd->CardProperties.OperBlockCountLimit; } memset(GenSize, 0, sizeof(GenSize)); GenSize[0] = pInstance->Size; /* * 1024, already in kbytes*/ pInstance->Config.pGenDisk->sizes = GenSize; blk_size[pDriverContext->Driver.Major] = pInstance->Config.pGenDisk->sizes; blksize_size[pDriverContext->Driver.Major] = GenSize_size; hardsect_size[pDriverContext->Driver.Major] = GenHardsects; max_sectors[pDriverContext->Driver.Major] = GenMaxsects; } read_ahead[pDriverContext->Driver.Major] = 2; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: CreateDisk: size %d (Size %d, FileSysBlockSize %d) GenSize[0]: %d\n", pInstance->Size/pInstance->FileSysBlockSize * 1024, (INT)pInstance->Size, (INT)pInstance->FileSysBlockSize, GenSize[0])); /* is it read only ? */ //?? set_disk_ro(pInstance->Config.pGenDisk, pInstance->WriteProtected); add_disk(pInstance, pInstance->Config.pGenDisk); DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: CreateDisk major: %d, max_p: %d\n", pInstance->Config.pGenDisk->major, pInstance->Config.pGenDisk->max_p)); #endif DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: CreateDisk\n")); return SDIO_STATUS_SUCCESS; } /* * DeleteDisk - remove the disk */ static void DeleteDisk(PSDMEMORY_CONFIG pConfig) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeleteDisk\n")); del_gendisk(pConfig->pGenDisk); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) pConfig->pGenDisk->queue = NULL; //??? put_disk(pConfig->pGenDisk); blk_cleanup_queue(pConfig->pRequestQueue); #else /* 2.4 */ { int ii; int major = DriverContext.Driver.Major; /* flush the devices */ for (ii = 0; ii < SDIO_MEMORY_MAX_PARTITIONS; ii++) { fsync_dev(MKDEV(major, ii)); } #ifdef CONFIG_DEVFS devfs_register_partitions(pConfig->pGenDisk, 0, 1); if (devfs_handle) { devfs_unregister(devfs_handle); } devfs_handle = NULL; #endif invalidate_device (MKDEV(DriverContext.Driver.Major,0), 1); blk_cleanup_queue(BLK_DEFAULT_QUEUE(major)); read_ahead[major] = 0; blk_size[major] = NULL; if (pConfig->pGenDisk->part != NULL) { kfree(pConfig->pGenDisk->part); } blksize_size[major] = NULL; del_gendisk(pConfig->pGenDisk); } #endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* 2.4 */ static int Revalidate(kdev_t i_rdev) { int index; int max_p; int ii; int start; PSDIO_MEMORY_INSTANCE pInstance = GetFirstInstance(&DriverContext); index = DEVICE_NR(i_rdev); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: Revalidate: %d \n", index)); max_p = pInstance->Config.pGenDisk->max_p; start = index << MEM_SHIFT; for (ii = max_p - 1 ; ii >= 0 ; ii--) { int item = start + ii; invalidate_device(MKDEV(DriverContext.Driver.Major, item),1); pInstance->Config.pGenDisk->part[item].start_sect = 0; pInstance->Config.pGenDisk->part[item].nr_sects = 0; } register_disk(pInstance->Config.pGenDisk, i_rdev, 1 << MEM_SHIFT, pInstance->Config.pGenDisk->fops, (pInstance->Size * 1024) / pInstance->FileSysBlockSize); return 0; } #endif /* * Open - open the device */ static int Open(struct inode *inode, struct file *filp) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) PSDIO_MEMORY_INSTANCE pInstance = (PSDIO_MEMORY_INSTANCE)inode->i_bdev->bd_disk->private_data; #else /* 2.4 */ PSDIO_MEMORY_INSTANCE pInstance = GetFirstInstance(&DriverContext); #endif DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: Open\n")); atomic_inc(&pInstance->Config.OpenCount); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) check_disk_change(inode->i_bdev); #else check_disk_change(inode->i_rdev); /* 2.4 */ #endif DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: Open\n")); return 0; } /* * Release - handle close */ static int Release(struct inode *inode, struct file *filp) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) PSDIO_MEMORY_INSTANCE pInstance = (PSDIO_MEMORY_INSTANCE)inode->i_bdev->bd_disk->private_data; #else /* 2.4 */ PSDIO_MEMORY_INSTANCE pInstance = GetFirstInstance(&DriverContext); #endif DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: Release\n")); atomic_dec(&pInstance->Config.OpenCount); DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: Release\n")); return 0; } /* * DiskRequest - process a user request */ static void DiskRequest(request_queue_t *pQueue) { struct request *pRequest; PSDIO_MEMORY_INSTANCE pInstance; DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: DiskRequest\n")); /* for each request in queue */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) while ((pRequest = elv_next_request(pQueue)) != NULL) { if (!blk_fs_request(pRequest)) { /* not a command we care about */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequest - unsupported command: flags 0x%X\n", (UINT)pRequest->flags)); end_request(pRequest, 0); continue; } pInstance = (PSDIO_MEMORY_INSTANCE)pRequest->rq_disk->private_data; /* we don't need the queue spinlock while processing the head of the queue */ spin_unlock_irq(&pInstance->Config.RequestLock); if (SDIO_SUCCESS(MemoryTransfer(pInstance, pRequest->sector, pRequest->current_nr_sectors, pRequest->buffer, rq_data_dir(pRequest)))) { #else /* 2.4 */ #define rq_data_dir(r) ((r)->cmd == WRITE) spin_unlock_irq(&io_request_lock); pInstance = GetFirstInstance(&DriverContext); spin_lock_irq(&pInstance->Config.RequestLock); while(TRUE) { INIT_REQUEST; pRequest = CURRENT; /* we don't need the queue spinlock while processing the head of the queue */ spin_unlock_irq(&io_request_lock); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequest -sector:%d, nr_sectors: %d, hard_sector: %d, hard_nr_sectors: %d, \n nr_segments: %d, nr_hw_segments: %d, current_nr_sectors: %d, hard_cur_sectors: %d, start_sect: %d, minor: %d\n", (UINT)pRequest->sector, (UINT)pRequest->nr_sectors, (UINT)pRequest->hard_sector, (UINT)pRequest->hard_nr_sectors, (UINT)pRequest->nr_segments, (UINT)pRequest->nr_hw_segments, (UINT)pRequest->current_nr_sectors, (UINT)pRequest->hard_cur_sectors, (UINT)pInstance->Config.pGenDisk->part[MINOR(pRequest->rq_dev)].start_sect, MINOR(pRequest->rq_dev))); if (SDIO_SUCCESS(MemoryTransfer(pInstance, pRequest->sector+pInstance->Config.pGenDisk->part[MINOR(pRequest->rq_dev)].start_sect, pRequest->current_nr_sectors, pRequest->buffer, rq_data_dir(pRequest)))) { #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) spin_lock_irq(&pInstance->Config.RequestLock); end_request(pRequest, 1); #else /* 2.4 */ spin_lock_irq(&io_request_lock); end_request(1); #endif } else { DBG_PRINT(SDDBG_WARN, ("SDIO Memory Function: DiskRequest - failing request\n")); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) spin_lock_irq(&pInstance->Config.RequestLock); end_request(pRequest, 0); #else /* 2.4 */ spin_lock_irq(&io_request_lock); end_request(0); #endif } } DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: DiskRequest\n")); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) /* * AsyncCompletion - asynch I/O completion routine */ void AsyncCompletion(struct _SDREQUEST *pRequest) { PREQUEST_CONTEXT pRegContext = (PREQUEST_CONTEXT)pRequest->pCompleteContext; PSDIO_MEMORY_INSTANCE pInstance = pRegContext->pInstance; struct request * pOSRequest = pRegContext->pOSRequest; UINT sectorsTransferred = pRegContext->SectorsToTransfer; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: AsyncCompletion: status: %d\n", pRequest->Status)); if (DBG_GET_DEBUG_LEVEL() >= SDDBG_DUMP) { SDLIB_PrintBuffer(pOSRequest->buffer, sectorsTransferred* pInstance->BlockSize, "SDIO Memory Function: AsyncCompletion"); } KernelFree(pRegContext); SDDeviceFreeRequest(pInstance->pDevice, pRequest); /* now deal with the disk request */ spin_lock_irq(&pInstance->Config.RequestLock); do { if (SDIO_SUCCESS(pRequest->Status)) { if (end_that_request_first(pOSRequest, 1, sectorsTransferred)) { /* more to do */ DiskRequestDma(pInstance->Config.pRequestQueue); break; } } else { if (end_that_request_first(pOSRequest, 0, sectorsTransferred)) { /* more to do */ DiskRequestDma(pInstance->Config.pRequestQueue); break; } } blkdev_dequeue_request(pOSRequest); end_that_request_last(pOSRequest, 0); } while (FALSE); spin_unlock_irq(&pInstance->Config.RequestLock); } /* * AsyncCompletionLast - asynch I/O completion routine, handles last of series of requests */ void AsyncCompletionLast(struct _SDREQUEST *pRequest) { PREQUEST_CONTEXT pRegContext = (PREQUEST_CONTEXT)pRequest->pCompleteContext; PSDIO_MEMORY_INSTANCE pInstance = pRegContext->pInstance; struct request * pOSRequest = pRegContext->pOSRequest; UINT sectorsTransferred = pRegContext->SectorsToTransfer; DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: AsyncCompletionLast: status: %d\n", pRequest->Status)); if (DBG_GET_DEBUG_LEVEL() >= SDDBG_DUMP) { SDLIB_PrintBuffer(pOSRequest->buffer, sectorsTransferred * pInstance->BlockSize, "SDIO Memory Function: AsyncCompletion"); } KernelFree(pRegContext); SDDeviceFreeRequest(pInstance->pDevice, pRequest); /* now deal with the disk request */ spin_lock_irq(&pInstance->Config.RequestLock); do { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: completed sectors :%d nr_sectors: %d \n", (UINT)sectorsTransferred, (UINT)pOSRequest->nr_sectors)); if (SDIO_SUCCESS(pRequest->Status)) { if (end_that_request_first(pOSRequest, 1, sectorsTransferred)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: end_that_request_first not done!\n")); /* more to do */ DiskRequestDma(pInstance->Config.pRequestQueue); break; } } else { if (end_that_request_first(pOSRequest, 0, sectorsTransferred)) { /* more to do */ DiskRequestDma(pInstance->Config.pRequestQueue); break; } } DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: current disk request done!\n")); blkdev_dequeue_request(pOSRequest); end_that_request_last(pOSRequest, 0); /* look for more requests */ DiskRequestDma(pInstance->Config.pRequestQueue); } while (FALSE); spin_unlock_irq(&pInstance->Config.RequestLock); DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: AsyncCompletionLast\n")); } /* * DiskRequest - process a user request via DMA */ static void DiskRequestDma(request_queue_t *pQueue) { struct request *pRequest; PSDIO_MEMORY_INSTANCE pInstance; UINT MaxDescriptors; SDIO_STATUS status; INT outstandingReq = 1; /* for now, just queue one at a time */ UINT32 checkLength; PREQUEST_CONTEXT pContext; DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: DiskRequestDma\n")); /* for each request in queue */ while ((outstandingReq-- > 0) && ((pRequest = elv_next_request(pQueue)) != NULL)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma : processing block request :0x%X\n",(UINT32)pRequest)); if (!blk_fs_request(pRequest)) { /* not a command we care about */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - unsupported command: flags 0x%X\n", (UINT)pRequest->flags)); end_request(pRequest, 0); /* reset */ outstandingReq = 1; continue; } pInstance = (PSDIO_MEMORY_INSTANCE)pRequest->rq_disk->private_data; /* we don't need the queue spinlock while processing the head of the queue */ spin_unlock_irq(&pInstance->Config.RequestLock); /* allocate a context for ASYNC requests */ pContext = KernelAlloc(sizeof(REQUEST_CONTEXT) + ((sizeof(SDDMA_DESCRIPTOR))*pRequest->nr_phys_segments)); if (pContext == NULL) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: DiskRequestDma - no memory, failing request\n")); spin_lock_irq(&pInstance->Config.RequestLock); end_request(pRequest, 0); /* exit */ break; } pContext->pOSRequest = pRequest; pContext->pInstance = pInstance; DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - Phys Segments %d, HW Segments:%d, current_nr_sectors:%d, nr_sectors:%d , hard_nr_sectors:%d \n", pRequest->nr_phys_segments, pRequest->nr_hw_segments, pRequest->current_nr_sectors, (UINT32)pRequest->nr_sectors, (UINT32)pRequest->hard_nr_sectors)); if (pInstance->FileSysBlockSize != pInstance->BlockSize) { /* incompatible block size, use PIO mode, asynchronously */ DO_DIO: pContext->SectorsToTransfer = pRequest->current_nr_sectors; status = IssueAsyncTransfer(pInstance->pDevice, pInstance, pRequest->sector * pInstance->FileSysBlockSize, pContext->SectorsToTransfer*pInstance->FileSysBlockSize, rq_data_dir(pRequest), pRequest->buffer, 0, (outstandingReq > 0) ? AsyncCompletion : AsyncCompletionLast, pContext); } else { /* try DMA */ int ii; UINT32 align = SDGET_DMA_DESCRIPTION(pInstance->pDevice)->AddressAlignment; UINT32 lenAlign = SDGET_DMA_DESCRIPTION(pInstance->pDevice)->LengthAlignment; /* process as a DMA */ if (!((SDGET_DMA_DESCRIPTION(pInstance->pDevice)->Flags) & SDDMA_DESCRIPTION_FLAG_SGDMA)) { /* single block DMA */ DBG_ASSERT_WITH_MSG((pRequest->nr_phys_segments == 1), "SDIO Memory Function: DiskRequestDma, invalid SG size") } /* transfer all sectors */ pContext->SectorsToTransfer = pRequest->nr_sectors; /* get the scatter gather mapping */ MaxDescriptors = blk_rq_map_sg(pQueue, pRequest, pContext->SGlist); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - SG entries: %d, sector start: %d, seccnt: %d\n", MaxDescriptors, (UINT)pRequest->sector, pContext->SectorsToTransfer)); DBG_ASSERT_WITH_MSG((MaxDescriptors > 0), "SDIO Memory Function: DiskRequestDma, zero descriptors in request") checkLength = 0; /* check DMA restrictions */ for (ii = 0; ii < MaxDescriptors; ii++) { DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - SG Index:%d, page: 0x%X, length: %d, offset: 0x%X\n", ii, (UINT)pContext->SGlist[ii].page, pContext->SGlist[ii].length, pContext->SGlist[ii].offset)); /* check address alignment */ if (pContext->SGlist[ii].offset & align) { /* we have some illegal bits here, not a supportable address boundary, go PIO*/ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - punting to direct IO, offset: 0x%X, alignment: 0x%X\n", pContext->SGlist[ii].offset, align)); goto DO_DIO; } /* check length alignement */ if (pContext->SGlist[ii].length & lenAlign) { /* we have some illegal bits here, not a supportable length go PIO*/ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - punting to direct IO, Length: 0x%X (%d bytes), Length Alignment: 0x%X\n", pContext->SGlist[ii].length, pContext->SGlist[ii].length, lenAlign)); goto DO_DIO; } /* we are all good here, add the length */ checkLength += pContext->SGlist[ii].length; } if (checkLength != pContext->SectorsToTransfer*pInstance->FileSysBlockSize) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: DiskRequestDma - SG Data length and Sector Mismatch! SG Total Length %d, Sectors:%d, bytespersector:%d\n", checkLength, pContext->SectorsToTransfer, pInstance->FileSysBlockSize)); goto DO_DIO; } DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DiskRequestDma - submitting %s using SG entries: %d,len: %d\n", rq_data_dir(pRequest) ? "Write":"Read", MaxDescriptors, (UINT)(pContext->SectorsToTransfer * pInstance->BlockSize))); /* issue async transfer */ status = IssueAsyncTransfer(pInstance->pDevice, pInstance, pRequest->sector * pInstance->FileSysBlockSize, pContext->SectorsToTransfer*pInstance->FileSysBlockSize, rq_data_dir(pRequest), pContext->SGlist, MaxDescriptors, (outstandingReq > 0)?AsyncCompletion : AsyncCompletionLast, pContext); } /* reacquire the lock, the lock is held on entry of this function */ spin_lock_irq(&pInstance->Config.RequestLock); } DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: DiskRequestDma\n")); } /* ReplaceChar - replace all Find with Replace */ static char* ReplaceChar(char* pStr, char Find, char Replace) { int ii; for(ii = 0; pStr[ii] != 0; ii++) { if (pStr[ii] == Find) { pStr[ii] = Replace; } } return pStr; } #endif /* * DeviceIoctl - handle IOCTL requests */ static int DeviceIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct hd_geometry geometry; DBG_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: DeviceIoctl - cmd: %d\n", cmd)); switch(cmd) { case HDIO_GETGEO: { /* get device geometry request. Return something reasonable, our device doesn't care */ geometry.heads = 4; geometry.sectors = 16; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) geometry.start = get_start_sect(inode->i_bdev); geometry.cylinders = get_capacity(inode->i_bdev->bd_disk) / (4 * 16); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeviceIoctl - HDIO_GETGEO, size: %d heads: %d sectors: %d start: %d cylinders: %d\n", (INT)get_capacity(inode->i_bdev->bd_disk), (INT)geometry.heads, (INT)geometry.sectors, (INT)geometry.start, (INT)geometry.cylinders)); #else /* 2.4 */ { PSDIO_MEMORY_INSTANCE pInstance = GetFirstInstance(&DriverContext); geometry.start = 0; geometry.cylinders = (pInstance->Size/pInstance->FileSysBlockSize * 1024) / (4 * 16); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeviceIoctl - HDIO_GETGEO, size: %d heads: %d sectors: %d start: %d cylinders: %d\n", (INT)(pInstance->Size/pInstance->FileSysBlockSize * 1024), (INT)geometry.heads, (INT)geometry.sectors, (INT)geometry.start, (INT)geometry.cylinders)); } #endif if (copy_to_user((void *) arg, &geometry, sizeof(geometry))) { return -EFAULT; } return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* 2.4 */ case BLKGETSIZE: { PSDIO_MEMORY_INSTANCE pInstance = GetFirstInstance(&DriverContext); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeviceIoctl BLKGETSIZE\n")); if (!access_ok(VERIFY_WRITE, arg, sizeof(long))) { return -EFAULT; } return put_user(pInstance->Config.pGenDisk->part[MINOR(inode->i_rdev)].nr_sects, (long *)arg); } case BLKRRPART: /* re-read partition table */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeviceIoctl BLKRRPART\n")); if (!capable(CAP_SYS_ADMIN)) return -EACCES; return Revalidate(inode->i_rdev); default: DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: DeviceIoctl default\n")); return blk_ioctl(inode->i_rdev, cmd, arg); #endif } DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: DeviceIoctl - cmd: %d\n", cmd)); return -ENOTTY; /* unknown command */ } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) static int CheckDeviceChange(struct gendisk *disk) #else /* 2.4 */ static int CheckDeviceChange(kdev_t kdev) #endif { return 0; } /* * module init */ static int __init sdio_memory_init(void) { SDIO_STATUS status; SYSTEM_STATUS err; REL_PRINT(SDDBG_TRACE, ("+SDIO Memory Function: enter sdio_memory_init\n")); SDLIST_INIT(&DriverContext.InstanceList); status = SemaphoreInitialize(&DriverContext.InstanceSem, 1); if (!SDIO_SUCCESS(status)) { return SDIOErrorToOSError(status); } /* register with the block core */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) err = register_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE); #else /* 2.4 */ err = devfs_register_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE, &driver_ops); devfs_handle = devfs_mk_dir(NULL, SDIO_MEMORY_BASE, NULL); #endif if (err <= 0) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: unable to register with block driver, %d\n", (INT)err)); return err; } if (DriverContext.Driver.Major == 0) { /* save the assigned major number if it was a dynanmic assignment */ DriverContext.Driver.Major = err; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* 2.4 */ temp_major = DriverContext.Driver.Major; #endif } /* register with bus driver core */ if (!SDIO_SUCCESS((status = SDIO_RegisterFunction(&DriverContext.Function)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO Memory Function: failed to register with bus driver, %d\n", status)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) unregister_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE); #else devfs_unregister_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE); #endif return SDIOErrorToOSError(status); } DBG_PRINT(SDDBG_TRACE, ("-SDIO Memory Function: sdio_memory_init, major: %d\n", DriverContext.Driver.Major)); return 0; } /* * module cleanup */ static void __exit sdio_memory_cleanup(void) { REL_PRINT(SDDBG_TRACE, ("SDIO Memory Function: enter sdio_memory_cleanup\n")); DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: sdio_memory_cleanup unregistering sdio device\n")); SDIO_UnregisterFunction(&DriverContext.Function); /* unregister with the block driver core */ DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: sdio_memory_cleanup unregistering block device, major: %d\n", DriverContext.Driver.Major)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) unregister_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE); #else devfs_unregister_blkdev(DriverContext.Driver.Major, SDIO_MEMORY_BASE); #endif DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: sdio_memory_cleanup unregistering bus device\n")); } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DESCRIPTION); MODULE_AUTHOR(AUTHOR); module_init(sdio_memory_init); module_exit(sdio_memory_cleanup);