SDIO 类设备驱动

更新时间:
2024-03-14
下载文档

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);
}
文档内容是否对您有所帮助?
有帮助
没帮助