这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » rtthread设备驱动框架理解

共6条 1/1 1 跳转至

rtthread设备驱动框架理解

助工
2024-08-21 22:13:59   被打赏 50 分(兑奖)     打赏

背景

   最近查看了rtt主线仓库,发现自己一年前合并到主干的野火启明6M5板卡,除了我所提交的部分,仅仅是被提交了小部分的几个驱动。因此打算利用空闲时间,将该板卡的外设驱动逐个适配完毕。而适配前的动作,需要了解rtt外设驱动管理框架。

设备框架

   首先需要明确,在rtt的代码逻辑里面,所有设备实现入口都在device.c,而所有设备都被看作对象,其最底层的管理逻辑见object.c。

设备注册

// 这部分代码已经很清晰,先是拿到系统设备类的句柄
// 其次是将要注册的设备标注为静态设备类设备
// 最后在临界区中将要注册的设备加入到系统设备类句柄中
void rt_object_init(struct rt_object         *object,
                    enum rt_object_class_type type,
                    const char               *name)
{
    rt_base_t level;
#ifdef RT_DEBUGING_ASSERT
    struct rt_list_node *node = RT_NULL;
#endif /* RT_DEBUGING_ASSERT */
    struct rt_object_information *information;
#ifdef RT_USING_MODULE
    struct rt_dlmodule *module = dlmodule_self();
#endif /* RT_USING_MODULE */

    /* get object information */
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);

#ifdef RT_DEBUGING_ASSERT
    /* check object type to avoid re-initialization */

    /* enter critical */
    level = rt_spin_lock_irqsave(&(information->spinlock));
    /* try to find object */
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        struct rt_object *obj;

        obj = rt_list_entry(node, struct rt_object, list);
        RT_ASSERT(obj != object);
    }
    /* leave critical */
    rt_spin_unlock_irqrestore(&(information->spinlock), level);
#endif /* RT_DEBUGING_ASSERT */

    /* initialize object's parameters */
    /* set object type to static */
    object->type = type | RT_Object_Class_Static;
#if RT_NAME_MAX > 0
    rt_strncpy(object->name, name, RT_NAME_MAX);  /* copy name */
#else
    object->name = name;
#endif /* RT_NAME_MAX > 0 */

    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

    level = rt_spin_lock_irqsave(&(information->spinlock));

#ifdef RT_USING_MODULE
    if (module)
    {
        rt_list_insert_after(&(module->object_list), &(object->list));
        object->module_id = (void *)module;
    }
    else
#endif /* RT_USING_MODULE */
    {
        /* insert object into information object list */
        rt_list_insert_after(&(information->object_list), &(object->list));
    }
    rt_spin_unlock_irqrestore(&(information->spinlock), level);
}

rt_err_t rt_device_register(rt_device_t dev,
                            const char *name,
                            rt_uint16_t flags)
{
    if (dev == RT_NULL)
        return -RT_ERROR;

    // 如果要注册的设备名已存在,则不执行设备注册,直接返回无法注册
    if (rt_device_find(name) != RT_NULL)
        return -RT_ERROR;

    // 可以理解为把设备类对象加到现有设备列表中
    rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
    dev->flag = flags; // 设置当前设备所支持的功能标记
    dev->ref_count = 0; // 初始化当前该设备正在被打开的次数
    dev->open_flag = 0; // 设备开启状态

#ifdef RT_USING_POSIX_DEVIO
    dev->fops = RT_NULL;
    rt_wqueue_init(&(dev->wait_queue));
#endif /* RT_USING_POSIX_DEVIO */

#ifdef RT_USING_DFS_V2
    dfs_devfs_device_add(dev);
#endif /* RT_USING_DFS_V2 */

    return RT_EOK;
}

设备注销
// 这部分代码其实也挺清晰的
// 首先获取系统设备类的句柄
// 在临界区中移除当前设备
// 当前设备的设备标记清空
void rt_object_detach(rt_object_t object)
{
    rt_base_t level;
    struct rt_object_information *information;

    /* object check */
    RT_ASSERT(object != RT_NULL);

    RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));

    information = rt_object_get_information((enum rt_object_class_type)object->type);
    RT_ASSERT(information != RT_NULL);

    level = rt_spin_lock_irqsave(&(information->spinlock));
    /* remove from old list */
    rt_list_remove(&(object->list));
    rt_spin_unlock_irqrestore(&(information->spinlock), level);

    object->type = 0;
}

rt_err_t rt_device_unregister(rt_device_t dev)
{
    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
    RT_ASSERT(rt_object_is_systemobject(&dev->parent));

    // 从系统当前设备列表中移除当前设备
    rt_object_detach(&(dev->parent));

    return RT_EOK;
}
设备查询
struct _obj_find_param
{
    const char *match_name;
    rt_object_t matched_obj;
};

static rt_err_t _match_name(struct rt_object *obj, void *data)
{
    struct _obj_find_param *param = data;
    const char *name = param->match_name;
    if (rt_strncmp(obj->name, name, RT_NAME_MAX) == 0)
    {
        param->matched_obj = obj;

        /* notify an early break of loop, but not on error */
        return 1;
    }

    return RT_EOK;
}

// 此函数的关键在 iter(object, data),即_match_name(object, ¶m),
// 而查看_match_name实现便可发现,该函数本质上就是按设备名比较,发现一致则把句柄提供给上层
rt_err_t rt_object_for_each(rt_uint8_t type, rt_object_iter_t iter, void *data)
{
    struct rt_object *object = RT_NULL;
    struct rt_list_node *node = RT_NULL;
    struct rt_object_information *information = RT_NULL;
    rt_base_t level;
    rt_err_t error;

    information = rt_object_get_information((enum rt_object_class_type)type);

    /* parameter check */
    if (information == RT_NULL)
    {
        return -RT_EINVAL;
    }

    /* which is invoke in interrupt status */
    RT_DEBUG_NOT_IN_INTERRUPT;

    /* enter critical */
    level = rt_spin_lock_irqsave(&(information->spinlock));

    /* try to find object */
    rt_list_for_each(node, &(information->object_list))
    {
        object = rt_list_entry(node, struct rt_object, list);
        if ((error = iter(object, data)) != RT_EOK)
        {
            rt_spin_unlock_irqrestore(&(information->spinlock), level);

            return error >= 0 ? RT_EOK : error;
        }
    }

    rt_spin_unlock_irqrestore(&(information->spinlock), level);

    return RT_EOK;
}

// 从系统设备列表中找到需要找到的设备(按设备名查找)
// 若能找到,则返回设备对象句柄,若找不到,则返回空
rt_object_t rt_object_find(const char *name, rt_uint8_t type)
{
    struct _obj_find_param param =
    {
        .match_name = name,
        .matched_obj = RT_NULL,
    };

    /* parameter check */
    if (name == RT_NULL) return RT_NULL;

    /* which is invoke in interrupt status */
    RT_DEBUG_NOT_IN_INTERRUPT;

    // 由于param.matched_obj的值能直接表征成功与否,因此不需要判定该函数的返回状态
    rt_object_for_each(type, _match_name, ¶m);
    return param.matched_obj;
}

rt_device_t rt_device_find(const char *name)
{
    return (rt_device_t)rt_object_find(name, RT_Object_Class_Device);
}
设备打开
#ifdef RT_USING_DEVICE_OPS
#define device_init     (dev->ops ? dev->ops->init : RT_NULL)
#define device_open     (dev->ops ? dev->ops->open : RT_NULL)
#define device_close    (dev->ops ? dev->ops->close : RT_NULL)
#define device_read     (dev->ops ? dev->ops->read : RT_NULL)
#define device_write    (dev->ops ? dev->ops->write : RT_NULL)
#define device_control  (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init     (dev->init)
#define device_open     (dev->open)
#define device_close    (dev->close)
#define device_read     (dev->read)
#define device_write    (dev->write)
#define device_control  (dev->control)
#endif /* RT_USING_DEVICE_OPS */

// 由此代码可以看到,只要驱动有注册init函数,
// 此函数便会在调用rt_device_init时运行,前提是该设备当前并未被打开
rt_err_t rt_device_init(rt_device_t dev)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev != RT_NULL);

    /* get device_init handler */
    if (device_init != RT_NULL)
    {
        if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
        {
            result = device_init(dev);
            if (result != RT_EOK)
            {
                LOG_E("To initialize device:%s failed. The error code is %d",
                      dev->parent.name, result);
            }
            else
            {
                dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
            }
        }
    }

    return result;
}

// 如果该设备未被打开过,则先调用初始化函数再打开
// 如果没有被打开,则打开当前设备并记录打开次数和打开标记
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{
    rt_err_t result = RT_EOK;

    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    /* if device is not initialized, initialize it. */
    if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
    {
        if (device_init != RT_NULL)
        {
            result = device_init(dev);
            if (result != RT_EOK)
            {
                LOG_E("To initialize device:%s failed. The error code is %d",
                      dev->parent.name, result);

                return result;
            }
        }

        dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
    }

    /* device is a stand alone device and opened */
    if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&
        (dev->open_flag & RT_DEVICE_OFLAG_OPEN))
    {
        return -RT_EBUSY;
    }

    /* device is not opened or opened by other oflag, call device_open interface */
    if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN) ||
         ((dev->open_flag & RT_DEVICE_OFLAG_MASK) != (oflag & RT_DEVICE_OFLAG_MASK)))
    {
        if (device_open != RT_NULL)
        {
            result = device_open(dev, oflag);
        }
        else
        {
            /* set open flag */
            dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK);
        }
    }

    /* set open flag */
    if (result == RT_EOK || result == -RT_ENOSYS)
    {
        dev->open_flag |= RT_DEVICE_OFLAG_OPEN;

        dev->ref_count++;
        /* don't let bad things happen silently. If you are bitten by this assert,
        * please set the ref_count to a bigger type. */
        RT_ASSERT(dev->ref_count != 0);
    }

    return result;
}
设备关闭
#ifdef RT_USING_DEVICE_OPS
#define device_init     (dev->ops ? dev->ops->init : RT_NULL)
#define device_open     (dev->ops ? dev->ops->open : RT_NULL)
#define device_close    (dev->ops ? dev->ops->close : RT_NULL)
#define device_read     (dev->ops ? dev->ops->read : RT_NULL)
#define device_write    (dev->ops ? dev->ops->write : RT_NULL)
#define device_control  (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init     (dev->init)
#define device_open     (dev->open)
#define device_close    (dev->close)
#define device_read     (dev->read)
#define device_write    (dev->write)
#define device_control  (dev->control)
#endif /* RT_USING_DEVICE_OPS */

rt_err_t rt_device_close(rt_device_t dev)
{
    rt_err_t result = RT_EOK;

    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    // 检查当前设备是否被打开
    if (dev->ref_count == 0)
        return -RT_ERROR;

    // 打开次数减1
    dev->ref_count--;

    // 如果当前仍有应用使用设备,则直接返回
    if (dev->ref_count != 0)
        return RT_EOK;

    // 若所有应用都关闭了该设备,则调用关闭设备函数
    /* call device_close interface */
    if (device_close != RT_NULL)
    {
        result = device_close(dev);
    }

    // 打开标志位置为关闭状态
    /* set open flag */
    if (result == RT_EOK || result == -RT_ENOSYS)
        dev->open_flag = RT_DEVICE_OFLAG_CLOSE;

    return result;
}
设备操作
#ifdef RT_USING_DEVICE_OPS
#define device_init     (dev->ops ? dev->ops->init : RT_NULL)
#define device_open     (dev->ops ? dev->ops->open : RT_NULL)
#define device_close    (dev->ops ? dev->ops->close : RT_NULL)
#define device_read     (dev->ops ? dev->ops->read : RT_NULL)
#define device_write    (dev->ops ? dev->ops->write : RT_NULL)
#define device_control  (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init     (dev->init)
#define device_open     (dev->open)
#define device_close    (dev->close)
#define device_read     (dev->read)
#define device_write    (dev->write)
#define device_control  (dev->control)
#endif /* RT_USING_DEVICE_OPS */

// 此函数仅仅是实现了设备控制,从代码上看,其实先并不考虑设备是否已被打开
// 也就是说,应用可以先预配置设备信息,之后再打开设备,
// 以防止设备以一种状态打开后,短时间内切换至最终状态
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
{
    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    /* call device_write interface */
    if (device_control != RT_NULL)
    {
        return device_control(dev, cmd, arg);
    }

    return -RT_ENOSYS;
}
设备读写
#ifdef RT_USING_DEVICE_OPS
#define device_init     (dev->ops ? dev->ops->init : RT_NULL)
#define device_open     (dev->ops ? dev->ops->open : RT_NULL)
#define device_close    (dev->ops ? dev->ops->close : RT_NULL)
#define device_read     (dev->ops ? dev->ops->read : RT_NULL)
#define device_write    (dev->ops ? dev->ops->write : RT_NULL)
#define device_control  (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init     (dev->init)
#define device_open     (dev->open)
#define device_close    (dev->close)
#define device_read     (dev->read)
#define device_write    (dev->write)
#define device_control  (dev->control)
#endif /* RT_USING_DEVICE_OPS */

// 看两个设备,会发现其实逻辑都一样
// 都是先判定当前设备是否被打开
// 如果被打开,则根据是否注册了读写函数来决定是否读写
rt_ssize_t rt_device_read(rt_device_t dev,
                         rt_off_t    pos,
                         void       *buffer,
                         rt_size_t   size)
{
    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    if (dev->ref_count == 0)
    {
        rt_set_errno(-RT_ERROR);
        return 0;
    }

    /* call device_read interface */
    if (device_read != RT_NULL)
    {
        return device_read(dev, pos, buffer, size);
    }

    /* set error code */
    rt_set_errno(-RT_ENOSYS);

    return 0;
}

rt_ssize_t rt_device_write(rt_device_t dev,
                          rt_off_t    pos,
                          const void *buffer,
                          rt_size_t   size)
{
    /* parameter check */
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    if (dev->ref_count == 0)
    {
        rt_set_errno(-RT_ERROR);
        return 0;
    }

    /* call device_write interface */
    if (device_write != RT_NULL)
    {
        return device_write(dev, pos, buffer, size);
    }

    /* set error code */
    rt_set_errno(-RT_ENOSYS);

    return 0;
}

总结

   从以上代码的梳理,其实我们可以发现,其实rtt的设备类驱动的添加,存在一个套路。

       而实际上,rtt所有的外设,几乎都遵循此逻辑,唯一的区别为部分外设接口基于该接口以及模块特性做了一层封装,暴露给应用的接口按照实际硬件特性的接口,而不是直接调用统一的设备框架,此部分后续适配具体驱动模块时再进一步分析。

       另外,梳理完这部分内容后,我们其实已经掌握了一种快速找到应用对应的驱动实现的入口,即查找注册设备名的驱动,再通过驱动对应的操作函数找到对应的实现。






关键词: rtthread     设备驱动     框架     理解    

工程师
2024-08-22 00:25:19     打赏
2楼

感谢分享


专家
2024-08-22 00:44:51     打赏
3楼

感谢分享


高工
2024-08-22 00:46:29     打赏
4楼

感谢分享


专家
2024-08-22 09:13:38     打赏
5楼

写的好,学习和参考


院士
2024-08-30 10:37:20     打赏
6楼

框架编程的最大优势就是可以引入多人开发,团队开发。但这又考验了软件架构师的能力了


共6条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]