网上对SD驱动总体结构的分析已经很多了,SD卡驱动一般采用微软的三层结构,最上层是Client层,可以有多个SD卡或者MMC卡调用;最下层是硬件控制层,直接读写寄存器;中间层是Bus层,Bus层作为Client层和HC层的中间桥梁,传输读写等命令。幸运的是,微软已经为我们编写好了Client层和Bus层,我们一般不要去修改,我们主要分析HC代码。不同的板子有不同的HC,我们看看S3C2413的HC层源代码,位于%WINCEROOT%/PLATFORM/SMDK2413/Src/Drivers/SDHC下面。
先分析sdiocontroller.cpp,看CustomSetup函数:
/*打开注册表*/
HKEY hKeyDevice = OpenDeviceKey(pszRegistryPath);
/*提取注册表"CardDetectGPIO"键值,我设置为"G"了,意思是选择G端口来检测卡*/
LPCTSTR pszCardDetectGPIO = regDevice.ValueSZ( CARD_DETECT_GPIO_TEXT );
/*提取注册表"CardDetectMask"键值,我设置为dword:100了,意思是选择G端口的8位,即EINT16来检测卡*/
m_dwCardDetectMask = regDevice.ValueDW( CARD_DETECT_MASK_TEXT );
/*提取注册表"CardDetectFlag"键值,我设置为dword:0了,意思是选择将GPG8置0*/
m_dwCardDetectFlag = regDevice.ValueDW( CARD_DETECT_FLAG_TEXT );
/*提取注册表"CardDetectControlMask"键值,我设置为dword:fffcffff了*/
m_dwCardDetectControlMask=regDevice.ValueDW( CARD_DETECT_CONTROL_MASK_TEXT );
/*提取注册表"CardDetectControlFlag"键值,我设置为dword:0*/
m_dwCardDetectControlFlag=regDevice.ValueDW( CARD_DETECT_CONTROL_FLAG_TEXT );
/*提取注册表"CardDetectPullupMask"键值,我设置为dword:fffffeff*/
m_dwCardDetectPullupMask=regDevice.ValueDW( CARD_DETECT_PULLUP_MASK_TEXT );
/*提取注册表"CardDetectPullupFlag"键值,我设置为dword:100*/
m_dwCardDetectPullupFlag=regDevice.ValueDW( CARD_DETECT_PULLUP_FLAG_TEXT );
/*提取注册表"CardReadWriteGPIO"键值,我设置为"H",意思是选择H端口来读写*/
LPCTSTR pszCardReadWriteGPIO=
regDevice.ValueSZ( CARD_READWRITE_GPIO_TEXT );
/*跟前面类似,不再赘述*/
m_dwCardReadWriteMask = regDevice.ValueDW( CARD_READWRITE_MASK_TEXT );
m_dwCardReadWriteFlag = regDevice.ValueDW( CARD_READWRITE_FLAG_TEXT );
m_dwCardReadWriteControlMask=regDevice.ValueDW( CARD_READWRITE_CONTROL_MASK_TEXT );
m_dwCardReadWriteControlFlag=regDevice.ValueDW( CARD_READWRITE_CONTROL_FLAG_TEXT );
m_dwCardReadWritePullupMask=regDevice.ValueDW( CARD_READWRITE_PULLUP_MASK_TEXT );
m_dwCardReadWritePullupFlag=regDevice.ValueDW( CARD_READWRITE_PULLUP_FLAG_TEXT );
重点还是放在硬件初始化函数InitializeHardware:
前面已经提到,我们在注册表里面设置"CardReadWriteGPIO"为"H",m_chCardReadWriteGPIO就为H。
/*设置GPHCON寄存器和GPHDN寄存器,前者是配置端口H的pin的寄存器,根据datasheet,后者是H端口Pull-down function的允许禁止寄存器,至于Pull-down function是什么意思,我也不清楚,先不管,继续在黑暗中探索*/
case 'H':
vm_pIOPreg->GPHCON = ( vm_pIOPreg->GPHCON & m_dwCardReadWriteControlMask ) | m_dwCardReadWriteControlFlag;
vm_pIOPreg->GPHDN = ( vm_pIOPreg->GPHDN & m_dwCardReadWritePullupMask ) | m_dwCardReadWritePullupFlag;
break;
前面已经提到,我们在注册表里面设置"CardDetectGPIO"为"G",m_chCardDetectGPIO就为G。
/*设置GPGDN寄存器*/
case 'G':
vm_pIOPreg->GPGDN = ( vm_pIOPreg->GPGDN & m_dwCardDetectPullupMask ) | m_dwCardDetectPullupFlag;
break;
我们再来看sdiocontrollerbase.cpp的源代码,看函数Initialize:
/*SD_API_STATUS 在sdcarddk.h里面定义为LONG,SD_API_STATUS_SUCCESS被定义为0*/
SD_API_STATUS status = SD_API_STATUS_SUCCESS; // intermediate status
/*初始化临界变量m_ControllerCriticalSection*/
InitializeCriticalSection(&m_ControllerCriticalSection);
/*给2413的IO端口分配内存*/
vm_pIOPreg = (S3C2413_IOPORT_REG *)VirtualAlloc(0, sizeof(S3C2413_IOPORT_REG), MEM_RESERVE, PAGE_NOACCESS);
/*把刚才分配的虚拟内存映射成物理内存*/
VirtualCopy((PVOID)vm_pIOPreg, (PVOID)(S3C2413_BASE_REG_PA_IOPORT >> 8), sizeof(S3C2413_IOPORT_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)
/*给2413的SDI寄存器分配内存*/
vm_pSDIReg = (S3C2413_SDI_REG *)VirtualAlloc(0, sizeof(S3C2413_SDI_REG), MEM_RESERVE, PAGE_NOACCESS);
/*把刚才分配给SDI的虚拟内存映射成物理内存*/
VirtualCopy((PVOID)vm_pSDIReg, (PVOID)(S3C2413_BASE_REG_PA_SDI >> 8), sizeof(S3C2413_SDI_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)
/*给2413的时钟电源寄存器分配内存*/
vm_pCLKPWR=(S3C2413_CLKPWR_REG*)VirtualAlloc(0, sizeof(S3C2413_CLKPWR_REG), MEM_RESERVE, PAGE_NOACCESS);
/*把刚才分配给时钟电源寄存器映射成为物理内存*/
VirtualCopy((PVOID)vm_pCLKPWR,(PVOID)(S3C2413_BASE_REG_PA_CLOCK_POWER >> 8), sizeof(S3C2413_CLKPWR_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)
/*给2413的DMA寄存器分配虚拟内存*/
vm_pDMAreg = (S3C2413_DMA_REG *)VirtualAlloc(0, sizeof(S3C2413_DMA_REG), MEM_RESERVE, PAGE_NOACCESS);
/*把刚才分配给DMA寄存器的虚拟内存映射成物理内存*/
VirtualCopy((PVOID)vm_pDMAreg, (PVOID)(S3C2413_BASE_REG_PA_DMA >> 8), sizeof(S3C2413_DMA_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)
/*分配内存给DAM以传输数据使用*/
m_pDMABuffer=(PBYTE)HalAllocateCommonBuffer(&dmaAdapter,MAXIMUM_DMA_TRANSFER_SIZE, &m_pDMABufferPhys, FALSE );
/* MMC_Hardware_PowerUp函数就是往CLKCON寄存器的9位置位,给SDI控制器提供电源*/
MMC_Hardware_PowerUp();
/*设置GPIO为SDI模式,允许上拉寄存器*/
vm_pIOPreg->GPEDN &= 0xF83F;
vm_pIOPreg->GPECON |= 0x2AA800;
/*设置为默认时钟频率*/
SetClockRate(SD_DEFAULT_CARD_ID_CLOCK_RATE);
/*设置SDICON SDIFSTA SDIBSIZE SDIDTIMER寄存器*/
vm_pSDIReg->SDICON |= LITTLE_ENDIAN_BYTE_ORDER;
vm_pSDIReg->SDIFSTA |= FIFO_RESET;
vm_pSDIReg->SDIBSIZE = BYTES_PER_SECTOR;
vm_pSDIReg->SDIDTIMER = MAX_DATABUSY_TIMEOUT;
/*创建一个事件等待卡插入的中断*/
m_hCardInsertInterruptEvent = CreateEvent(NULL, FALSE, FALSE,NULL);
/*创建一个为侦测卡的插入创建一个线程*/
m_hCardInsertInterruptThread = CreateThread(NULL,
0, (LPTHREAD_START_ROUTINE)SD_CardDetecThread,
this,
0,
&threadID);
/*中断初始化,绑定m_hCardInsertInterruptEvent事件给中断m_dwSDDetectSysIntr*/
InterruptInitialize (m_dwSDDetectSysIntr, m_hCardInsertInterruptEvent,NULL, 0)
然后是对信号响应、DMA数据传输的中断的设置,与上雷同。InitializeHardware()函数为初始化硬件操作,在本文的开头已经分析过了。下面还有一个关键的函数SlotOptionHandler。
SD_SLOT_OPTION_CODE是一个枚举,定义在sdhcd.h里面:
typedef enum _SD_SLOT_OPTION_CODE {
SDHCDNop = -1,
SDHCDSetSlotPower, // set slot power, takes a DWORD for the power bit mask
SDHCDSetSlotInterface, // set slot interface, takes a SD_CARD_INTERFACE structure
SDHCDEnableSDIOInterrupts, // enable SDIO interrupts on the slot, no parameters
SDHCDDisableSDIOInterrupts, // disable SDIO interrupts on the slot, no parameters
SDHCDAckSDIOInterrupt, // acknowledge that the SDIO interrupt was handled, no parameters
SDHCDGetWriteProtectStatus, // get Write protect status. Updates SD_CARD_INTERFACE structure
SDHCDQueryBlockCapability, // query whether HC supports requested block length,
// takes SD_HOST_BLOCK_CAPABILITY structure
SDHCDSetClockStateDuringIdle, // set the clock state(on or off) during the idle state
SDHCDSetSlotPowerState, // set the slot power state, takes a CEDEVICE_POWER_STATE
SDHCDGetSlotPowerState, // get the slot power state, takes a CEDEVICE_POWER_STATE
SDHCDWakeOnSDIOInterrupts, // wake on SDIO interrupts in D3, takes a BOOL
SDHCDGetSlotInfo, // get info on a specific slot, takes a PSDCARD_HC_SLOT_INFO
SDHCDSetSlotInterfaceEx, // set slot interface, takes a SD_CARD_INTERFACE_EX structure
SDHCDSlotOptionCount // count of valid slot option codes
} SD_SLOT_OPTION_CODE, *PSD_SLOT_OPTION_CODE;
/*看来电源是不能随便改变的*/
case SDHCDSetSlotPower:
// Nothing to do because this system only operates at the reported 3.3V
break;
接下来我们要搞清楚pData究竟是个什么东西?
它是一个PSD_CARD_INTERFACE类型的结构体,PSD_CARD_INTERFACE定义在sdcardddk.h里面:
// structure for information about a card's interface
typedef struct _SD_CARD_INTERFACE {
SD_INTERFACE_MODE InterfaceMode; // interface mode
ULONG ClockRate; // clock rate
BOOL WriteProtected; // write protect flag (SD Memory cards)
} SD_CARD_INTERFACE, *PSD_CARD_INTERFACE;
pData是被函数SlotOptionHandler传入的,在SDHCDSlotOptionHandler函数的定义里又用到了SlotOptionHandler函数,具体在什么地方确定pData的值呢?这是个问题!
/*设置总线宽度和时钟频率*/
case SDHCDSetSlotInterface:
// First set the bus width
if(((PSD_CARD_INTERFACE)pData)->InterfaceMode == SD_INTERFACE_SD_4BIT)
{
Set_SDI_Bus_Width_4Bit();
}else
{
Set_SDI_Bus_Width_1Bit();
}
// Next, set the clock rate
((PSD_CARD_INTERFACE)pData)->ClockRate = SetClockRate(((PSD_CARD_INTERFACE)pData)->ClockRate);
break;
/*打开SDIO中断*/
case SDHCDEnableSDIOInterrupts:
Enable_SDIO_Interrupts();
break;
/*关掉SDIO中断*/
case SDHCDDisableSDIOInterrupts:
Disable_SDIO_Interrupts();
break;
/*清楚SDIO中断悬置位*/
case SDHCDAckSDIOInterrupt:
Ack_SDIO_Interrupts();
InterruptDone(m_dwSDIOSysIntr);
break;
/*保存写保护状态*/
case SDHCDGetWriteProtectStatus:
((PSD_CARD_INTERFACE)pData)->WriteProtected = IsCardWriteProtected();
break;
/*修正读写块大小设置*/
case SDHCDQueryBlockCapability:
pBlockCaps = (PSD_HOST_BLOCK_CAPABILITY)pData;
//----- Validate block transfer properties -----
if (pBlockCaps->ReadBlockSize < MINIMUM_BLOCK_TRANSFER_SIZE )
{
pBlockCaps->ReadBlockSize = MINIMUM_BLOCK_TRANSFER_SIZE;
}
if (pBlockCaps->WriteBlockSize < MINIMUM_BLOCK_TRANSFER_SIZE )
{
pBlockCaps->WriteBlockSize = MINIMUM_BLOCK_TRANSFER_SIZE;
}
if (pBlockCaps->ReadBlockSize > MAXIMUM_BLOCK_TRANSFER_SIZE )
{
pBlockCaps->ReadBlockSize = MAXIMUM_BLOCK_TRANSFER_SIZE;
}
if (pBlockCaps->WriteBlockSize > MAXIMUM_BLOCK_TRANSFER_SIZE )
{
pBlockCaps->WriteBlockSize = MAXIMUM_BLOCK_TRANSFER_SIZE;
}
break;
/*设置槽信息和电压*/
case SDHCDGetSlotInfo:
if( OptionSize != sizeof(SDCARD_HC_SLOT_INFO) || pData == NULL )
{
status = SD_API_STATUS_INVALID_PARAMETER;
}
else
{
PSDCARD_HC_SLOT_INFO pSlotInfo = (PSDCARD_HC_SLOT_INFO)pData;
SDHCDSetSlotCapabilities(pSlotInfo, SD_SLOT_SD_4BIT_CAPABLE |
SD_SLOT_SD_1BIT_CAPABLE |
SD_SLOT_SDIO_CAPABLE |
SD_SLOT_SDIO_INT_DETECT_4BIT_MULTI_BLOCK);
SDHCDSetVoltageWindowMask(pSlotInfo, (SD_VDD_WINDOW_3_2_TO_3_3 | SD_VDD_WINDOW_3_3_TO_3_4));
SDHCDSetDesiredSlotVoltage(pSlotInfo, SD_VDD_WINDOW_3_2_TO_3_3);
SDHCDSetMaxClockRate(pSlotInfo, MAX_SDI_BUS_TRANSFER_SPEED);
SDHCDSetPowerUpDelay(pSlotInfo, 300);
}
break;