全志D1开发(八)SD驱动之注册Host信息

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2022年5月3日15:38:06 评论 729

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_SDSDHOST_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;
}
gewenbin
  • 本文由 发表于 2022年5月3日15:38:06
  • 转载请务必保留本文链接:http://www.databusworld.cn/10695.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: