LINUX&UVC驱动的初始化
UVC驱动入口代码位于uvc_driver.c文件中。
和普通有USB驱动类似,其代码如下:
static int __init uvc_init(void)
{
int ret;
uvc_debugfs_init();
ret = usb_register(&uvc_driver.driver);
if (ret < 0) {
uvc_debugfs_cleanup();
return ret;
}
printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
return 0;
}
static void __exit uvc_cleanup(void)
{
usb_deregister(&uvc_driver.driver);
uvc_debugfs_cleanup();
}
module_init(uvc_init);
module_exit(uvc_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
- 其入口函数module_init指定为uvc_init,这就相当于应用于的main函数和Windows驱动的DriverEntry.
- 驱动卸载时时的清理函数由module_exit指定为uvc_cleanup,类似Windows驱动的 DriverObject->DriverUnload回调函数。
其实作为一名习惯了Windows程序开发编码风格和程序设计的人码农来说,对于linux的这一套设计方式会有很多的不适应,而且有时会陷入两种风格编程孰优的争吵中而无法自拨~就如同种种语言那种好一样像前十几年很火的话题:PHP是世界上最好的语言一样,很容易争吵不休。。。。
而在linux/v5.5-rc2/source/include/linux/module.h源文件中,这两个宏那仅是两个宏,与编译器相关
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
再往下追,就到了linux/v5.5-rc2/source/include/linux/init.h#L234,其定义如下:
#define __initcall(fn) device_initcall(fn)
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
#define device_initcall(fn) __define_initcall(fn, 6)
最终为:
#define ___define_initcall(fn, id, __sec) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(#__sec ".init"))) = fn;
个人感觉,最终搞这么复杂,不如像Windows一样,干脆指定一个函数名不如就算了。也许Linux就是为的是开源,让大家看到最底层的东西,这么复杂也是因其本身复杂,Windows因其团源性,简单意味着只能遵守。
这些,可以看到都是与可执行文件的节有关,就如windows驱动的代码这样一样:
#define INITCODE code_seg("INIT")
类似的作法,只是细节不同而已。
有兴趣想更加深入地了解的,可以参见:https://blog.csdn.net/weixin_42697609/article/details/116663016
下面最回到驱动代码本身,驱动本身仅使用函数usb_register注册了一个驱动类到系统中,其注册的消息内容如下:
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
该驱动匹配的硬件ID相关信息由uvc_ids指定。
uvc驱动不仅适配uvc_ids指定的VID/PID设备,也对一些不包含在此内的设备,因其兼容ID也可使用。所以这就有一个匹配规则的问题,由match_flags指定。
usb_register是由内核提供的一个宏,其真正的函数是usb_register_driver:
/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
其调用大概流程为:
- usb_register_driver
- driver_register
- bus_add_driver
- klist_add_tail
- module_add_driver
最终应是加入系统驱动链表。当有新设备时,会根据注册的一些特性及设备硬件ID特性,从驱动链表中找到调用该驱动的probe函数。这就类于Windows系统的AddDevice回调函数一亲。
- bus_add_driver
- driver_register
而驱动的卸载函数有相对简单一些:
void usb_deregister_device_driver(struct usb_device_driver *udriver)
{
void driver_unregister(struct device_driver *drv)
{
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
}
是driver_register的反向操作。