第一章 rt-thread设备驱动模型

rt-thread设备驱动框架

1. 设备操作接口

rt-thread提供了一套通用的设备驱动框架,是为了应用开发使用统一的接口来操作设备,从而降低应用程序和驱动程序的耦合性。这样的驱动框架带来的好处是,当底层设备驱动更换后,上层应用可以不进行修改。

/* 根据设备名查找设备得到rt_device */
rt_device_t rt_device_find(const char* name)
/* 初始化设备 */
rt_err_t rt_device_init(rt_device_t dev)
/* 打开设备 */
rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflags)
/* 关闭设备 */
rt_err_t rt_device_close(rt_device_t dev)
/* 从设备读数据 */
rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
/* 向设备写数据 */
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
/* 控制设备 */
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg)

上述接口为应用层操作设备的统一接口,其操作设备的流程为:

  1. 根据设备名,调用rt_device_find查找设备的rt_device对象。
  2. rt_device为实参调用设备操作接口rt_device_opnert_device_read等。

2. 内核对象

rt-thread的设备驱动框架与内核对象是深度绑定的,在介绍设备驱动框架的时候有必要说明一下rt-thread的内核对象组织形式。rt-thread的内核中抽象了很多对象,比如线程对象、信号量对象、互斥体对象、消息队列对象、设备对象等。下面为内核中所有对象的枚举。

enum rt_object_class_type
{
    RT_Object_Class_Null          = 0x00,      /**< The object is not used. */
    RT_Object_Class_Thread        = 0x01,      /**< The object is a thread. */
    RT_Object_Class_Semaphore     = 0x02,      /**< The object is a semaphore. */
    RT_Object_Class_Mutex         = 0x03,      /**< The object is a mutex. */
    RT_Object_Class_Event         = 0x04,      /**< The object is a event. */
    RT_Object_Class_MailBox       = 0x05,      /**< The object is a mail box. */
    RT_Object_Class_MessageQueue  = 0x06,      /**< The object is a message queue. */
    RT_Object_Class_MemHeap       = 0x07,      /**< The object is a memory heap. */
    RT_Object_Class_MemPool       = 0x08,      /**< The object is a memory pool. */
    RT_Object_Class_Device        = 0x09,      /**< The object is a device. */
    RT_Object_Class_Timer         = 0x0a,      /**< The object is a timer. */
    RT_Object_Class_Module        = 0x0b,      /**< The object is a module. */
    RT_Object_Class_Memory        = 0x0c,      /**< The object is a memory. */
    RT_Object_Class_Unknown       = 0x0e,      /**< The object is unknown. */
    RT_Object_Class_Static        = 0x80       /**< The object is a static object. */
};

_object_container是一个成员类型为struct rt_object_information的数组,定义如下:

static struct rt_object_information _object_container[RT_Object_Info_Unknown] =
{
    /* initialize object container - thread */
    {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
    /* initialize object container - semaphore */
    {RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
//.......
#ifdef RT_USING_DEVICE
    /* initialize object container - device */
    {RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
};

这个数组是在代码中静态定义的,其数据成员跟用户配置了那些功能有关。比如,用户使用menuconfig使能了RT_USING_DEVICE,那么rt_device的这个数组成员就会编译进代码,否则不会。这个数组里面定义了内核中所有的对象信息,比如线程rt_thread对象信息、信号量rt_semaphore对象信息、设备rt_device对象信息等。

对象信息使用结构rt_object_information来描述:

struct rt_object_information
{
    enum rt_object_class_type type;                     /**< object class type */
    rt_list_t                 object_list;              /**< object list */
    rt_size_t                 object_size;              /**< object size */
};

该结构体中的type成员用来说明是哪一类对象,如RT_Object_Class_ThreadRT_Object_Class_SemaphoreRT_Object_Class_MutexRT_Object_Class_Device等。object_size描述的是对象的大小,如sizeof(struct rt_thread)sizeof(struct rt_device)。成员object_list是一个双向链表,同一类型的对象就会通过链表挂载到对应对象信息的链表上。

rt_object_information用来描述某一类内核对象的信息,具体的对象则使用struct rt_object来描述:

struct rt_object
{
    char       name[RT_NAME_MAX];                       /**< name of kernel object */
    rt_uint8_t type;                                    /**< type of kernel object */
    rt_uint8_t flag;                                    /**< flag of kernel object */

#ifdef RT_USING_MODULE
    void      *module_id;                               /**< id of application module */
#endif /* RT_USING_MODULE */
    rt_list_t  list;                                    /**< list node of kernel object */
};

其中,成员typert_object_informationtype相同,都表示是内核中的哪一类对象。name为对象的名字,在设备对象中,rt_device_find就是通过name进行比对查找的。list就是链表节点,用于挂载到对象信息的链表中。rt_object一般会嵌入到更大的结构体中,按照面向对象的思想作为一个基类。综上,rt-thread中内核对象的组织形式如下:
在这里插入图片描述

类型为RT_Object_Class_Device的对象都被挂载到相同类型的对象信息链表上,如果要查找某一个设备只需要遍历这个链表,逐个比较设备名就行了,函数rt_object_find就是这样实现的。

3. 设备驱动模型

设备驱动模型整体上可分为三层:

  • 应用层:应用程序调用设备操作接口rt_device_findrt_device_read等来操作设备
  • 核心层:提供驱动对象的注册接口、把驱动对象挂载到对应设备信息的链表上
  • 驱动相关层:定义驱动对象、实现设备操作函数、调用驱动对象的注册函数进行注册

在应用层,就是调用驱动框架提供的设备标准操作函数。核心层为驱动框架的主要部分,这一部分与前文介绍的内核对象组织形式深度绑定。驱动相关层就是具体设备的的驱动了,开发也较为简单只要实现相关的操作函数,然后调用注册接口进行注册就完成了驱动开发。

驱动框架的核心层的逻辑为:提供一个设备抽象数据结构,这个设备抽象数据结构包含一系列设备操作函数,这些函数需要驱动开发者实现,最后调用设备对象的注册函数进行注册。Linux驱动框架就是采用这种实现逻辑,例如熟悉的字符设备cdev和设备操作函数file_operations

rt-thread的驱动框架也采用相同的逻辑,首先对设备进行了抽象,使用一个rt_device结构体进行描述:

struct rt_device
{
    struct rt_object          parent;                   /**< inherit from rt_object */

    enum rt_device_class_type type;                     /**< device type */
    rt_uint16_t               flag;                     /**< device flag */
    rt_uint16_t               open_flag;                /**< device open flag */

    /* device call back */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};
  • 上述代码进行了部分删减,可以看到这个结构体中有rt_object内核对象,其作用就是将这个设备结构体挂载到设备对象信息的链表上,函数rt_object_find就可以通过遍历设备信息链表来找到这个设备的rt_device,如前文1小结的图所示。
  • init、open、close、read等就是设备的操作函数,这些函数需要驱动开发者实现。
  • 驱动框架最终会提供一个设备注册函数,调用流程如下:
rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags)
    rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
		1. struct rt_object_information *information;
		2. rt_list_insert_after(&(information->object_list), &(object->list));

这个函数的作用就是将rt_device中的rt_object挂载到类型为RT_Object_Class_Device设备信息的链表上,从而完成这个设备的注册。

  • 对于应用调用流程,首先通过函数rt_object_find在类型为RT_Object_Class_Device设备信息的链表上遍历查找,通过设备名进行比对,比对成功后返回rt_device
  • 假如此时应用程序调用read函数:
rt_device_read
	rt_device->read
		xxx_read

经过层层调用,最终执行驱动开发者实现的设备操作函数,其他应用api也是相同的逻辑,相关调用流程如下:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值