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