uvc_driver代码分析

一、uvc_probe部分

static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct uvc_device *dev;
    int ret;

    //完成产品相关信息的打印工作
    /*
    #define uvc_trace(flag, msg...) \
    do { \
        if (uvc_trace_param & flag) \
            printk(KERN_DEBUG "uvcvideo: " msg); \
      } while (0)
      uvc_trace_param这个全局变量是通过用户空间来传值实现的
    */
    if (id->idVendor && id->idProduct)
        uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
                "(%04x:%04x)\n", udev->devpath, id->idVendor,
                id->idProduct);
    else
        uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
                udev->devpath);

    /* Allocate memory for the device and initialize it. */
    if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
        return -ENOMEM;

    /*初始化链表*/
    INIT_LIST_HEAD(&dev->entities);//初始化entities(实体)链表 Terminal或Unit
    INIT_LIST_HEAD(&dev->chains);//初始化chains(链)链表
    INIT_LIST_HEAD(&dev->streams);//初始化streams(视频流)链表 

    /*设置原子变量的值,以便后续做原子操作*/
    atomic_set(&dev->nstreams, 0);
    atomic_set(&dev->users, 0);
    atomic_set(&dev->nmappings, 0);

    /*
    //以下两个函数在写法上使用container_of()只需要简单的操作就能返回原结构体
    usb_get_dev():通过struct usb_device --> struct device --> struct kobject
    增加引用计数
    usb_get_intf():通过struct usb_interface --> struct device --> struct kobject
    增加引用计数
    同时将struct usb_device及其struct usb_interface赋值给uvc结构体中的struct usb_device
    和struct usb_interface 结构体成员
    */
    dev->udev = usb_get_dev(udev);
    dev->intf = usb_get_intf(intf);

    /*通过接口找到接口描述符,这样得到接口数量*/
    dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;

    //quirks: 怪癖的意思,也就是说它某种特性与通常的USB设备不相同
    /*uvc_quirks_param 这个全局变量来自用户空间的输入*/
    dev->quirks = (uvc_quirks_param == -1)
            ? id->driver_info : uvc_quirks_param;

    /*
    此段代码的主要功能是将strcut usb_device 中的一些产品的
    信息(如产品号、版本号等)写入到struct uvc_device中,
    主要涉及到 struct uvc_device -> char name[32]
    */
    if (udev->product != NULL)
        strlcpy(dev->name, udev->product, sizeof dev->name);
    else
        snprintf(dev->name, sizeof dev->name,
            "UVC Camera (%04x:%04x)",
            le16_to_cpu(udev->descriptor.idVendor),
            le16_to_cpu(udev->descriptor.idProduct));

    /* Parse the Video Class control descriptor. */
    /*uvc解析usb视频类控制描述符*/
    /*整个函数都是围绕着struct usb_host_interface 中的extra成员展开的*/
    if (uvc_parse_control(dev) < 0) {
        uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
            "descriptors.\n");
        goto error;
    }

    uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
        dev->uvc_version >> 8, dev->uvc_version & 0xff,
        udev->product ? udev->product : "<unnamed>",
        le16_to_cpu(udev->descriptor.idVendor),
        le16_to_cpu(udev->descriptor.idProduct));

    if (dev->quirks != id->driver_info) {
        uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
            "parameter for testing purpose.\n", dev->quirks);
        uvc_printk(KERN_INFO, "Please report required quirks to the "
            "linux-uvc-devel mailing list.\n");
    }

    /* Register the media and V4L2 devices. */
    /*
    以下的操作均为对 struct uvc_device --> struct media_device 的
    成员进行赋值操作,赋值的成员如下:
    struct device *dev
    char model[32]
    char serial[40]
    char bus_info[32]
    u32 hw_revision
    u32 driver_version
    然后使用了media_device_register()函数进行media设备的注册工作
    */
#ifdef CONFIG_MEDIA_CONTROLLER
    dev->mdev.dev = &intf->dev;
    strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
    if (udev->serial)
        strlcpy(dev->mdev.serial, udev->serial,
            sizeof(dev->mdev.serial));
    strcpy(dev->mdev.bus_info, udev->devpath);
    dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
    dev->mdev.driver_version = DRIVER_VERSION_NUMBER;

    /*                      
    1.在下面的函数中发现了struct media_file_operations 其中有open、release、ioctl函数。
    但是上述的函数都是空函数,没有任何意义
    */
    if (media_device_register(&dev->mdev) < 0)
        goto error;
    /*
    struct uvc_device --> strcut v412_device vdev -->struct media_device
    */
    dev->vdev.mdev = &dev->mdev;
#endif
    if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
        goto error;

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

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

    /* Register video device nodes. */
    /*      下面这个函数存在着以下调用关系
        uvc_register_chains() --> uvc_register_terms() --> uvc_register_video()

        函数初始化关系图如下:见笔记章节

        其中uvc_register_video()函数非常重要!
        因为这个函数中存在着 v4l2操作函数集(uvc_fops) 和 真正的ioctl操作
        集(3.19以后的内核才存在vdev->ioctl_ops = &uvc_ioctl_ops;)

        vdev->fops = &uvc_fops;
        vdev->release = uvc_release;
    */
    if (uvc_register_chains(dev) < 0)
        goto error;

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

    /* Initialize the interrupt URB. */
    /*
      uvc_status_init()这个函数主要有三个作用:
      1.动态申请一个urb(interrupt urb)
      2.给struct uvc_device --> _u8 *status 申请一个内存空间(作为urb的缓冲区)
      3.初始化中断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);
    }

    uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
    //打开usb设备的自动挂起功能,以便实现低功耗的要求
    usb_enable_autosuspend(udev);
    return 0;

error:
    uvc_unregister_video(dev);
    return -ENODEV;
}

二·、uvc_ctrl_init_device

int uvc_ctrl_init_device(struct uvc_device *dev)
{
    struct uvc_entity *entity;
    unsigned int i;

    /* Walk the entities list and instantiate controls */
    list_for_each_entry(entity, &dev->entities, list) {
        struct uvc_control *ctrl;
        unsigned int bControlSize = 0;//控制位域大小
        unsigned int ncontrols = 0;//控制组件个数
        __u8 *bmControls = NULL;//控制位图

        /*判断每次遍历到的实体的类型,并对类型相应的参数进行赋值*/
        if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
            bmControls = entity->extension.bmControls;
            bControlSize = entity->extension.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
            bmControls = entity->processing.bmControls;
            bControlSize = entity->processing.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
            bmControls = entity->camera.bmControls;
            bControlSize = entity->camera.bControlSize;
        }

        /* Remove bogus/blacklisted controls 移除假的/黑名单控制组件*/
        uvc_ctrl_prune_entity(dev, entity);

        /* Count supported controls and allocate the controls array */
        for (i = 0; i < bControlSize; ++i)
            /*
            hweight8()函数的功能:能快速得出一个字段内有多少个bit是1

            bmControls 中的每一位表示一种控制组件,所以我们只需要通过使用
            hweight8()函数就可以得出我们控制组件的个数

            又因为 bmControls 的数据类型为 __u8 所以我们将数据分为三组存储,
            这就是这里需要用到for循环的原因(bmControls 这个数据实际存在24位)
            */
            ncontrols += hweight8(bmControls[i]);
        if (ncontrols == 0)
            continue;
        /*给每一个实体中的所有要使用到的控制组件都申请一段内存*/
        entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
                       GFP_KERNEL);
        if (entity->controls == NULL)
            return -ENOMEM;
        entity->ncontrols = ncontrols;

        /* Initialize all supported controls */
        ctrl = entity->controls;
        for (i = 0; i < bControlSize * 8; ++i) {
            if (uvc_test_bit(bmControls, i) == 0)
                continue;

            ctrl->entity = entity;
            ctrl->index = i;

            uvc_ctrl_init_ctrl(dev, ctrl);//初始化uvc控制组件
            ctrl++;
        }
    }

    return 0;
}

三、uvc_scan_device

 /*
    struct uvc_entity 这个结构体描述的主要为uvc协议中的:
    IT、OT、PU、SU、EU、XU 这些实体
*/
static int uvc_scan_device(struct uvc_device *dev)
{
    struct uvc_video_chain *chain;//uvc视频链
    struct uvc_entity *term;//uvc实体

    /*
    遍历整个实体链表
    循环遍历每一个term中的list子项, &dev->entities为链表头
    */
    list_for_each_entry(term, &dev->entities, list) {

    /*
    获取实体链表中的输出Terminal实体 
    通过struct uvc_entity --> __u16 type 来判断Terminal的类型

    #define UVC_ENTITY_IS_TERM(entity)  (((entity)->type & 0xff00) != 0)    
    #define UVC_ENTITY_IS_OTERM(entity) \
        (UVC_ENTITY_IS_TERM(entity) && \
        ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)

    这里主要完成了两个判断:
    (其中的0x8000为判断Terminal类型的掩码,0xff00位判断实体类型的掩码)
    1.判断这个实体是否为Terminal 
    2.判断这个实体是否为output Terminal
    */
        if (!UVC_ENTITY_IS_OTERM(term))
            continue;

        /* If the terminal is already included in a chain, skip it.
         * This can happen for chains that have multiple output
         * terminals, where all output terminals beside the first one
         * will be inserted in the chain in forward scans.
         */
        if (term->chain.next || term->chain.prev)
            continue;

        chain = kzalloc(sizeof(*chain), GFP_KERNEL);
        if (chain == NULL)
            return -ENOMEM;
        /*初始化uvc视频链entities(实体)链表*/
        INIT_LIST_HEAD(&chain->entities);
        mutex_init(&chain->ctrl_mutex);
        /*捆绑uvc视频链和uvc设备*/
        chain->dev = dev;

        /*
        (自己的观点)
        uvc_scan_chain()这个函数主要作用扫描到的每个实体经过
        类型判断及其相关信息打印后,将实体添加到uvc视频链
        */
        if (uvc_scan_chain(chain, term) < 0) {
            kfree(chain);
            continue;
        }

        uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
              uvc_print_chain(chain));

        list_add_tail(&chain->list, &dev->chains);
    }

    if (list_empty(&dev->chains)) {
        uvc_printk(KERN_INFO, "No valid video chain found.\n");
        return -1;
    }

    return 0;
}

四、uvc_register_chains、uvc_register_terms

static int uvc_register_chains(struct uvc_device *dev)
{
    struct uvc_video_chain *chain;
    int ret;

    list_for_each_entry(chain, &dev->chains, list) {
        ret = uvc_register_terms(dev, chain);
        if (ret < 0)
            return ret;

#ifdef CONFIG_MEDIA_CONTROLLER
        ret = uvc_mc_register_entities(chain);
        if (ret < 0) {
            uvc_printk(KERN_INFO, "Failed to register entites "
                "(%d).\n", ret);
        }
#endif
    }

    return 0;
}
static int uvc_register_terms(struct uvc_device *dev,
    struct uvc_video_chain *chain)
{
    struct uvc_streaming *stream;
    struct uvc_entity *term;
    int ret;

    list_for_each_entry(term, &chain->entities, chain) {
        /*如果判断此实体的类型不是streaming的话则重新遍历*/
        if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
            continue;
        /*找到每个实体相对应的视频流(也就是说找到与之对应的 struct uvc_streaming 这个结构体)*/
        stream = uvc_stream_by_id(dev, term->id);
        if (stream == NULL) {
            uvc_printk(KERN_INFO, "No streaming interface found "
                   "for terminal %u.", term->id);
            continue;
        }
        /*将uvc视频链和uvc流中的uvc视频链进行绑定*/
        stream->chain = chain;
        /*对其中的uvc视频流做初始化工作,并初始化视频设备且注册*/
        ret = uvc_register_video(dev, stream);
        if (ret < 0)
            return ret;
        /*将uvc实体中的struct video_device和uvc流中的struct video_device进行绑定*/
        term->vdev = stream->vdev;
    }

    return 0;
}

五、uvc_ctrl_init_ctrl

static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
{
    const struct uvc_control_info *info = uvc_ctrls;
    const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
    const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
    const struct uvc_control_mapping *mend =
        mapping + ARRAY_SIZE(uvc_ctrl_mappings);

    /* XU controls initialization requires querying the device for control
     * information. As some buggy UVC devices will crash when queried
     * repeatedly in a tight loop, delay XU controls initialization until
     * first use.
     */
    if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
        return;

    /*
    uvc_entity_match_guid()函数的主要作用:
    1.首先通过传入的uvc_control中的实体成员判断其实体的类型,主要是筛选以下
      四种类型(UVC_ITT_CAMERA、UCV_ITT_MEDIA_TRANSPORT_INPUT、UVC_VC_PROCESSING_UNIT、
      UVC_VC_EXTENSION_UNIT)
    2.然后将控制信息(uvc_control_info)中的实体标识与默认的类型标识做对比,如果一致
      则不需要添加新的控制信息,否则需要使用uvc_ctrl_add_info()去添加
    */
    for (; info < iend; ++info) {
        if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
            ctrl->index == info->index) {
            uvc_ctrl_add_info(dev, ctrl, info);
            break;
         }
    }

    if (!ctrl->initialized)
        return;

    for (; mapping < mend; ++mapping) {
        if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
            ctrl->info.selector == mapping->selector)
            __uvc_ctrl_add_mapping(dev, ctrl, mapping);
    }
}

六、uvc_video_init

int uvc_video_init(struct uvc_streaming *stream)
{
    struct uvc_streaming_control *probe = &stream->ctrl;
    struct uvc_format *format = NULL;
    struct uvc_frame *frame = NULL;
    unsigned int i;
    int ret;

    /*首先检查UVC视频流中是否有格式信息,如果没有则返回错误*/
    if (stream->nformats == 0) {
        uvc_printk(KERN_INFO, "No supported video formats found.\n");
        return -EINVAL;
    }

    atomic_set(&stream->active, 0);

    /* Initialize the video buffers queue. */
    /*初始化队列,并且通过队列来管理视频缓冲区*/
    uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);

    /* Alternate setting 0 should be the default, yet the XBox Live Vision
     * Cam (and possibly other devices) crash or otherwise misbehave if
     * they don't receive a SET_INTERFACE request before any other video
     * control request.
     */
     /*这个函数功能在自己看来作用为:改变指定usb设备的接口设置*/
    usb_set_interface(stream->dev->udev, stream->intfnum, 0);

    /* Set the streaming probe control with default streaming parameters
     * retrieved from the device. Webcams that don't suport GET_DEF
     * requests on the probe control will just keep their current streaming
     * parameters.
     */
     /*
        uvc_get_video_ctrl()获得uvc流的默认参数设置,如果参数一切正常将返回0,
        否则返回负值
        uvc_set_video_ctrl()设置uvc视频控制  
    */
    if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
        uvc_set_video_ctrl(stream, probe, 1);

    /* Initialize the streaming parameters with the probe control current
     * value. This makes sure SET_CUR requests on the streaming commit
     * control will always use values retrieved from a successful GET_CUR
     * request on the probe control, as required by the UVC specification.
     */
     /*获取当前的控制值,确认上面的设置是否正确*/
    ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
    if (ret < 0)
        return ret;

    /* Check if the default format descriptor exists. Use the first
     * available format otherwise.
     */
    for (i = stream->nformats; i > 0; --i) {
        format = &stream->format[i-1];
        if (format->index == probe->bFormatIndex)
            break;
    }

    /*检查frame的选择数量,如果为0则返回错误值*/
    if (format->nframes == 0) {
        uvc_printk(KERN_INFO, "No frame descriptor found for the "
            "default format.\n");
        return -EINVAL;
    }

    /* Zero bFrameIndex might be correct. Stream-based formats (including
     * MPEG-2 TS and DV) do not support frames but have a dummy frame
     * descriptor with bFrameIndex set to zero. If the default frame
     * descriptor is not found, use the first available frame.
     */
    for (i = format->nframes; i > 0; --i) {
        frame = &format->frame[i-1];
        if (frame->bFrameIndex == probe->bFrameIndex)
            break;
    }

    //设置uvc视频流控制的格式索引为uvc格式的索引
    probe->bFormatIndex = format->index;
    //设置uvc视频流控制的帧索引为uvc帧索引
    probe->bFrameIndex = frame->bFrameIndex;

    /*设置UVC视频流的当前的UVC视频格式及其UVC视频帧*/
    stream->cur_format = format;
    stream->cur_frame = frame;
            .
            .
         省     略
            .
            .
    return 0;
}

这就是小弟对uvc_driver驱动代码的分析,希望各位大佬指点一下!!

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值