Linux&UVC驱动
+ -

LINUX&UVC驱动的初始化

2024-03-12 23 0

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回调函数一亲。

而驱动的卸载函数有相对简单一些:

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的反向操作。

0 篇笔记 写笔记

LINUX&UVC驱动的初始化
UVC驱动入口代码位于uvc_driver.c文件中。和普通有USB驱动类似,其代码如下:static int __init uvc_init(void){ int ret; uvc_debugfs_init(); ret = usb_register(&uv......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!