分类: C/C++
2015-07-24 19:13:49
上一篇说道,SD Core设备主要是封装了底层的细节,为应用层提供唯一的传输接口。SDM,即SD Driver Management,是用来管理SD协议栈里的驱动的,包括硬件控制器驱动和设备类驱动(这与USB协议栈的设计非常类似),以此将两者隔离开来。SDM的功能主要有以下几点:
1. 对上,提供Client层驱动注册的接口,包括SD 基础驱动和 SDIO子设备驱动
2. 对下,提供Host层驱动信息注册的接口,这些信息一部分是应用驱动需要的,比如控制器类型,支持的位宽等等,由SDM管理,在适当的时候提供给应用驱动。这些信息还包括控制器本身在一些特定条件下的行为定义,比如当一个设备经设备驱动创建或移除后,主控如何去处理的方法。因为在具体的Host驱动实现中,其内部状态可能会依赖插入其控制器的设备的状态(比如SDHCI标准驱动就需要提供此信息)。
3. 为每类设备提供一些设备相关的内部信息,如:可能存在使用同一驱动的设备,各个设备的名称应该是不同的,由SDM给出。
4. 提供让Host驱动通知SDM设备状态改变的通知机制,当设备插入/拔出,由SDM负责设备驱动匹配,进行设备的自动创建和删除工作(在以前的版本中,由于没有SDM,都是由控制器驱动来完成设备的创建和删除,非常不合理)。
为了完成以上功能,在实现具体的SDM代码之前,我们需要考虑,在实际应用中,一个支持热插拔多对象的系统,都会存在哪些必须解决的问题,笔者总结有如下最重要的几点:
1. 如何做到Client层与Host层真正隔离?
前面说道,Client层实际上只需要一个SD Core设备传输对象就可以了,为了让Client不比关系SD Core设备的细节,必须让SDM来负责管理(创建、删除)SD Core设备。因此,SDM必须能够得到创建一个SD Coe设备对象所必要的硬件信息。这写信息应该由注册到SDM的控制器驱动提供。
2. 如何让SDM感知到设备的状态变化?
这里的状态变化主要指的是插入/拔出状态(后续可能在具体的SD设备上有更多其他状态)。因为有了SDM后,设备的创建和删除工作由SDM内部处理。此状态首先由控制器端检测到,因此需要提供某种机制,让控制器端将状态报告给SDM。
3. 如何正确处理设备状态变化?
这主要存在两类问题:其一是, SDM如何判断是哪一个Host上插入或删除了设备,也就是处理设备与控制器的关联性问题。其二是,SDM如何以正确的驱动创建当前设备、如何以正确的驱动删除设备,也就是设备与设备驱动的关联性问题。因此,SDM内部必须完成对设备驱动、控制器驱动、设备实体对象三者之间数据结构良好的定义以及管理措施,能够有效地在合适的时候将三者关联(或取消关联)。
4. 设备特征信息管理
SDM应该假设在一个系统中可能同时存在多个SD控制器,SD设备,并且可能有很多SD设备都是相同类型的,内部如何区分它们?这就需要为相同类型的对象设置唯一的特征信息。
5. 考虑对象生命周期的依赖性
即SDM应该考虑内部管理的对象(就是上面所说的设备、设备驱动、控制器驱动等)它们之间的相互依赖关系,有的是没有必然关系的,有的存在依赖关系,比如当有设备在使用驱动时,该驱动就不应该被注销或删除。
6. 如何为一个新的设备找到合适的设备驱动?
由于SD设备只有SD存储卡和SDIO卡两种,这里的驱动也就只有SD存储卡驱动和SDIO驱动两大类。但SDIO设备又存在不同的应用,比如WIFI、GPS、UART等,实际上也就存在很多不同的驱动。SDM需要实现为一个新的设备找到正确的设备驱动的方法,即驱动匹配工作。
7. 未定义设备
指的是如果一个插入的设备没有对应的驱动,该如何处理?
在接下来的讲解中,所有涉及的数据结构和API函数,都是围绕以上问题设计的。
这里称为Host信息而不是驱动,是因为严格来讲,之前所说的Hos总线适配器才是驱动,因为它实实在在地完成了控制器硬件上的数据传输工作。这里的信息只是为配合SDM管理而产生的。在文件sdcard/sddrvm.h文件中,其数据结构的定义如下:
SDHOST_iType:控制器的类型,是SD还是SPI。SDM将通过名称和类型找到系统中对应的总线适配器,以便创建对应的SD Core设备对象。
SDHOST_iCapbility:控制器的功能特性。这些特性可能是和硬件电路的设计有关的(比如支持的速度、使用的传输位宽等),需要由驱动提供。
SDHOST_pfuncSpicsEn:
SDHOST_pfuncSpicsDis:如果是SPI,则该方法必须提供。这两个函数也是与硬件电路设计有关,由驱动提供。
SDHOST_pfuncCallbackInstall:
SDHOST_pfuncCallbackUnInstall:必须提供。这两个函数为安装上层的回电函数的方法。在后面的驱动开发里将具体说明如何使用。
SDHOST_pfuncSdioIntEn:用于设置控制器的SDIO中断的使能或禁止状态。因为总线适配器里面没有处理SDIO中断的相关措施,所以如果Host支持SDIO,则该方法必须提供。
SDHOST_pfuncIsCardWp:用于判断SD卡(主要是SD存储卡)是否写保护。卡的写保护检测也和具体的电路设计相关,如果不支持,则Host驱动可以不提供该方法。
SDHOST_pfuncDevAttach:
SDHOST_pfuncDevDetach: 可选。这两个函数为HOST驱动提供了在SDM创建/删除设备时其处理内部信息或者状态的一个机制。Host驱动不一定需要处理,因此是可选实现的。
有了以上这些信息,SDM就能正确的完成SD Core设备的创建工作,并能为客户驱动提供一些实用的额外信息。这就解决了上面所讲的第一个问题。向SDM注册Host信息的API如下:
PVOID API_SdmHostRegister(SD_HOST *psdhost);
该函数成功,将返回一个Host在SDM里面管理的一个对象指针。驱动程序使用该指针向SDM报告自己检测到的相关事件。其API如下:
INT API_SdmEventNotify(PVOID pvSdmHost, INT iEvtType);
该函数第一个参数即为上面一个函数的返回值,第二个参数表示报告的事件类型,有如下定义:
#define SDM_EVENT_DEV_INSERT 0
#define SDM_EVENT_DEV_REMOVE 1
#define SDM_EVENT_SDIO_INTERRUPT 2
SDM根据接收到的不同事件类型,处理不同的工作。具体如何处理,不需要Host驱动关心。从这里也看出,一个控制器驱动自始至终都不关心当前系统的设备情况,只负责处理自身相关的信息。这实际上解决了上面的第一和第二点问题。
SD通用设备类驱动主要是供SD存储和SDIO基础驱动使用,其结构如下:
SDDRV_lineManage:SDM内部将注册的驱动以链表的形式管理起来。
SDDRV_cpcName:驱动名称。
SDDRV_pfuncDevCreate:驱动创建对应设备的方法。参数 psdcoredev由SDM提供,输出参数ppvDevPriv保存创建的设备私有数据。
SDDRV_pfuncDevDelete:驱动删除对应设备的方法。参数pvDevPriv即为创建时的设备私有数据。上面的设备创建和删除方法的定义是很多系统中采用的形式,即驱动完成具体的工作和自身的数据处理,而SDM负责在何时使用驱动处理这些工作。
SDDRV_atomicDevCnt:该驱动对应的设备计数,即当前有多少个设备在使用该驱动,此信息的主要用途是判断当前驱动是否能被删除,如上面所讲的第5点问题。
SDDRV_pvSpec:由SDM内部使用。驱动程序本身不关心该成员的意义。当前,SDM内部使用此指针保存一个“驱动为它的设备分配设备单元号的【数据对象】”,解决上面的第4点问题。
可以看到,一个设备类驱动完全不必关心硬件控制器的任何信息。使用如下API向SDM注册一个SD驱动:
INT API_SdmSdDrvRegister(SD_DRV *psddrv);
SDIO驱动相关信息位于SylixOS/system/device/sdcard/core/sdiodrvm.h中,一个具体的SDIO设备驱动(比如SDIO无线网卡,SDIO串口等)结构定义如下:
对比SD驱动,两者的结构非常相似,不过后者多了几个与SDIO相关的数据信息。在其设备创建方法SDIODRV_pfuncDevCreate中,由SDIO_INIT_DATA替换了SD驱动中的PLW_SDCORE_DEVICE数据类型。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协议息息相关。由于本博文的重点是软件设计的结构而非协议本身,因此SDIO协议的详细信息,将会在其他文章中,本篇不再作介绍。
SDIODRV_pdevidTbl与SDIODRV_iDevidCnt两个成员定义了当前SDIO驱动可支持的SDIO设备ID。SDIO_DEV_ID与USB或PCI里对设备标识的定义类似,只不过它相对更加简单,包括设备的接口类型(比如无线网卡,GPS,串口等)、厂商标识、当前设备编码。如下定义:
此外,SDIODRV_pfuncIrqHandle也是一个SDIO驱动必须实现的方法,当控制器产生SDIO中断时,SDM层会调用该方法,以完成具体设备对中断的处理。使用以下API向SDM注册一个SDIO驱动:
INT API_SdmSdioDrvRegister(SDIO_DRV *psdiodrv);
孤儿(orphan)设备指的是在系统中存在与其对应的设备实体,但是没有与之对应的驱动程序,该设备当前还无法正常工作,但它仍然处于系统的管理之下。定义孤儿设备,是为了满足实用性的需求。这里我们可以对比在Windows系统中,当我们插入一个还没有安装任何驱动的USB设备时,在设备管理器中将会看到一个图标带有问号的设备,并显示该设备不能正常工作。如果此时我们安装该USB设备的驱动,该设备随后就能工作,而不需要重新插拔设备或重启系统。这种策略,让设备驱动与设备硬件本身在系统上的存在顺序没有任何依赖关系,这给实际应用带来了更大的灵活性。而在SylixOS(或是Linux、Unix等)的标准IO设备中,创建设备时必须指明该设备的驱动程序,否则不能创建设备,这说明标准IO驱动与设备实体的产生存在必然的依赖关系。
SDM同样支持孤儿设备的管理,这样达到的效果就是,注册设备驱动,注册控制器驱动和合适插如SD设备三者之间的操作顺序完全独立,只要他们三者都存在,对应的SD设备就能正确创建并正常工作。
总体来看,SDM组件解决了第2节中列出的所有问题,当前已经满足绝大多数需求。