1.內容簡介
SD設備包括SD存儲設備和SDIO設備兩種。
SD設備一般支持SD總線接口和SPI總線接口兩種工作方式,兩者的硬件接口不同,如圖 1-1所示。
圖 1-1 SD硬件接口
本文目前涵蓋的內容如下:
SD存儲設備。
SD設備的SD總線接口。
2.原理概述
2.1 SD 協議棧功能
SD協議棧負責SD設備驅動管理,創建、刪除設備節點,實現傳輸、控制等功能。
2.2 SD 協議棧框架 圖 2-1 SD協議棧結構
SD協議棧的結構如圖 2-1所示,分爲三個層次,各個層次獨立實現各自的功能,每個層次之間通過統一的接口進行調用。
這樣做的優勢:完全隔離Client層和Host層,使兩者的開發相互獨立。
Client層無需Host層的任何信息,僅僅處理具體應用協議。
Core層處理核心的設備管理和SD、SPI傳輸協議。
Host層僅僅處理硬件傳輸相關的工作。
3.框架詳解
3.1 Core核心層
Core核心層中有以下幾個概念:
SD適配器 操作管理SD總線的抽象單元,對於SD總線的傳輸操作都通過SD適配器完成。
SDM 設備 SD 存儲設備管理的抽象單元,負責控制器設備和Core設備的管理。
SD Core設備 SD設備的抽象,具體實現SD設備創建、銷燬和傳輸等邏輯,傳輸時再具體調用SD適配器傳輸接口或SPI適配器傳輸接口。
3.1.1 SD適配器操作接口
SD總線是由SD適配器進行管理和操作的,SD總線適配器本身是一個鏈表的結構。
SD設備驅動首先應該創建SD總線適配器,SD設備都是掛在SD總線適配器鏈表上,通過適配器的接口來控制總線的,關係如圖 3-1所示。
圖 3-1適配器和設備的關係
1. 創建SD適配器
#include <SylixOS.h> LW_API INT API_SdAdapterCreate (CPCHAR pcName, PLW_SD_FUNCS psdfunc);
創建過程中創建SD總線控制塊、SD總線鎖,並將SD適配器加入到系統總線層鏈表。
2. 刪除SD適配器
#include <SylixOS.h> LW_API INT API_SdAdapterDelete (CPCHAR pcName);
刪除過程中將SD適配器從總線層鏈表中刪除,並且刪除SD總線鎖。
3. 獲取SD適配器
#include <SylixOS.h> LW_API INT API_SdAdapterDelete (CPCHAR pcName);
獲取SD適配器的過程就是從總線層鏈表中拿到SD總線控制塊。
3.1.2 SDM操作接口
1. 初始化SDM組建庫
#include <SylixOS.h> LW_API INT API_SdmLibInit (VOID);
函數具體操作如下:
調用API_SdLibInit()初始化SD組建庫。
初始化SDM自身的組建庫。
2. 處理Host通知事件
#include <SylixOS.h> LW_API INT API_SdmEventNotify (POVID pvSdmHost, INT iEvtType);
主要的邏輯如代碼清單 1所示。
代碼清單 1 Host處理通知事件
switch(iEvtType){ case SDM_EVENT_BOOT_DEV_INSERT: __sdmDevCreate(psdmhost); break; case SDM_EVENT_DEV_INSERT: iError = hotplugEvent((VOIDFUNCPTR)__sdmDevCreate,(PVOID)psdmhost,0,0,0,0,0); break; case SDM_EVENT_DEV_REMOVE: iError = hotplugEvent((VOIDFUNCPTR)__sdmDevDelete,(PVOID)psdmhost,0,0,0,0,0); break; …… }
相應的,例如當SD設備作爲BOOT設備插入時,驅動中應該有如代碼清單 2的代碼,這樣系統就會在SD設備插入後調用__sdmDevCreate創建SDM設備。
代碼清單 2 BOOT設備創建
if(bIsBootDev){ /* * BOOT設備特殊處理: * 在當前線程(而非熱插拔線程)直接創建設備,提供啓動速度 */ API_SdmEventNotify(pvSdmHost, SDM_EVENT_BOOT_DEV_INSERT); }
3. 創建SDM設備
#include <SylixOS.h> static VOID __sdmDevCreate (__SDM_HOST *psdmhost);
該函數調用了API_SdCoreDevCreate函數創建SDM設備。
SD存儲設備插入後系統處理流程如圖 3-2所示。
圖 3-2 SD存儲設備插入後系統處理流程
4. 刪除SDM設備
#include <SylixOS.h> static VOID __sdmDevDelete (__SDM_HOST *psdmhost);
3.1.3 SD Core操作接口
SD Core設備是SD設備在Core層中的抽象實現,每一個SD設備都會對應一個SD Core設備。
1. 創建核心SD設備
#include <SylixOS.h> LW_API PLW_SDCORE_DEVICE API_SdCoreDevCreate (INT iAdpaterType, CPCHAR pcAdpaterName, CPCHAR pcDeviceName, PLW_SDCORE_CHAN psdcorechan);
函數實現的主要功能如下:
調用sdLib庫中的API_SdDeviceCreate創建SD設備;
註冊SD總線傳輸、控制、刪除的函數。
2. 刪除核心SD設備
#include <SylixOS.h> LW_API INT API_SdCoreDevDelete (PLW_SDCORE_DEVICE psdcoredevice);
3.1.4 SdLib具體實現
1. 初始化SD組建庫
#include <SylixOS.h> LW_API INT API_SdLibInit (VOID);
2. 創建一個SD設備
#include <SylixOS.h> LW_API PLW_SD_DEVICE API_SdDeviceCreate (CPCHAR pcAdapterName, CPCHAR pcDeviceName);
其中核心的邏輯是將SD Memory設備鏈入適配器的設備鏈表。
3. 刪除SD設備
#include <SylixOS.h> LW_API INT API_SdDeviceDelete (PLW_SD_DEVICE psddevice);
其中核心的邏輯是將SD Memory設備從適配器鏈表中刪除。
3.1.5 SD總線傳輸
SD Xfer實現的是SD模式下的傳輸接口。
在系統中總線結構基本上都需要實現以下兩個接口:
SDFUNC_pfuncMasterXfer——用於實現總線傳輸
SDFUNC_pfuncMasterCtl ——用於實現總線控制
在以上兩個接口基礎上,SD Xfer層實現了傳輸、控制、刪除函數。
1. 對設備發送一次請求
#include <SylixOS.h> LW_API INT API_SdDeviceTransfer (PLW_SD_DEVICE psddevice, PLW_SD_MESSAGE psdmsg, INT iNum);
其中核心的邏輯是調用Host層中的SDHCI接口__sdhciTransfer。
2. 對設備發送一次控制請求
#include <SylixOS.h> LW_API INT API_SdDeviceCtl (PLW_SD_DEVICE psddevice, INT iCmd, LONG lArg);
3.2 Client設備層
Client層實現具體的SD設備應用協議。Client層的源碼文件結構如圖 3-3所示。
圖 3-3 Client層源碼文件
3.2.1 SD Memory存儲設備接口
SD Memory處理和SD存儲卡(包括SD卡、MMC卡、EMMC)相關的具體設備協議。
當SD存儲卡作爲啓動設備或熱插拔設備接入後,系統會創建SD Memory設備並進行初始化,最後調用文件系統接口完成文件系統掛載的工作。
1. sdmemoryDrv.c中主要的函數和功能
(1)驅動安裝
代碼清單 3驅動安裝函數
LW_API INT API_SdMemDrvInstall (VOID) { _G_sddrvMem.SDDRV_cpcName ="/dev/sdmem"; _G_sddrvMem.SDDRV_pfuncDevCreate = __sdmemDevCreate; _G_sddrvMem.SDDRV_pfuncDevDelete = __sdmemDevDelete; API_SdmSdDrvRegister(&_G_sddrvMem); return(ERROR_NONE); }
當SD Memory設備接入時,系統會調用__sdmemDevCreate函數;當SD Memory設備移除時會調用__sdmemDevDelete函數。
(2)創建並初始化SD Memory設備,並掛載文件系統
(3)銷燬SD Memory設備,並卸載文件系統
2.sdmemory.c中主要的函數和功能
sdmemory.c實現了SD Memory設備創建、刪除、初始化、設備控制、文件系統讀寫接口等具體應用的邏輯。
(1)創建並初始化SD Memory設備
#include <SylixOS.h> LW_API PLW_BLK_DEV API_SdMemDevCreate (INT iAdapterType, CPCHAR pcAdapterName, CPCHAR pcDeviceName, LW_SDMEM_CHAN psdmemchan);
(2)刪除SD Memory設備
#include <SylixOS.h> LW_API INT API_SdMemDevDelete (PLW_BLK_DEV pblkdevice);
(3) 初始化SD Memory設備
SD Memory設備初始化具體流程可以參考SD標準協議,詳見4參考資料。
(4)SD Memory設備的塊讀取
代碼清單4 SD Memory設備的塊讀取
static INT __sdMemBlkRd (__PSD_BLK_DEV psdblkdevice, VOID *pvRdBuffer, ULONG ulStartBlk, ULONG ulBlkCount) { …… if (ulBlkCount <=1){ iError = __sdMemRdSingleBlk(psdcoredevice,(UINT8 *)pvRdBuffer,(UINT32)ulStartBlk); }else{ iError = __sdMemRdMultiBlk(psdcoredevice,(UINT8 *)pvRdBuffer, (UINT32)ulStartBlk,(UINT32)ulBlkCount); } …… }
其中核心的實現如代碼清單 4所示,根據讀取塊的數量選擇調用單塊讀取命令或多塊讀取命令。
單塊讀取的流程如圖 3-4所示。
圖 3-4單塊讀取流程
多塊讀取的流程如圖 3-5所示。
圖 3-5多塊讀取流程
(5)SD Memory設備的塊寫入
代碼清單 5 SD Memory設備的塊寫入
static INT __sdMemBlkWrt (__PSD_BLK_DEV psdblkdevice, VOID *pvWrtBuffer, ULONG ulStartBlk, ULONG ulBlkCount) { …… if (ulBlkCount <=1){ iError = __sdMemWrtSingleBlk(psdcoredevice,(UINT8 *)pvWrtBuffer,(UINT32)ulStartBlk); }else{ iError = __sdMemWrtMultiBlk(psdcoredevice,(UINT8 *)pvWrtBuffer, (UINT32)ulStartBlk,(UINT32)ulBlkCount); } …… }
其中核心的實現如代碼清單 5所示,根據寫入塊的數量選擇調用單塊寫入命令或多塊寫入命令。
單塊寫的流程如圖 3-6所示。
圖 3-6單塊寫流程
多塊寫的流程如圖 3-7所示,在每塊傳輸應答之後需要發送Stop標記。
圖 3-7多塊寫流程
3.3 Host層
3.3.1 SD Host接口
目前驅動編寫的思路是抽取出和SDHCI標準協議不兼容的部分在BSP包中實現。
3.3.2 SDHCI標準協議接口
1. 創建Host
#include <SylixOS.h> LW_API PVOID API_SdhciHostCreate (CPCHAR pcAdapterName, PLW_SDHCI_HOST_ATTR psdhcihostattr);
標準的SD協議要求驅動實現8bits/16bits/32bits讀接口和8bits/16bits/32bits寫接口,目前在sdhci.c中實現了標準的讀寫接口。
但是不同的芯片可能有自己不同的寄存器訪問驅動,所以在標準協議接口中需要判斷採用標準的讀寫接口還是驅動自己各異的驅動接口。
2. 總線傳輸
圖 3-8傳輸流程
總線傳輸如圖 3-8所示,分爲準備、傳輸、等待、清理四個階段。
3.3.3 BSP包中的驅動實現
1. 驅動初始化函數
代碼清單 7驅動初始化
static INT __imx6SdhciIoDrvInit (PLW_SDHCI_HOST_ATTR psdhcihostattr) { static SDHCI_DRV_FUNCS sdhcidrvfunc; sdhcidrvfunc.sdhciReadB = __imx6SdhciReadB; sdhcidrvfunc.sdhciReadW = __imx6SdhciReadW; sdhcidrvfunc.sdhciReadL = __imx6SdhciReadL; sdhcidrvfunc.sdhciWriteB = __imx6SdhciWriteB; sdhcidrvfunc.sdhciWriteW = __imx6SdhciWriteW; sdhcidrvfunc.sdhciWriteL = __imx6SdhciWriteL; psdhcihostattr->SDHCIHOST_pdrvfuncs =&sdhcidrvfunc; return(ERROR_NONE); }
這個函數中註冊了驅動中自己實現的讀寫接口,所以當系統調用到Host層最終的控制傳輸或數據傳輸時,會調用驅動自己實現的讀寫接口。
2. 註冊SD設備驅動
代碼清單 8註冊SD設備驅動
INT imx6ulSdiDrvInstall (UINT uiChannel, UINT32 uiSdCdPin, UINT32 uiSdWpPin, BOOL bIsBootDev) { /* * 創建HOST */ pvSdhciHost = API_SdhciHostCreate(psdichannel->SDICH_cpcHostName,&psdichannel->SDICH_sdhciattr); /* * 獲取創建的HOST */ pvSdmHost = API_SdhciSdmHostGet(pvSdhciHost); /* * 調用API_SdmEventNotify創建設備 * 或者在hotplug線程中創建設備 */ …… }
創建設備時,若是BOOT設備則在當前線程調用API_SdmEventNotify,若是熱插拔線程,則在熱插拔掃描函數中調用API_SdmEventNotify。
4. 參考資料
《SD Specifications Part 1 Physical Layer Simplified Specification Version 5.00 August 10, 2016》
《SD Specifications Part A2 SD Host Controller Simplified Specification Version 3.00 February 25, 2011》