背景
最近查看了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所有的外设,几乎都遵循此逻辑,唯一的区别为部分外设接口基于该接口以及模块特性做了一层封装,暴露给应用的接口按照实际硬件特性的接口,而不是直接调用统一的设备框架,此部分后续适配具体驱动模块时再进一步分析。
另外,梳理完这部分内容后,我们其实已经掌握了一种快速找到应用对应的驱动实现的入口,即查找注册设备名的驱动,再通过驱动对应的操作函数找到对应的实现。
我要赚赏金
