Linux&UVC驱动
+ -

LINUX&UVC驱动的uvc_probe函数主体分析

2024-03-13 147 0

UVC驱动的prob函数原型如下:

static int uvc_probe(struct usb_interface *intf,
             const struct usb_device_id *id);

这是由USBCore回调并提供相关的USB逻辑设备相关的参数,这我们在上一节已经分析过了。
对于UVC设备,和其它类驱动一样,也会创建自己的设备对象来代表自己是驱动中的一层,所以这里UVC驱动定义的设备结构体就是uvc_device。
其结构体如下:

struct uvc_device {
    struct usb_device *udev;
    struct usb_interface *intf;
    unsigned long warnings;
    u32 quirks;
    int intfnum;
    char name[32];

    const struct uvc_device_info *info;

    struct mutex lock;        /* Protects users */
    unsigned int users;
    atomic_t nmappings;

    /* Video control interface */
#ifdef CONFIG_MEDIA_CONTROLLER
    struct media_device mdev;
#endif
    struct v4l2_device vdev;
    u16 uvc_version;
    u32 clock_frequency;

    struct list_head entities;
    struct list_head chains;

    /* Video Streaming interfaces */
    struct list_head streams;
    struct kref ref;

    /* Status Interrupt Endpoint */
    struct usb_host_endpoint *int_ep;
    struct urb *int_urb;
    u8 *status;
    struct input_dev *input;
    char input_phys[64];

    struct uvc_ctrl_work {
        struct work_struct work;
        struct urb *urb;
        struct uvc_video_chain *chain;
        struct uvc_control *ctrl;
        const void *data;
    } async_ctrl;
};

可以看到,这里不仅包括了USB设备对象本身相关的信息,也有UVC类设备特有的东西,如视频控制接口视频流接口等。
1.在uvc_probe中,进行必要的信息验证后,就应该和Windows驱动调用IoCreateDevice创建本层的设备对象一样,UVC驱动申请本层设备驱动的设备对象结构体。

    struct uvc_device *dev;
    ...
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);

2.对申请的结构体对象进行初始化,并保存该逻辑USB设备对象的相关信息。

    INIT_LIST_HEAD(&dev->entities);
    INIT_LIST_HEAD(&dev->chains);
    INIT_LIST_HEAD(&dev->streams);
    kref_init(&dev->ref);
    atomic_set(&dev->nmappings, 0);
    mutex_init(&dev->lock);

    dev->udev = usb_get_dev(udev);
    dev->intf = usb_get_intf(intf);
    dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
    ...

3.UVC规范定义的UVC设备,至少有一个视频控制接口和0个或者多个视频流接口(实际一般只有一个视频接口)

    /* Parse the Video Class control descriptor. */
    if (uvc_parse_control(dev) < 0) {
        uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
            "descriptors.\n");
        goto error;
    }

使用uvc_parse_control解析该UVC设备的配置描述符,解析出需要的信息。

4.与V4L2扯上关系,使用v4l2_device_register函数。
第一个参数intf->dev是LINUX系统层的设备struct device *dev
第二个是V4L2设备,由v4l2_device_register初始化,但其位于struct uvc_device结构体中。

    /* Register the V4L2 device. */
    if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
        goto error;

4.初始化UVC控制接口中的各种Unit或者Terminal,比如UVC扩展单元,UVC相机终端单元,UVC处理单元等,这些支持的UVC特定类请求

    /* Initialize controls. */
    if (uvc_ctrl_init_device(dev) < 0)
        goto error;

5.这一块没看,估计与视频流接口中UVC设备的格式,分辨率等相关。

    /* Scan the device for video chains. */
    if (uvc_scan_device(dev) < 0)
        goto error;

    /* Register video device nodes. */
    if (uvc_register_chains(dev) < 0)
        goto error;

6.将UVC对象保留在USBCore对象给的私有数据成员中。

    /* Save our data pointer in the interface data. */
    usb_set_intfdata(intf, dev);

详见:存储驱动私有数据 https://www.usbzh.com/article/detail-1318.html

7.初始化中断状态。这应与UVC控制接口某些设备中有一些中断端点,用于向主机上报其错误状态。当然如这个中断端点是可选的。

    /* Initialize the interrupt URB. */
    if ((ret = uvc_status_init(dev)) < 0) {
        uvc_printk(KERN_INFO, "Unable to initialize the status "
            "endpoint (%d), status interrupt will not be "
            "supported.\n", ret);
    }

8.设备就绪-自动挂起

    usb_enable_autosuspend(udev);

奇怪的问题

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
...
    if (!dev_get_drvdata(dev))
        dev_set_drvdata(dev, v4l2_dev);
    return 0;
}

static inline void usb_set_intfdata(struct usb_interface *intf, void *data)
{
    dev_set_drvdata(&intf->dev, data);
}

UVC驱动中的prob函数中,会调用如下2行代码

v4l2_device_register(&intf->dev, &dev->vdev) < 0);
usb_set_intfdata(intf, dev);

但从结果上来看,最终都是设置的是intf->dev的私有数据,
那么前面调用的v4l2_device_register是不是就没有意义了,因为会被usb_set_intfdata又被覆盖了.
这是为啥?

其实在disconnect函数中可以解释,确实是覆盖了。

struct uvc_driver uvc_driver = {
    .driver = {
...
        .disconnect    = uvc_disconnect,
...
    },
};

函数实现如下:

static void uvc_disconnect(struct usb_interface *intf)
{
    struct uvc_device *dev = usb_get_intfdata(intf);

    /* Set the USB interface data to NULL. This can be done outside the
     * lock, as there's no other reader.
     */
    usb_set_intfdata(intf, NULL);

    if (intf->cur_altsetting->desc.bInterfaceSubClass ==
        UVC_SC_VIDEOSTREAMING)
        return;

    uvc_unregister_video(dev);
    kref_put(&dev->ref, uvc_delete);
}

其通过usb_get_intfdata获取的接口设备的私有数据为uvc_device,其正好对应的是

usb_set_intfdata(intf, dev);

最后就是反注册uvc设备,使用uvc_unregister_video,最后删除申请的内存数据。

0 篇笔记 写笔记

关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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