1. 注册SD Memory设备驱动
在SylixOS的SD框架设计中,需要将主控制器驱动和设备驱动分别注册到SDM框架中以方便管理。使用内核SDM相关接口之前需要先调用 API_SdmLibInit 进行内核一些信号量之类的初始化,这个接口无参数,直接调用一下就可。初始化完后我们就可以注册Host或者设备驱动了,本次我们编写的是SD卡驱动,用于存储使用,所以可以注册SD Memory设备驱动到SDM,内核已经为我们封装好对应的接口 API_SdMemDrvInstall ,此接口同样没有参数,直接调用即可。
2. SD Host描述信息
为了让SDM管理Host驱动,SylixOS使用 SD_HOST 数据结构来为SDM描述一个控制器信息:
struct sd_host { CPCHAR SDHOST_cpcName; INT SDHOST_iType; #define SDHOST_TYPE_SD 0 #define SDHOST_TYPE_SPI 1 INT SDHOST_iCapbility; /* 主动支持的特性 */ #define SDHOST_CAP_HIGHSPEED (1 << 0) /* 支持高速传输 */ #define SDHOST_CAP_DATA_4BIT (1 << 1) /* 支持4位数据传输 */ #define SDHOST_CAP_DATA_8BIT (1 << 2) /* 支持8位数据传输 */ #define SDHOST_CAP_DATA_4BIT_DDR (1 << 3) /* 支持4位ddr数据传输 */ #define SDHOST_CAP_DATA_8BIT_DDR (1 << 4) /* 支持8位ddr数据传输 */ #define SDHOST_CAP_MMC_FORCE_1BIT (1 << 5) /* MMC卡 强制使用 1 位总线 */ #define SDHOST_CAP_SDIO_FORCE_1BIT (1 << 6) /* SDIO 卡 强制使用 1 位总线 */ #define SDHOST_CAP_SD_FORCE_1BIT (1 << 7) /* SD 卡 强制使用 1 位总线 */ #define SDHOST_CAP_HS200 (1 << 8) /* 支持 HS200 模式 */ #define SDHOST_CAP_HS400 (1 << 9) /* 支持 HS400 模式 */ VOID (*SDHOST_pfuncSpicsEn)(SD_HOST *psdhost); VOID (*SDHOST_pfuncSpicsDis)(SD_HOST *psdhost); INT (*SDHOST_pfuncCallbackInstall) ( SD_HOST *psdhost, INT iCallbackType, /* 安装的回调函数的类型 */ SD_CALLBACK callback, /* 回调函数指针 */ PVOID pvCallbackArg /* 回调函数的参数 */ ); INT (*SDHOST_pfuncCallbackUnInstall) ( SD_HOST *psdhost, INT iCallbackType /* 安装的回调函数的类型 */ ); #define SDHOST_CALLBACK_CHECK_DEV 0 /* 卡状态检测 */ #define SDHOST_DEVSTA_UNEXIST 0 /* 卡状态:不存在 */ #define SDHOST_DEVSTA_EXIST 1 /* 卡状态:存在 */ VOID (*SDHOST_pfuncSdioIntEn)(SD_HOST *psdhost, BOOL bEnable); BOOL (*SDHOST_pfuncIsCardWp)(SD_HOST *psdhost); VOID (*SDHOST_pfuncDevAttach)(SD_HOST *psdhost, CPCHAR cpcDevName); VOID (*SDHOST_pfuncDevDetach)(SD_HOST *psdhost); };
这里简介其中几个重要的成员作用:
- SDHOST_cpcName:主控制器的名字,就是上一章节中使用的”/bus/sd/xxx“,这样就通过一个唯一的名字将 SD_HOST 和具体的SD适配器绑定起来了。
- SDHOST_iType:SD总线的类型,有 SDHOST_TYPE_SD 和 SDHOST_TYPE_SPI 两种,我们本次驱动中设置为 SDHOST_TYPE_SD 。
- SDHOST_iCapbility:控制器属性,比如支持的线宽、是否支持高速模式(50MHz时钟)等等,本SD卡驱动中设置为 SDHOST_CAP_HIGHSPEED | SDHOST_CAP_DATA_4BIT ,表示控制器使用4线和高速传输模式。
- SDHOST_pfuncCallbackInstall:历史遗留接口,实际无作用,但是必须要初始化,本驱动中初始化为空函数。
- SDHOST_pfuncCallbackUnInstall:历史遗留接口,实际无作用,但是必须要初始化,本驱动中初始化为空函数。
- 还有其他一些成员由于本次驱动中没有使用到所以不做过多介绍,这样减少干扰项,方便理解简单的SD驱动组成。
3. 定义SD Host描述信息
我们定义一个全局变量来表示SD Host描述信息,如下所示:
static SD_HOST sdhost = { .SDHOST_cpcName = "/bus/sd/0", .SDHOST_iType = SDHOST_TYPE_SD, .SDHOST_iCapbility = SDHOST_CAP_HIGHSPEED | SDHOST_CAP_DATA_4BIT, .SDHOST_pfuncCallbackInstall = sdCallBackInstall, .SDHOST_pfuncCallbackUnInstall = sdCallBackUnInstall, };
定义的同时对变量进行初始化,前三个成员的意义在上面已经讲解过,最后两个回调函数我们这里只需要实现为空函数即可,如下所示:
static int sdCallBackInstall (SD_HOST *sdhost, int type, SD_CALLBACK callback, void *arg) { return 0; } static int sdCallBackUnInstall (SD_HOST *sdhost, int type) { return 0; }
4. 注册SD Host控制器驱动
注册Host驱动到SDM是通过 API_SdmHostRegister 接口实现的,其函数原型如下:
LW_API PVOID API_SdmHostRegister(SD_HOST *psdhost);
- 参数为初始化好的 SD_HOST 描述信息。
- 注册成功返回内核初始化好的 __SDM_HOST 数据结构,由于此数据结构在具体的驱动中不会访问其中的成员,所以返回值是一个void *类型指针,这个 SDM_HOST 后续会被其他接口作为参数使用,所以还需要定义一个全局void *指针变量来存放返回值。
根据上述信息,注册SD Host控制器驱动代码如下:
static void *sdmhost; // 注册host信息到sdm框架 sdmhost = API_SdmHostRegister(&sdhost);
5. 通知SDM立即初始化设备
由于SD卡是支持热插拔的,所以SD协议栈也必须提供对应的接口来处理热插拔事件。在SylixOS下这是通过 API_SdmEventNotify 来实现的,这个接口主要用来通知SDM层有事件产生了,比如热插拔、SDIO中断等,其函数原型如下:
LW_API INT API_SdmEventNotify(PVOID pvSdmHost, INT iEvtType);
- pvSdmHost:SDM_HOST数据结构,就是之前通过 API_SdmHostRegister 接口注册成功后的返回值。
- iEvtType:通知SDM的事件类型,目前有四种:SDM_EVENT_DEV_INSERT和SDM_EVENT_DEV_REMOVE表示SD设备的插入和拔出,SDM_EVENT_SDIO_INTERRUPT表示产生了SDIO中断,SDM_EVENT_BOOT_DEV_INSERT作用跟SDM_EVENT_DEV_INSERT一样,但是区别是SDM_EVENT_DEV_INSERT和SDM_EVENT_DEV_REMOVE将内核SD设备的创建和卸载推迟到内核热插拔线程中统一处理的,而SDM_EVENT_BOOT_DEV_INSERT是直接创建内核SD设备,以最快的速度创建好SD设备和准备好驱动,以让后续文件系统挂载使用。
本次驱动中我们不实现热插拔功能,在实际SD驱动中,一般都是通过GPIO来检测SD状态。通过上述信息,通知SDM立即初始化设备的代码如下:
// 通知sdm框架立即初始化sd卡,创建块设备等 API_SdmEventNotify(sdmhost, SDM_EVENT_BOOT_DEV_INSERT);
处于简单原则考虑,本教程不会去检测接口调用返回值做错误判断,后续的网络驱动教程也是如此。
附源码:
/* * sd.c * * Created on: Apr 23, 2022 * Author: gewenbin */ #define __SYLIXOS_KERNEL #include <SylixOS.h> #include <linux/compat.h> static int sdCallBackInstall (SD_HOST *sdhost, int type, SD_CALLBACK callback, void *arg) { return 0; } static int sdCallBackUnInstall (SD_HOST *sdhost, int type) { return 0; } static int sdIoctl (PLW_SD_ADAPTER psdadapter, int cmd, long arg) { switch (cmd) { case SDBUS_CTRL_POWEROFF: // TODO:关闭电源 break; case SDBUS_CTRL_POWERUP: case SDBUS_CTRL_POWERON: // TODO:使能电源 break; case SDBUS_CTRL_SETBUSWIDTH: // TODO:设置线宽 break; case SDBUS_CTRL_SETCLK: // TODO:设置时钟频率 break; case SDBUS_CTRL_DELAYCLK: break; case SDBUS_CTRL_GETOCR: // TODO:获取支持的电压情况 *(UINT32 *)arg = SD_VDD_32_33 | SD_VDD_33_34; break; default: return -1; } return 0; } static int sdTransfer (PLW_SD_ADAPTER psdadapter, PLW_SD_DEVICE psddevice, PLW_SD_MESSAGE psdmsg, int num) { return 0; } static LW_SD_FUNCS sdfuncs = { .SDFUNC_pfuncMasterXfer = sdTransfer, .SDFUNC_pfuncMasterCtl = sdIoctl, }; static SD_HOST sdhost = { .SDHOST_cpcName = "/bus/sd/0", .SDHOST_iType = SDHOST_TYPE_SD, .SDHOST_iCapbility = SDHOST_CAP_HIGHSPEED | SDHOST_CAP_DATA_4BIT, .SDHOST_pfuncCallbackInstall = sdCallBackInstall, .SDHOST_pfuncCallbackUnInstall = sdCallBackUnInstall, }; static void *sdmhost; int sdDevCreate (void) { // TODO:硬件初始化 // 创建SD适配器 API_SdAdapterCreate("/bus/sd/0", &sdfuncs); // 注册SD存储设备类驱动 API_SdmLibInit(); API_SdMemDrvInstall(); // 注册host信息到sdm框架 sdmhost = API_SdmHostRegister(&sdhost); // 通知sdm框架立即初始化sd卡,创建块设备等 API_SdmEventNotify(sdmhost, SDM_EVENT_BOOT_DEV_INSERT); return 0; }
评论