www.pudn.com > sdio-2.6.18-full.rar > sdio_memory.c


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@file: sdio_memory.c

@abstract: SDIO Memeory Function driver

#notes: includes OS independent portions -

@notice: Copyright (c), 2004-2006 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
 *
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#define MODULE_NAME  SDMEMORYFD
#include 

#include 
#include 
#include 
#include 
#include "sdio_memory.h"


#define MMC_GET_START_BIT(pCsd) (pCsd[0] & 0x1)
#define MMC_GET_FILE_FORMAT(pCsd) (pCsd[1] & 0xC)
#define MMC_GET_PERM_WRITE_PROTECT(pCsd) (pCsd[1] & 0x20)
#define MMC_GET_TEMP_WRITE_PROTECT(pCsd) (pCsd[1] & 0x10)
#define MMC_GET_FILE_FORMAT_GROUP(pCsd) (pCsd[1] & 0x80)
#define MMC_GET_PARTIAL_WRITE_DATA(pCsd) (pCsd[2] & 0x20)
#define MMC_GET_MAX_WRITE_DATA_BLOCK(pCsd) (((pCsd[2] & 0xC0)>>6) | ((pCsd[3] & 0x3) << 2))
#define MMC_GET_WRITE_SPEED_FACTOR(pCsd) (pCsd[3] & 0x1C)
#define MMC_GET_MAX_READ_DATA_BLOCK(pCsd) (pCsd[10] & 0x0F)
#define MMC_GET_PARTIAL_READ_DATA(pCsd) (pCsd[9] & 0x80)
#define MMC_GET_C_SIZE_MULT(pCsd) (((pCsd[5] & 0x80)>>7) | ((pCsd[6] & 0x03)<<1))
#define MMC_GET_C_SIZE(pCsd) (((pCsd[7] & 0xC0)>>6) | (pCsd[8]<<2) |((pCsd[9] & 0x03)<<10))
#define MMC_GET_SPEC_VERSION(pCsd) ((pCsd[15] & 0x3C) >> 2) /* MMC Only */
#define MMC_GET_CSD_VERSION(pCsd) ((pCsd[15] & 0xC0) >> 6)
static const INT MMC_POWER_TABLE[12] = {
    1,2,4,8,16,32,64,128,256,512,1024,2048
};

#define MMC_GET_WRITE_CURR_MAX(pCsd) (((pCsd)[6] >> 2) & 0x07)
#define MMC_GET_READ_CURR_MAX(pCsd) ((pCsd)[7] & 0x07)

    /* VDD current table, mA */
static const UINT8 VDDCurrentTable[8] = {
    1,5,10,25,35,45,80,200
};

#define MMC_CMD_SET_BLOCK_LENGTH    CMD16
#define MMC_CMD_READ_SINGLE_BLOCK   CMD17
#define MMC_CMD_READ_MULTIPLE_BLOCK CMD18
#define MMC_CMD_WRITE_SINGLE_BLOCK  CMD24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK CMD25


const SD_SLOT_CURRENT MMC_PowerClass_3_6V_To_Current[MMC_EXT_MAX_PWR_CLASSES] =
    /* in milliamps */
{
   200, /* 0 */
   220, /* 1 */
   250, /* 2 */
   280, /* 3 */
   300, /* 4 */
   320, /* 5 */
   350, /* 6 */
   400, /* 7 */
   450, /* 8 */
   500, /* 9 */
   550, /* 10 */
   0,0,0,0,0 /* 11 - 15 are reserved */
};

const SD_SLOT_CURRENT MMC_PowerClass_1_95V_To_Current[MMC_EXT_MAX_PWR_CLASSES] =
    /* in milliamps */
{
   130, /* 0 */
   140, /* 1 */
   160, /* 2 */
   180, /* 3 */
   200, /* 4 */
   220, /* 5 */
   240, /* 6 */
   260, /* 7 */
   280, /* 8 */
   300, /* 9 */
   350, /* 10 */
   0,0,0,0,0 /* 11 - 15 are reserved */
};



static SDIO_STATUS IssueDeviceRequest(PSDDEVICE        pDevice,
                                      UINT8            Cmd,
                                      UINT32           Argument,
                                      SDREQUEST_FLAGS  Flags,
                                      PSDREQUEST       pReqToUse,
                                      PVOID            pData,
                                      INT              Length);
static SDIO_STATUS ReadBlocks(PSDDEVICE        pDevice,
                              PSDIO_MEMORY_INSTANCE pInstance,
                              UINT32           Address,
                              PVOID            pData,
                              UINT32           Length);
static SDIO_STATUS WriteBlocks(PSDDEVICE        pDevice,
                              PSDIO_MEMORY_INSTANCE pInstance,
                              UINT32           Address,
                              PVOID            pData,
                              UINT32           Length);


/* delete an instance  */
void DeleteInstance(PSDIO_MEMORY_CONTEXT   pFuncContext,
                    PSDIO_MEMORY_INSTANCE  pInstance)
{

    if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pFuncContext->InstanceSem))) {
        return;
    }
        /* pull it out of the list */
    SDListRemove(&pInstance->SDList);
    SemaphorePost(&pFuncContext->InstanceSem);


        /* free slot current */
    SDLIB_IssueConfig(pInstance->pDevice,
                      SDCONFIG_FUNC_FREE_SLOT_CURRENT,
                      NULL,
                      0);

    KernelFree(pInstance);
}

static void ReorderBuffer(UINT8 *pBuffer, INT Bytes)
{
    UINT8 *pEnd;
    UINT8 temp;

    DBG_ASSERT(!(Bytes & 1));
        /* point to the end */
    pEnd = &pBuffer[Bytes - 1];
        /* divide in half */
    Bytes = Bytes >> 1;

    while (Bytes) {
        temp = *pBuffer;
            /* swap bytes */
        *pBuffer = *pEnd;
        *pEnd = temp;
        pBuffer++;
        pEnd--;
        Bytes--;
    }
}

SD_SLOT_CURRENT LookupCurrent(PSDDEVICE pDevice, PUINT8 pExtendedCSD)
{
    const SD_SLOT_CURRENT *pTable = NULL;
    UINT8 powerClass = 0;

    switch(SDDEVICE_GET_SLOT_VOLTAGE_MASK(pDevice)) {
        case SLOT_POWER_3_3V:
        case SLOT_POWER_3_0V:
        case SLOT_POWER_2_8V:
            pTable = MMC_PowerClass_3_6V_To_Current;
            if (SDDEVICE_GET_OPER_CLOCK(pDevice) <= 26000000) {
                DBG_PRINT(SDDBG_TRACE, ("    Using MMC 3.6V and 26Mhz power table \n"));
                powerClass = pExtendedCSD[MMC_EXT_PWR_CL_26_360_OFFSET];
            } else {
                DBG_PRINT(SDDBG_TRACE, ("    Using MMC 3.6V and 52Mhz power table \n"));
                powerClass = pExtendedCSD[MMC_EXT_PWR_CL_52_360_OFFSET];
            }
            break;
        case SLOT_POWER_2_0V:
        case SLOT_POWER_1_8V:
        case SLOT_POWER_1_6V:
            pTable = MMC_PowerClass_1_95V_To_Current;
            if (SDDEVICE_GET_OPER_CLOCK(pDevice) <= 26000000) {
                DBG_PRINT(SDDBG_TRACE, ("    Using MMC 1.95V and 26Mhz power table \n"));
                powerClass = pExtendedCSD[MMC_EXT_PWR_CL_26_195_OFFSET];
            } else {
                DBG_PRINT(SDDBG_TRACE, ("    Using MMC 1.95V and 52Mhz power table \n"));
                powerClass = pExtendedCSD[MMC_EXT_PWR_CL_52_195_OFFSET];
            }
            break;
        default:
            DBG_ASSERT(FALSE);
            break;
    }

    if (pTable != NULL) {
        if (SDDEVICE_GET_BUSWIDTH(pDevice) == SDCONFIG_BUS_WIDTH_MMC8_BIT) {
                /* use upper nibble for power class */
            powerClass >>= 4;
        }
        powerClass &= 0xF;
        DBG_PRINT(SDDBG_TRACE, ("    MMC Power Class %d: \n",powerClass));
        return pTable[powerClass];
    }

    return 0;
}
/* create a memory instance */
PSDIO_MEMORY_INSTANCE CreateDeviceInstance(PSDIO_MEMORY_CONTEXT pFuncContext,
                                           PSDDEVICE            pDevice)
{
    PSDIO_MEMORY_INSTANCE pInstance = NULL;
    SDCONFIG_FUNC_SLOT_CURRENT_DATA   slotCurrent;
    PUINT8 pCSD = SDDEVICE_GET_CARDCSD(pDevice);
    SD_SLOT_CURRENT  maxReadCurrent = 0;
    SD_SLOT_CURRENT  maxWriteCurrent = 0;
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;
    PSDREQUEST  pReq = NULL;

    ZERO_OBJECT(slotCurrent);

    do {
        pInstance = (PSDIO_MEMORY_INSTANCE)KernelAlloc(sizeof(SDIO_MEMORY_INSTANCE));
        if (NULL == pInstance) {
            status = SDIO_STATUS_NO_RESOURCES;
            break;
        }
        ZERO_POBJECT(pInstance);
        SDLIST_INIT(&pInstance->SDList);
        pInstance->pDevice = pDevice;

        DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function Instance: 0x%X \n",(INT)pInstance));
        DBG_PRINT(SDDBG_TRACE, (" Card Flags:   0x%X \n",SDDEVICE_GET_CARD_FLAGS(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Card RCA:     0x%X \n",SDDEVICE_GET_CARD_RCA(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Oper Clock:   %d Hz \n",SDDEVICE_GET_OPER_CLOCK(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Max Clock:    %d Hz \n",SDDEVICE_GET_MAX_CLOCK(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Oper BlklenLim:  %d bytes \n",SDDEVICE_GET_OPER_BLOCK_LEN(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Max  BlkLen:     %d bytes\n",SDDEVICE_GET_MAX_BLOCK_LEN(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Oper BlksLim:    %d blocks per trans \n",SDDEVICE_GET_OPER_BLOCKS(pDevice)));
        DBG_PRINT(SDDBG_TRACE, (" Max  Blks:       %d blocks per trans \n",SDDEVICE_GET_MAX_BLOCKS(pDevice)));


        pReq = SDDeviceAllocRequest(pDevice);

        if (NULL == pReq) {
            status = SDIO_STATUS_NO_RESOURCES;
            break;
        }

            /* for SD cards in high speed mode, the power consumption is reported in the switch
             * status block */
        if ((SDDEVICE_GET_CARD_FLAGS(pDevice) & CARD_SD) &&
             (SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_SD_HS)) {
                /* fetch the switch status block */
            pReq->Command = CMD6;
            pReq->Argument = SD_SWITCH_FUNC_ARG_GROUP_CHECK(SD_SWITCH_HIGH_SPEED_GROUP,
                                                     SD_SWITCH_HIGH_SPEED_FUNC_NO);
            pReq->Flags =  SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS;
            pReq->BlockCount = 1;
            pReq->BlockLen = SD_SWITCH_FUNC_STATUS_BLOCK_BYTES;
            pReq->pDataBuffer = pInstance->ExtendedData;

            status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq);

            if (SDIO_SUCCESS(status)) {
                    /* need to reorder this since cards send this MSB first */
                ReorderBuffer(pInstance->ExtendedData,SD_SWITCH_FUNC_STATUS_BLOCK_BYTES);
                maxWriteCurrent = SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pInstance->ExtendedData);
                if (maxWriteCurrent == 0) {
                    DBG_PRINT(SDDBG_WARN, ("SDIO Memory: SD Card Switch Status indicates 0 current!, using CSD instead\n"));
                } else {
                    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory: SD High Speed card requires %d mA of current\n",
                        maxWriteCurrent));
                }
                maxReadCurrent = maxWriteCurrent;
            } else {
                DBG_PRINT(SDDBG_WARN, ("SDIO Memory: Failed to get SD Card Switch Status \n"));
                status = SDIO_STATUS_SUCCESS;
                    /* since we can't figure this out, use the largest value the SD spec says */
                maxWriteCurrent = 200;
                maxReadCurrent = 200;
            }
        }
            /* for MMC cards in high speed mode the current consumption is in the extended CSD */
        if ((SDDEVICE_GET_CARD_FLAGS(pDevice) & CARD_MMC) &&
             (SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_MMC_HS)) {

            pReq->Command = MMC_CMD8;
            pReq->Argument = 0;
            pReq->Flags =  SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS;
            pReq->BlockCount = 1;
            pReq->BlockLen = MMC_EXT_CSD_SIZE;
            pReq->pDataBuffer = pInstance->ExtendedData;

            status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq);

            if (SDIO_SUCCESS(status)) {
                maxWriteCurrent = LookupCurrent(pDevice,pInstance->ExtendedData);
                if (maxWriteCurrent == 0) {
                    DBG_PRINT(SDDBG_WARN, ("SDIO Memory: MMC card Extended CSD indicates 0 current!, using old CSD instead\n"));
                } else {
                    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory: MMC High Speed card requires %d mA of current\n",
                        maxWriteCurrent));
                }
                maxReadCurrent = maxWriteCurrent;
            } else {
                DBG_PRINT(SDDBG_WARN, ("SDIO Memory: Failed to get MMC Extended CSD \n"));
                status = SDIO_STATUS_SUCCESS;
                    /* since we can't figure this out, pick a reasonable value */
                maxWriteCurrent = 500;
                maxReadCurrent = 500;
            }
        }

        if (0 == maxWriteCurrent) {
                /* get max currents for write from CSD */
            maxWriteCurrent =  VDDCurrentTable[MMC_GET_WRITE_CURR_MAX(pCSD)];
        }

        if (0 == maxReadCurrent) {
                /* get max currents for read from CSD */
            maxReadCurrent =  VDDCurrentTable[MMC_GET_READ_CURR_MAX(pCSD)];
        }

        DBG_PRINT(SDDBG_TRACE, ("SDIO Memory: Write Max Curr: %d mA, Read Max Curr: %d mA\n", maxWriteCurrent,
            maxReadCurrent));

            /* pick the highest of the read or write */
        slotCurrent.SlotCurrent = max(maxWriteCurrent, maxReadCurrent);

        DBG_PRINT(SDDBG_TRACE, ("SDIO Memory: Allocating Slot current: %d mA\n", slotCurrent.SlotCurrent));
        status = SDLIB_IssueConfig(pDevice,
                                   SDCONFIG_FUNC_ALLOC_SLOT_CURRENT,
                                   &slotCurrent,
                                   sizeof(slotCurrent));

        if (!SDIO_SUCCESS((status))) {
            DBG_PRINT(SDDBG_ERROR, ("SDIO Memory: failed to allocate slot current %d\n",
                                    status));
            if (status == SDIO_STATUS_NO_RESOURCES) {
                DBG_PRINT(SDDBG_ERROR, ("SDIO Memory: Remaining Slot Current: %d mA\n",
                                    slotCurrent.SlotCurrent));
            }
            break;
        }

    } while (FALSE);

    if (!SDIO_SUCCESS(status) && (pInstance != NULL)) {
        KernelFree(pInstance);
        pInstance = NULL;
    }

    if (pReq != NULL) {
        SDDeviceFreeRequest(pDevice,pReq);
    }
    return pInstance;
}

PSDIO_MEMORY_INSTANCE GetFirstInstance(PSDIO_MEMORY_CONTEXT pFuncContext)
{
    SDIO_STATUS status;
    PSDIO_MEMORY_INSTANCE pInstance;

    status = SemaphorePendInterruptable(&pFuncContext->InstanceSem);
    if (!SDIO_SUCCESS(status)) {
        return NULL;
    }

    if (SDLIST_IS_EMPTY(&pFuncContext->InstanceList)) {
            /* no more left */
        pInstance = NULL;
    } else {
            /* get the item at the head */
        pInstance = CONTAINING_STRUCT(SDLIST_GET_ITEM_AT_HEAD(&pFuncContext->InstanceList),
                                      SDIO_MEMORY_INSTANCE,
                                      SDList);
    }

    SemaphorePost(&pFuncContext->InstanceSem);
    return pInstance;
}

/* find an instance associated with the SD device */
PSDIO_MEMORY_INSTANCE FindInstance(PSDIO_MEMORY_CONTEXT pFuncContext,
                                          PSDDEVICE                 pDevice)
{
    SDIO_STATUS status;
    PSDLIST     pItem;
    PSDIO_MEMORY_INSTANCE pInstance = NULL;

    status = SemaphorePendInterruptable(&pFuncContext->InstanceSem);
    if (!SDIO_SUCCESS(status)) {
        return NULL;
    }
        /* walk the list and find our instance */
    SDITERATE_OVER_LIST(&pFuncContext->InstanceList, pItem) {
        pInstance = CONTAINING_STRUCT(pItem, SDIO_MEMORY_INSTANCE, SDList);
        if (pInstance->pDevice == pDevice) {
                /* found it */
            break;
        }
        pInstance = NULL;
    }

    SemaphorePost(&pFuncContext->InstanceSem);
    return pInstance;
}

/* add and instance to our list */
SDIO_STATUS AddDeviceInstance(PSDIO_MEMORY_CONTEXT  pFuncContext,
                              PSDIO_MEMORY_INSTANCE pInstance)
{
    SDIO_STATUS status;

    status = SemaphorePendInterruptable(&pFuncContext->InstanceSem);
    if (!SDIO_SUCCESS(status)) {
        return status;
    }

    SDListAdd(&pFuncContext->InstanceList,&pInstance->SDList);
    SemaphorePost(&pFuncContext->InstanceSem);

    return SDIO_STATUS_SUCCESS;
}

/* cleanup the function context */
void CleanupFunctionContext(PSDIO_MEMORY_CONTEXT pFuncContext)
{
    SemaphoreDelete(&pFuncContext->InstanceSem);
}

/* initialize the function context */
SDIO_STATUS InitFunctionContext(PSDIO_MEMORY_CONTEXT pFuncContext)
{
    SDIO_STATUS status;
    SDLIST_INIT(&pFuncContext->InstanceList);

    status = SemaphoreInitialize(&pFuncContext->InstanceSem, 1);
    if (!SDIO_SUCCESS(status)) {
        return status;
    }

    return SDIO_STATUS_SUCCESS;
}

/*
 * GetCardCSD - get the interesting part of Card CSD register
*/
SDIO_STATUS GetCardCSD(PSDDEVICE pDevice, PSDIO_MEMORY_INSTANCE pInstance, BOOL IsMmcCardType)
{
    PUINT8 pCSD = SDDEVICE_GET_CARDCSD(pDevice);
    UINT32 size;
    SDIO_STATUS status;

    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: ViewCardCSD\n"));
    if (!MMC_GET_START_BIT(pCSD)) {
        /* this bit is not really available to be read DBG_PRINT(SDDBG_ERROR, ("**ERROR, start bit not 1\n"));*/
    }
    if (MMC_GET_CSD_VERSION(pCSD) == 0) {
        DBG_PRINT(SDDBG_TRACE, ("  CSD version: 1.0\n")); /* all SD cards should be here */
    } else if (MMC_GET_CSD_VERSION(pCSD) == 1) {
        DBG_PRINT(SDDBG_TRACE, ("  CSD version: 1.1\n"));
    } else if (MMC_GET_CSD_VERSION(pCSD) == 2) {
        DBG_PRINT(SDDBG_TRACE, ("  CSD version: 1.2\n"));
    } else {
        DBG_PRINT(SDDBG_TRACE, ("  CSD version: reserved %d\n", MMC_GET_SPEC_VERSION(pCSD)));
    }
    if (IsMmcCardType) {
        if (MMC_GET_SPEC_VERSION(pCSD) == 0) {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: 1.0-1.2\n"));
        } else if (MMC_GET_SPEC_VERSION(pCSD) == 1) {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: 1.4\n"));
        } else if (MMC_GET_SPEC_VERSION(pCSD) == 2) {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: 2.0-2.2\n"));
        } else if (MMC_GET_SPEC_VERSION(pCSD) == 3) {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: 3.1-3.31\n"));
        } else if (MMC_GET_SPEC_VERSION(pCSD) == 4) {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: 4.0-4.1\n"));
        } else {
            DBG_PRINT(SDDBG_TRACE, ("  spec version: reserved %d\n", MMC_GET_SPEC_VERSION(pCSD)));
        }
    }
    if (MMC_GET_FILE_FORMAT_GROUP(pCSD)) {
        DBG_PRINT(SDDBG_TRACE, ("  File format reserved: %d\n", MMC_GET_FILE_FORMAT(pCSD)));
    } else {
        if (MMC_GET_FILE_FORMAT(pCSD) == 0) {
            DBG_PRINT(SDDBG_TRACE, ("  File format: Hard disk-like file system with partition table\n"));
        } else if (MMC_GET_FILE_FORMAT(pCSD) == 1) {
            DBG_PRINT(SDDBG_TRACE, ("  File format: DOS FAT (floppy-like) with boot sector only (no partition table)\n"));
        } else if (MMC_GET_FILE_FORMAT(pCSD) == 2) {
            DBG_PRINT(SDDBG_TRACE, ("  File format: Universal File Format\n"));
        } else if (MMC_GET_FILE_FORMAT(pCSD) == 3) {
            DBG_PRINT(SDDBG_TRACE, ("  File format: Others - Unknown\n"));
        }
    }
    if (MMC_GET_MAX_WRITE_DATA_BLOCK(pCSD) > 11) {
        DBG_PRINT(SDDBG_WARN, ("  Max write block *reserved* value: %d\n",
                               MMC_GET_MAX_WRITE_DATA_BLOCK(pCSD)));
        /* use the max, as this size is in error */
        pInstance->WriteBlockLength = MMC_POWER_TABLE[11];
    } else {
        DBG_PRINT(SDDBG_TRACE, ("  Max write block: %d\n",
                                MMC_POWER_TABLE[MMC_GET_MAX_WRITE_DATA_BLOCK(pCSD)]));
        pInstance->WriteBlockLength = MMC_POWER_TABLE[MMC_GET_MAX_WRITE_DATA_BLOCK(pCSD)];
    }
    pInstance->PartialWritesAllowed = MMC_GET_PARTIAL_WRITE_DATA(pCSD);
    if (pInstance->PartialWritesAllowed) {
        DBG_PRINT(SDDBG_TRACE, ("  partial write allowed:\n"));
    } else {
        DBG_PRINT(SDDBG_TRACE, ("  partial write not allowed:\n"));
    }

    DBG_PRINT(SDDBG_TRACE, ("  write speed R2W_FACTOR: %d\n",
                            MMC_POWER_TABLE[MMC_GET_WRITE_SPEED_FACTOR(pCSD)]));

    if (MMC_GET_MAX_READ_DATA_BLOCK(pCSD) > 11) {
        DBG_PRINT(SDDBG_WARN, ("  Max read block reserved value: %d\n",
                               MMC_GET_MAX_READ_DATA_BLOCK(pCSD)));
        /* use the max, as this size is in error */
        pInstance->ReadBlockLength = MMC_POWER_TABLE[11];
    } else {
        DBG_PRINT(SDDBG_TRACE, ("  Max read block: %d\n",
                                MMC_POWER_TABLE[MMC_GET_MAX_READ_DATA_BLOCK(pCSD)]));
        pInstance->ReadBlockLength = MMC_POWER_TABLE[MMC_GET_MAX_READ_DATA_BLOCK(pCSD)];
    }
    pInstance->PartialReadsAllowed = MMC_GET_PARTIAL_READ_DATA(pCSD);
    if (pInstance->PartialReadsAllowed) {
        DBG_PRINT(SDDBG_TRACE, ("  partial read allowed:\n"));
    } else {
        DBG_PRINT(SDDBG_TRACE, ("  partial read not allowed:\n"));
    }

    DBG_PRINT(SDDBG_TRACE, ("  C_SIZE/C_SIZE_MULT: %d/%d\n",
                            MMC_GET_C_SIZE(pCSD),MMC_GET_C_SIZE_MULT(pCSD) ));
    /* calulate the size in 1k increments, handle 4GB as a special case to avoid overflow */
    if ((MMC_POWER_TABLE[MMC_GET_C_SIZE_MULT(pCSD)+2] == 512) &&
        ((MMC_GET_C_SIZE(pCSD) + 1) == 4096) &&
        (MMC_POWER_TABLE[MMC_GET_MAX_READ_DATA_BLOCK(pCSD)] == 2048)) {
           /* special case of 4GB card */
        size = 0x400000;
    } else {
        size = (MMC_POWER_TABLE[MMC_GET_C_SIZE_MULT(pCSD)+2] * (MMC_GET_C_SIZE(pCSD) + 1) *
               MMC_POWER_TABLE[MMC_GET_MAX_READ_DATA_BLOCK(pCSD)]) / 1024;
    }
    DBG_PRINT(SDDBG_TRACE, ("  capacity: %dK\n", (UINT)size));
    pInstance->Size = size;
    /* set the block size to the smaller of the read/write block sizes */
    /* use the smaller of two block sizes */
//??    pInstance->BlockSize = (pInstance->ReadBlockLength < pInstance->WriteBlockLength)?
//??                            pInstance->ReadBlockLength : pInstance->WriteBlockLength;
    /* MMC cards seem to want us to ignore the write block size and use the read block size,
       SD cards have these equal to each other */
    pInstance->BlockSize = pInstance->ReadBlockLength;
    pInstance->MaxBlocksPerTransfer = pDevice->pHcd->CardProperties.OperBlockCountLimit;
    /* tell the device this is the block size we'll use for reads and writes */
    status =  IssueDeviceRequest(pDevice, MMC_CMD_SET_BLOCK_LENGTH, pInstance->BlockSize,
                                 SDREQ_FLAGS_RESP_R1, NULL, NULL, 0);

    pInstance->WriteProtected =
            (MMC_GET_PERM_WRITE_PROTECT(pCSD) || MMC_GET_TEMP_WRITE_PROTECT(pCSD)) ||
            SDDEVICE_IS_CARD_WP_ON(pDevice);
    DBG_PRINT(SDDBG_TRACE, ("  %s media- perm:%d temp:%d\n",
                            (pInstance->WriteProtected)? "write protected" : "writable",
                            (UINT)MMC_GET_PERM_WRITE_PROTECT(pCSD), (UINT)MMC_GET_TEMP_WRITE_PROTECT(pCSD)));

    if (DBG_GET_DEBUG_LEVEL() >= SDDBG_DUMP) {
        SDLIB_PrintBuffer((PUCHAR)pCSD, (INT)MAX_CARD_RESPONSE_BYTES, "SDIO Memory Function: CSD");
    }

    return status;
}



/*
 * MemoryTransfer - read/write to device
*/
SDIO_STATUS MemoryTransfer(PSDIO_MEMORY_INSTANCE pInstance, SDSECTOR_SIZE sectorNumber,
                           ULONG sectorCount, PUCHAR pBuffer, BOOL WriteDirection)
{
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;
    ULONG startOffset = sectorNumber * pInstance->FileSysBlockSize;
    ULONG length = sectorCount *  pInstance->FileSysBlockSize;

    if (((SDSECTOR_SIZE)startOffset+(SDSECTOR_SIZE)length) >
            ((SDSECTOR_SIZE)pInstance->Size * (SDSECTOR_SIZE)1024)) {
        /* past end of disk */
        DBG_PRINT(SDDBG_WARN, ("SDIO Memory Function: MemoryTransfer sector past end of disk, %d/%d size/blockSize %d/%d \n",
                               (INT)sectorNumber, (INT)sectorCount,
                               (INT)pInstance->Size, (INT)pInstance->FileSysBlockSize));
        return SDIO_STATUS_INVALID_PARAMETER;
    }
    if (WriteDirection) {
        /* write to device */
        DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: MemoryTransfer writing, %d/%d \n",
                                (UINT)startOffset, (UINT)length));
        if ((length/pInstance->BlockSize) <= pInstance->MaxBlocksPerTransfer) {
            status = WriteBlocks(pInstance->pDevice, pInstance, startOffset, pBuffer, length);
        } else {
            /* break into smaller chunks to transfer */
            int tlen;
            int offset = 0;
            for(tlen = length; tlen > 0; tlen = length) {
                tlen = (tlen/pInstance->MaxBlocksPerTransfer) * pInstance->BlockSize;
                tlen = ((tlen/pInstance->MaxBlocksPerTransfer) > pInstance->MaxBlocksPerTransfer) ?
                            pInstance->MaxBlocksPerTransfer * pInstance->BlockSize: tlen;
                length -= tlen;
                status = WriteBlocks(pInstance->pDevice, pInstance, startOffset, &pBuffer[offset], tlen);
                offset += tlen;
                startOffset += tlen/pInstance->MaxBlocksPerTransfer;
                if (!SDIO_SUCCESS(status)) {
                  break;
                }
            }
        }
        if (SDIO_SUCCESS(status)) {
            if (DBG_GET_DEBUG_LEVEL() >= SDDBG_DUMP) {
                SDLIB_PrintBuffer(pBuffer, length, "SDIO Memory Function: Write buffer");
            }
        } else {
            DBG_PRINT(SDDBG_WARN, ("SDIO Memory Function: MemoryTransfer error on WriteBlock, %d\n",
                                   status ));
            if (DBG_GET_DEBUG_LEVEL() >= SDDBG_TRACE) {
                SDLIB_PrintBuffer(pBuffer, (length > 16) ? 16 : length, "SDIO Memory Function: Write buffer, with error");
            }
        }
    } else {
        /* read from device */
        DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: MemoryTransfer reading, Offset:%d Length:%d \n",
                                (UINT)startOffset, (UINT)length));
        if ((length/pInstance->BlockSize) <= pInstance->MaxBlocksPerTransfer) {
            status = ReadBlocks(pInstance->pDevice, pInstance, startOffset, pBuffer, length);
        } else {
            /* break into smaller chunks to transfer */
            int tlen;
            int offset = 0;
            for(tlen = length; tlen > 0; tlen = length) {
                tlen = (tlen/pInstance->MaxBlocksPerTransfer) * pInstance->BlockSize;
                tlen = ((tlen/pInstance->MaxBlocksPerTransfer) > pInstance->MaxBlocksPerTransfer) ?
                            pInstance->MaxBlocksPerTransfer * pInstance->BlockSize: tlen;
                length -= tlen;
                status = ReadBlocks(pInstance->pDevice, pInstance, startOffset, &pBuffer[offset], tlen);
                offset += tlen;
                startOffset += tlen/pInstance->MaxBlocksPerTransfer;
                if (!SDIO_SUCCESS(status)) {
                  break;
                }
            }
        }

        if (SDIO_SUCCESS(status)) {
            if (DBG_GET_DEBUG_LEVEL() >= SDDBG_DUMP) {
                SDLIB_PrintBuffer(pBuffer, length, "SDIO Memory Function: Read buffer");
            }
        } else {
            DBG_PRINT(SDDBG_WARN, ("SDIO Memory Function: MemoryTransfer error on ReadBlock, %d\n",
                                   status ));
            if (DBG_GET_DEBUG_LEVEL() >= SDDBG_TRACE) {
                SDLIB_PrintBuffer(pBuffer, (length > 16) ? 16 : length, "SDIO Memory Function: Read buffer, with error");
            }
        }
    }
    return status;
}

/*
 *  ReadBlocks
*/
static SDIO_STATUS ReadBlocks(PSDDEVICE        pDevice,
                              PSDIO_MEMORY_INSTANCE pInstance,
                              UINT32           Address,
                              PVOID            pData,
                              UINT32           Length)
{
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;
    PSDREQUEST  pReq;

    pReq = SDDeviceAllocRequest(pDevice);
    if (NULL == pReq) {
        return SDIO_STATUS_NO_RESOURCES;
    }

    pReq->Argument = Address;
    pReq->Flags = SDREQ_FLAGS_DATA_TRANS | SDREQ_FLAGS_RESP_R1;
    pReq->Command = (Length > pInstance->BlockSize)?
                        MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK;
    if (pReq->Command == MMC_CMD_READ_MULTIPLE_BLOCK) {
            /* bus driver issues auto stop */
        pReq->Flags |= SDREQ_FLAGS_AUTO_CMD12;
    }
    pReq->pDataBuffer  = pData;
    pReq->BlockCount = Length / pInstance->BlockSize;
    pReq->BlockLen = pInstance->BlockSize;
    if (SDDEVICE_IS_BUSMODE_SPI(pDevice)) {
        pReq->RetryCount = 3;
    }
    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: ReadBlocks reading, count/size length %d/%d %d cmd: %d \n",
                            (UINT)pReq->BlockCount, (UINT)pReq->BlockLen,
                            (UINT)(pReq->BlockLen*pReq->BlockCount), (UINT)pReq->Command));
    status = SDDEVICE_CALL_REQUEST_FUNC(pDevice, pReq);

    SDDeviceFreeRequest(pDevice, pReq);

    return status;
}

/*
 *  WriteBlocks
*/
static SDIO_STATUS WriteBlocks(PSDDEVICE        pDevice,
                              PSDIO_MEMORY_INSTANCE pInstance,
                              UINT32           Address,
                              PVOID            pData,
                              UINT32           Length)
{
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;
    PSDREQUEST  pReq;

    pReq = SDDeviceAllocRequest(pDevice);
    if (NULL == pReq) {
        return SDIO_STATUS_NO_RESOURCES;
    }

    pReq->Argument = Address;
    pReq->Flags = SDREQ_FLAGS_DATA_TRANS | SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_WRITE |
                  SDREQ_FLAGS_AUTO_TRANSFER_STATUS;
    pReq->Command = (Length > pInstance->BlockSize)?
                        MMC_CMD_WRITE_MULTIPLE_BLOCK : MMC_CMD_WRITE_SINGLE_BLOCK;

    if (pReq->Command == MMC_CMD_WRITE_MULTIPLE_BLOCK){
        pReq->Flags |= SDREQ_FLAGS_AUTO_CMD12;
    }
    if (SDDEVICE_IS_BUSMODE_SPI(pDevice)) {
        pReq->RetryCount = 3;
    }

    pReq->pDataBuffer  = pData;
    pReq->BlockCount = Length / pInstance->BlockSize;
    pReq->BlockLen = pInstance->BlockSize;
    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: WriteBlocks reading, count/size length %d/%d %d cmd: %d \n",
                            (UINT)pReq->BlockCount, (UINT)pReq->BlockLen,
                            (UINT)(pReq->BlockLen*pReq->BlockCount), (UINT)pReq->Command));

    status = SDDEVICE_CALL_REQUEST_FUNC(pDevice, pReq);

    SDDeviceFreeRequest(pDevice, pReq);
    return status;
}

/*
 *  IssueAsyncTransfer
*/
SDIO_STATUS IssueAsyncTransfer(PSDDEVICE        pDevice,
                            PSDIO_MEMORY_INSTANCE pInstance,
                            UINT32           Address,
                            UINT32           Length,
                            BOOL             Write,
                            PVOID            pBufferOrSGList,
                            UINT             SGcount,
                            void (*pCompletion)(struct _SDREQUEST *pRequest),
                            PVOID            pContext)
{
    PSDREQUEST  pReq;

    pReq = SDDeviceAllocRequest(pDevice);
    if (NULL == pReq) {
        return SDIO_STATUS_NO_RESOURCES;
    }

    pReq->Argument = Address;

        /* set up default flags */
    pReq->Flags = SDREQ_FLAGS_DATA_TRANS | SDREQ_FLAGS_RESP_R1 |
                  SDREQ_FLAGS_TRANS_ASYNC;

    if (SGcount > 0) {
             /* using DMA , pBufferOrSGList is a scatter gather list*/
        pReq->Flags |= SDREQ_FLAGS_DATA_DMA;
    }

    if (Write) {
        pReq->Flags |= SDREQ_FLAGS_DATA_WRITE | SDREQ_FLAGS_AUTO_TRANSFER_STATUS;
        pReq->Command = (Length > pInstance->BlockSize)?
                        MMC_CMD_WRITE_MULTIPLE_BLOCK : MMC_CMD_WRITE_SINGLE_BLOCK;
    } else {
        pReq->Command = (Length > pInstance->BlockSize)?
                        MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK;
    }

    if (Length > pInstance->BlockSize) {
            /* multi-block transfer requires CMD12 to stop */
        pReq->Flags |= SDREQ_FLAGS_AUTO_CMD12;
    }

    pReq->pDataBuffer  = pBufferOrSGList;
    pReq->DescriptorCount = SGcount;
    pReq->BlockCount = Length / pInstance->BlockSize;
    pReq->BlockLen = pInstance->BlockSize;
    pReq->pCompletion = pCompletion;
    pReq->pCompleteContext = pContext;

    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: IssueAsyncTransfer (%s), (count/blksize:%d/%d) (length:%d) cmd: %d \n",
                            Write ? "TX": "RX",
                            (UINT)pReq->BlockCount, (UINT)pReq->BlockLen,
                            (UINT)(pReq->BlockLen*pReq->BlockCount), (UINT)pReq->Command));
    return SDDEVICE_CALL_REQUEST_FUNC(pDevice, pReq);
}

/*
 *  WriteBlocksAsync
*/
SDIO_STATUS WriteBlocksAsync(PSDDEVICE        pDevice,
                             PSDIO_MEMORY_INSTANCE pInstance,
                             UINT32           Address,
                             UINT32           Length,
                             PSDDMA_DESCRIPTOR    pDmaList,
                             UINT             SGcount,
                             void (*pCompletion)(struct _SDREQUEST *pRequest),
                             PVOID pContext)
{
    PSDREQUEST  pReq;

    pReq = SDDeviceAllocRequest(pDevice);
    if (NULL == pReq) {
        return SDIO_STATUS_NO_RESOURCES;
    }

    pReq->Argument = Address;
    pReq->Flags = SDREQ_FLAGS_DATA_TRANS | SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_WRITE |
                  SDREQ_FLAGS_TRANS_ASYNC | SDREQ_FLAGS_DATA_DMA | SDREQ_FLAGS_AUTO_TRANSFER_STATUS;

    pReq->Command = (Length > pInstance->BlockSize)?
                        MMC_CMD_WRITE_MULTIPLE_BLOCK : MMC_CMD_WRITE_SINGLE_BLOCK;
    if (pReq->Command == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
            /* bus driver issues auto stop */
        pReq->Flags |= SDREQ_FLAGS_AUTO_CMD12;
    }
    pReq->pDataBuffer  = (PVOID)pDmaList;
    pReq->DescriptorCount = SGcount;
    pReq->BlockCount = Length / pInstance->BlockSize;
    pReq->BlockLen = pInstance->BlockSize;
    pReq->pCompletion = pCompletion;
    pReq->pCompleteContext = pContext;

    DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function: WriteBlocksAsync reading, count/size length %d/%d %d cmd: %d \n",
                            (UINT)pReq->BlockCount, (UINT)pReq->BlockLen,
                            (UINT)(pReq->BlockLen*pReq->BlockCount), (UINT)pReq->Command));
    return SDDEVICE_CALL_REQUEST_FUNC(pDevice, pReq);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  IssueDeviceRequest - issue a bus request
  Input:  pDevice - device to send to
          Cmd - command to issue
          Argument - command argument
          Flags - request flags

  Output: pReqToUse - request to use (if caller wants response data)
  Return: SDIO Status
  Notes:  This function only issues 1 block data transfers
          This function issues the request synchronously
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static SDIO_STATUS IssueDeviceRequest(PSDDEVICE        pDevice,
                                      UINT8            Cmd,
                                      UINT32           Argument,
                                      SDREQUEST_FLAGS  Flags,
                                      PSDREQUEST       pReqToUse,
                                      PVOID            pData,
                                      INT              Length)
{
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;
    PSDREQUEST  pReq;

    if (NULL == pReqToUse) {
            /* caller doesn't care about the response data, allocate locally */
        pReq = SDDeviceAllocRequest(pDevice);
        if (NULL == pReq) {
            return SDIO_STATUS_NO_RESOURCES;
        }
    } else {
            /* use the caller's request buffer */
        pReq = pReqToUse;
    }

    pReq->Argument = Argument;
    pReq->Flags = Flags;
    pReq->Command = Cmd;
    if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS) {
        pReq->pDataBuffer  = pData;
        pReq->BlockCount = 1;
        pReq->BlockLen = Length;
    }

    status = SDDEVICE_CALL_REQUEST_FUNC(pDevice, pReq);

    if (NULL == pReqToUse) {
        DBG_ASSERT(pReq != NULL);
        SDDeviceFreeRequest(pDevice, pReq);
    }
    return status;
}