SDIO 类设备驱动
概述
SDIO 驱动相关信息位于“system/device/sdcard/core/sdiodrvm.h”中。另外从下表可以看出,在 SPI 模式下,由 DATA1 充当 SDIO 中断信号引脚,但 SPI 控制器本身无法捕捉该中断信号,因此 SPI 模式下要支持 SDIO 设备将对驱动程序有更多额外的需求,且不一定能很好地实现,软件设计中通常不必考虑在 SPI 模式下支持 SDIO。由于 SDIO 驱动相关设备太多,我们这里仅介绍下必须的函数以及部分常用的函数。
SDIO 类设备驱动注册
首先需要了解的就是如何注册一个驱动,使用 API_SdmSdioDrvRegister 函数向 SDM 注册一个 SDIO 类设备驱动:
#include <SylixOS.h>
INT API_SdmSdioDrvRegister(SDIO_DRV *psdiodrv);
函数 API_SdmSdioDrvRegister 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- psdiodrv 参数:SDIO 驱动对象,包含 SDIO 驱动注册信息。
注意:
注意SDM内部会直接引用该对象, 因此该对象需要持续有效。
向 SDM 注册一个具体的 SDIO 类设备驱动(比如 SDIO 无线网卡,SDIO 串口等)所需的结构体详细描述如下:
struct sdio_drv {
LW_LIST_LINE SDIODRV_lineManage; /* 驱动挂载链 */
CPCHAR SDIODRV_cpcName; /* 驱动名称 */
INT (*SDIODRV_pfuncDevCreate)(SDIO_DRV *psdiodrv,
SDIO_INIT_DATA *pinitdata,
VOID **ppvDevPriv);
INT (*SDIODRV_pfuncDevDelete)(SDIO_DRV *psdiodrv, VOID *pvDevPriv);
VOID (*SDIODRV_pfuncIrqHandle)(SDIO_DRV *psdiodrv, VOID *pvDevPriv);
SDIO_DEV_ID *SDIODRV_pdevidTbl;
INT SDIODRV_iDevidCnt;
VOID *SDIODRV_pvSpec;
atomic_t SDIODRV_atomicDevCnt;
};
对比 SD 驱动,两者的结构非常相似,不过后者多了几个与 SDIO 相关的数据信息。仅针对与 SD 驱动不同的地方进行说明:
- SDIODRV_pfuncIrqHandle:该驱动对应的设备发生 SDIO 中断时处理方法。
- SDIODRV_pdevidTbl:当前 SDIO 驱动所支持的设备相关信息链表。
- SDIODRV_iDevidCnt:设备相关信息链表中节点个数。
在其设备创建方法 SDIODRV_pfuncDevCreate 中,由 SDIO_INIT_DATA 替换了 SD 驱动中的 PLW_SDCORE_DEVICE 数据类型。 SDIO_INIT_DATA 结构体详细描述如下:
#define SDIO_FUNC_MAX 8
struct sdio_init_data {
SDIO_FUNC INIT_psdiofuncTbl[SDIO_FUNC_MAX];
INT INIT_iFuncCnt; /* 不包括Func0 */
PLW_SDCORE_DEVICE INIT_psdcoredev;
SDIO_CCCR INIT_sdiocccr;
};
typedef struct sdio_init_data SDIO_INIT_DATA;
首先需要明白的是, SDIO_INIT_DATA 数据对象是由 SDM 层提供的,实际上,这正是 SDIO Base 驱动在调用具体的 SDIO 驱动之前,进行设备初始化时获得的 SDIO 设备的通用信息,因此把它称作“sdio init data”,这些信息可供具体的 SDIO 设备驱动直接使用,而无需再次去获取或处理。这一点与 USB 主栈枚举设备的过程非常相似,即先获得设备描述符、配置描述符、接口描述符、端点描述符等信息,随后根据这些信息去匹配正确的 USB 设备类驱动,当找到具体的驱动时,调用其对应的设备创建方法,并且会提供相应的接口,让这些方法能够获取到这些通用信息。 SDIO_INIT_DATA 里面,主要有 SDIO_FUNC 结构体成员和 SDIO_CCCR 结构体成员,两者均与 SDIO 协议息息相关,这里只关心驱动,故不详细展开,另外还有一个最重要的结构体指针 INIT_psdcoredev,后续对该设备的操作全部依赖于此指针。
SDIODRV_pdevidTbl 与 SDIODRV_iDevidCnt 两个成员含义分别是当前 SDIO 驱动可支持的 SDIO 设备相关信息链表及链表中设备个数。 SDIO_DEV_ID 与 USB 或 PCI 里对设备标识的定义类似,只不过它相对更加简单,包括设备的接口类型(比如无线网卡,GPS,串口等)、厂商标识、当前设备编码,详细描述如下:
struct sdio_dev_id {
UINT8 DEVID_ucClass; /* Std interface or SDIO_ANY_ID */
UINT16 DEVID_usVendor; /* Vendor or SDIO_ANY_ID */
UINT16 DEVID_usDevice; /* Device ID or SDIO_DEV_ID_ANY */
};
#define SDIO_DEV_ID_ANY (~0)
SDIO 类设备驱动操作接口
SDIO 类设备驱动不需要关心底层驱动如何实现,SylixOS 已经封装好相关函数,在系统内实现的声明位于“libsylixos/SylixOS/system/device/sdcard/core/sdiocoreLib.h”中。下面仅介绍常用的几个函数,其原型如下:
#include #include <SylixOS.h>
INT API_SdioCoreDevReset(PLW_SDCORE_DEVICE psdcoredev);
INT API_SdioCoreDevReadByte(PLW_SDCORE_DEVICE psdcoredev,
UINT32 uiFn,
UINT32 uiAddr,
UINT8 *pucByte);
INT API_SdioCoreDevWriteByte(PLW_SDCORE_DEVICE psdcoredev,
UINT32 uiFn,
UINT32 uiAddr,
UINT8 ucByte);
函数 API_SdioCoreDevReset 是用来重启 SDIO 设备,其参数为指向 SD 核心设备结构体的指针,即 SDIO_INIT_DATA 结构体中的 INIT_psdcoredev 成员。
函数 API_SdioCoreDevReadByte 用来从 SDIO 设备读取指定 IO 功能上指定地址的一字节数据。
类似的,函数 API_SdioCoreDevWriteByte 用来向指定 IO 功能上指定地址写一个字节的数据。
下面给出了 SDIO 类设备驱动的通用实现。
所用宏定义及全局变量定义如下:
#define __SYLIXOS_KERNEL
#include <SylixOS.h>
#define SDIO_VENDOR_ID_V1 0x0001
#define SDIO_VENDOR_ID_V2 0x0002
#define SDIO_DEVICE_ID_D1 0x0001
#define SDIO_DEVICE_ID_D2 0x0002
#define SDIO_ERR(fmt, arg...) printk("[SDIO]"fmt, ##arg);
static SDIO_DEV_ID _G_sdioDevIdTbl[] = {
{
.DEVID_ucClass = SDIO_DEV_ID_ANY,
.DEVID_usVendor = SDIO_VENDOR_ID_V1,
.DEVID_usDevice = SDIO_DEVICE_ID_D1,
}, {
.DEVID_ucClass = SDIO_DEV_ID_ANY,
.DEVID_usVendor = SDIO_VENDOR_ID_V2,
.DEVID_usDevice = SDIO_DEVICE_ID_D2,
},
};
static SDIO_DRV _G_sdioDrv = {
.SDIODRV_cpcName = "sdio_test",
.SDIODRV_pfuncDevCreate = __sdioDevCreate,
.SDIODRV_pfuncDevDelete = __sdioDevDelete,
.SDIODRV_pfuncIrqHandle = __sdioIrqHandle,
.SDIODRV_pdevidTbl = _G_sdioDevIdTbl,
.SDIODRV_iDevidCnt = (sizeof(_G_sdioDevIdTbl) / sizeof(_G_sdioDevIdTbl[0])),
};
SDIO 设备结构体定义如下:
typedef struct {
SDIO_INIT_DATA *pinitdata; /* 针对每个设备分别保存其数据 */
PVOID pvPriv; /* SDIO 类设备私有数据示例 */
} MY_SDIO_DEV;
SDIO 设备的创建函数实现如下:
static INT __sdioDevCreate (SDIO_DRV *psdiodrv,
SDIO_INIT_DATA *pinitdata,
VOID **ppvDevPriv)
{
MY_SDIO_DEV *pmysdiodev;
INT iRet;
printk("This is %s.\r\n", __func__);
pmysdiodev = (MY_SDIO_DEV*)__SHEAP_ALLOC(sizeof(MY_SDIO_DEV));
if (pmysdiodev == LW_NULL) {
SDIO_ERR("%s err: Low memory, create sdio device fail!\r\n", __func__);
return (PX_ERROR);
}
pmysdiodev->pinitdata = pinitdata; /* 保存每个设备的基本数据 */
iRet = __mySdioDevInit(pmysdiodev);
if (iRet != ERROR_NONE) {
__SHEAP_FREE(pmysdiodev);
SDIO_ERR("%s err: Sdio device init fail!\r\n", __func__);
return (PX_ERROR);
}
*ppvDevPriv = (VOID *)pmysdiodev;
return (ERROR_NONE);
}
SDIO 设备的删除函数实现如下:
static INT __sdioDevDelete (SDIO_DRV *psdiodrv, VOID *pvDevPriv)
{
MY_SDIO_DEV *pmysdiodev = (MY_SDIO_DEV *)pvDevPriv;
INT iRet;
printk("This is %s.\r\n", __func__);
iRet = __mySdioDevDeinit(pmysdiodev);
if (iRet != ERROR_NONE) {
SDIO_ERR("%s err: Sdio device deinit fail!\r\n", __func__);
return (PX_ERROR);
}
__SHEAP_FREE(pmysdiodev);
return (ERROR_NONE);
}
SDIO 设备的中断处理函数实现如下:
static VOID __sdioIrqHandle (SDIO_DRV *psdiodrv, VOID *pvDevPriv)
{
printk("This is %s.\r\n", __func__);
return;
}
SDIO 类设备驱动安装函数如下:
INT sdioDrvInstall (VOID)
{
INT iRet;
iRet = API_SdmSdioDrvRegister(&_G_sdioDrv);
return (iRet);
}
卸载 SDIO 驱动的函数如下:
INT sdioDrvUninstall (VOID)
{
INT iRet;
iRet = API_SdmSdioDrvUnRegister(&_G_sdioDrv);
return (iRet);
}
SDIO 设备初始化函数实现如下:
static int __mySdioDevInit(MY_SDIO_DEV *pmysdiodev)
{
PLW_SDCORE_DEVICE pSdCoreDevice;
printk("This is %s.\r\n", __func__);
/*
* 自行实现设备初始化
*/
pSdCoreDevice = pmysdiodev->pinitdata->INIT_psdcoredev;
API_SdioCoreDevReset(pSdCoreDevice); /* 设备重启 */
return (ERROR_NONE);
}
SDIO 设备逆初始化函数结构如下:
static int __mySdioDevDeinit(MY_SDIO_DEV *pmysdiodev)
{
printk("This is %s.\r\n", __func__);
/*
* 自行实现设备逆初始化
*/
return (ERROR_NONE);
}