UVC摄像头(2)驱动层——UVC描述符参数设置

接着上一篇UVC摄像头(1)

composite_gadget_bind(cdev);

我们绑定它的函数是这个

webcam_bind(struct usb_composite_dev *cdev)
{
    int ret;

    /* Allocate string descriptor numbers ... note that string contents
     * can be overridden by the composite_dev glue.
     */
    if ((ret = usb_string_id(cdev)) < 0)
        goto error;
    webcam_strings[STRING_MANUFACTURER_IDX].id = ret;
    webcam_device_descriptor.iManufacturer = ret;
    webcam_device_descriptor.idVendor = cpu_to_le16(g_vendor_id);
    webcam_device_descriptor.idProduct = cpu_to_le16(g_product_id);
    webcam_device_descriptor.bcdDevice = cpu_to_le16(g_device_bcd);

    if ((ret = usb_string_id(cdev)) < 0)
        goto error;
    webcam_strings[STRING_PRODUCT_IDX].id = ret;
    webcam_device_descriptor.iProduct = ret;

    if ((ret = usb_string_id(cdev)) < 0)
        goto error;
    webcam_strings[STRING_DESCRIPTION_IDX].id = ret;
    webcam_config_driver.iConfiguration = ret;

    /* Register our configuration. */
    if ((ret = usb_add_config(cdev, &webcam_config_driver,
                    webcam_config_bind)) < 0)
        goto error;

    INFO(cdev, "Webcam Video Gadget, VID:%04x, PID:%04x, BCD:%04x\n", g_vendor_id, g_product_id, g_device_bcd);
    return 0;

error:
    webcam_unbind(cdev);
    return ret;
}

填充了webcam_strings、webcam_device_descriptor、webcam_config_driver结构体;之后是重点:usb_add_config

int usb_add_config(struct usb_composite_dev *cdev,
        struct usb_configuration *config,
        int (*bind)(struct usb_configuration *))
{
    int             status = -EINVAL;
    struct usb_configuration    *c;

    DBG(cdev, "adding config #%u '%s'/%p\n",
            config->bConfigurationValue,
            config->label, config);

    if (!config->bConfigurationValue || !bind)
        goto done;

    /* Prevent duplicate configuration identifiers */
    list_for_each_entry(c, &cdev->configs, list) {
        if (c->bConfigurationValue == config->bConfigurationValue) {
            status = -EBUSY;
            goto done;
        }
    }

    config->cdev = cdev;
    list_add_tail(&config->list, &cdev->configs);

    INIT_LIST_HEAD(&config->functions);
    config->next_interface_id = 0;
    memset(config->interface, 0, sizeof(config->interface));

    status = bind(config);
    if (status < 0) {
        list_del(&config->list);
        config->cdev = NULL;
    } else {
        unsigned    i;

        DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
            config->bConfigurationValue, config,
            config->superspeed ? " super" : "",
            config->highspeed ? " high" : "",
            config->fullspeed
                ? (gadget_is_dualspeed(cdev->gadget)
                    ? " full"
                    : " full/low")
                : "");

        for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
            struct usb_function *f = config->interface[i];

            if (!f)
                continue;
            DBG(cdev, "  interface %d = %s/%p\n",
                i, f->name, f);
        }
    }

    /* set_alt(), or next bind(), sets up
     * ep->driver_data as needed.
     */
    usb_ep_autoconfig_reset(cdev->gadget);

done:
    if (status)
        DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
                config->bConfigurationValue, status);
    return status;
}

形参cdev内有usb_gadget结构体,所以个人认为UVC描述符参数设置这部分还是属于UDC层。
主要设置流程:usb_add_config->webcam_config_bind->uvc_bind_config->usb_add_function->uvc_function_bind
最后一个函数很关键,其他都是套路

uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{
    struct usb_composite_dev *cdev = c->cdev;
    struct uvc_device *uvc = to_uvc(f);
    struct usb_ep *ep;
    int ret = -EINVAL;

    INFO(cdev, "uvc_function_bind\n");
#if 0
    /* Allocate endpoints. */
    ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
    if (!ep) {
        INFO(cdev, "Unable to allocate control EP\n");
        goto error;
    }
    uvc->control_ep = ep;
    ep->driver_data = uvc;
#endif
    ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep);
    if (!ep) {
        INFO(cdev, "Unable to allocate streaming EP\n");
        goto error;
    }
    uvc->video.ep = ep;
    ep->driver_data = uvc;

    /* Allocate interface IDs. */
    if ((ret = usb_interface_id(c, f)) < 0)
        goto error;
    uvc_iad.bFirstInterface = ret;
    uvc_control_intf.bInterfaceNumber = ret;
    uvc->control_intf = ret;

    if ((ret = usb_interface_id(c, f)) < 0)
        goto error;
    uvc_streaming_intf_alt0.bInterfaceNumber = ret;
    uvc_streaming_intf_alt1.bInterfaceNumber = ret;
    uvc->streaming_intf = ret;

    /* Copy descriptors. */
    f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
    f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);

    /* Preallocate control endpoint request. */
    uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
    uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
    if (uvc->control_req == NULL || uvc->control_buf == NULL) {
        ret = -ENOMEM;
        goto error;
    }

    uvc->control_req->buf = uvc->control_buf;
    uvc->control_req->complete = uvc_function_ep0_complete;
    uvc->control_req->context = uvc;

    /* Avoid letting this gadget enumerate until the userspace server is
     * active.
     */
    if ((ret = usb_function_deactivate(f)) < 0)
        goto error;

    /* Initialise video. */
    ret = uvc_video_init(&uvc->video);
    if (ret < 0)
        goto error;

    /* Register a V4L2 device. */
    ret = uvc_register_video(uvc);
    if (ret < 0) {
        printk(KERN_INFO "Unable to register video device\n");
        goto error;
    }

    return 0;

error:
    if (uvc->vdev)
        video_device_release(uvc->vdev);

    if (uvc->control_ep)
        uvc->control_ep->driver_data = NULL;
    if (uvc->video.ep)
        uvc->video.ep->driver_data = NULL;

    if (uvc->control_req) {
        usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
        kfree(uvc->control_buf);
    }

    kfree(f->descriptors);
    kfree(f->hs_descriptors);
    kfree(f->ss_descriptors);
    return ret;
}

这里面
1.Allocate interface IDs分配接口ID
2. Copy descriptors填充USB设备描述符,非常重要
3. Preallocate control endpoint request不去鸟他
4. Avoid letting this gadget enumerate until the userspace server is active不去鸟他
5. Initialise video初始化
6. Register a V4L2 device注册V4L2设备,重要

UVC描述符参数设置

需要的资料:UVC 1.5 Class specification.pdf

uvc_function_bind中复制描述符方法


uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
{
    struct uvc_input_header_descriptor *uvc_streaming_header;
    struct uvc_header_descriptor *uvc_control_header;
    const struct uvc_descriptor_header * const *uvc_streaming_cls;
    const struct usb_descriptor_header * const *uvc_streaming_std;
    const struct usb_descriptor_header * const *src;
    struct usb_descriptor_header **dst;
    struct usb_descriptor_header **hdr;
    unsigned int control_size;
    unsigned int streaming_size;
    unsigned int n_desc;
    unsigned int bytes;
    void *mem;

    uvc_streaming_cls = (speed == USB_SPEED_FULL)
              ? uvc->desc.fs_streaming : uvc->desc.hs_streaming;
    uvc_streaming_std = (speed == USB_SPEED_FULL)
              ? uvc_fs_streaming : uvc_hs_streaming;

    /* Descriptors layout
     *
     * uvc_iad
     * uvc_control_intf
     * Class-specific UVC control descriptors
     * uvc_control_ep
     * uvc_control_cs_ep
     * uvc_streaming_intf_alt0
     * Class-specific UVC streaming descriptors
     * uvc_{fs|hs}_streaming
     */

    /* Count descriptors and compute their size. */
    control_size = 0;
    streaming_size = 0;
    bytes = uvc_iad.bLength + uvc_control_intf.bLength
          /*+ uvc_control_ep.bLength + uvc_control_cs_ep.bLength*/
          + uvc_streaming_intf_alt0.bLength;
    n_desc = 3;

    for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) {
        control_size += (*src)->bLength;
        bytes += (*src)->bLength;
        n_desc++;
    }
    for (src = (const struct usb_descriptor_header**)uvc_streaming_cls; *src; ++src) {
        streaming_size += (*src)->bLength;
        bytes += (*src)->bLength;
        n_desc++;
    }
    for (src = uvc_streaming_std; *src; ++src) {
        bytes += (*src)->bLength;
        n_desc++;
    }

    mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
    if (mem == NULL)
        return NULL;

    hdr = mem;
    dst = mem;
    mem += (n_desc + 1) * sizeof(*src);

    /* Copy the descriptors. */
    UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
    UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);

    uvc_control_header = mem;
    UVC_COPY_DESCRIPTORS(mem, dst,
        (const struct usb_descriptor_header**)uvc->desc.control);
    uvc_control_header->wTotalLength = cpu_to_le16(control_size);
    uvc_control_header->bInCollection = 1;
    uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;

    //UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
    //UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
    UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);

    uvc_streaming_header = mem;
    UVC_COPY_DESCRIPTORS(mem, dst,
        (const struct usb_descriptor_header**)uvc_streaming_cls);
    uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
    uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress;

    UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);

    *dst = NULL;
    return hdr;
}

复制了这些

     * uvc_iad
     * uvc_control_intf
     * Class-specific UVC control descriptors
     * uvc_control_ep
     * uvc_control_cs_ep
     * uvc_streaming_intf_alt0
     * Class-specific UVC streaming descriptors
     * uvc_{fs|hs}_streaming

1.uvc_iad

见pdf表-Standard Video Interface Collection IAD,由于有些参数都是缺省值,所以这里以及以下的说明只圈重点,
bInterfaceCount:一般设置为2,包括控制(VC)流(VS)

2.uvc_control_intf

Standard VC Interface Descriptor
这个是表1的VC
.bInterfaceNumber:设置为0

3.uvc_control_ep

Standard VC Interrupt Endpoint Descriptor

4.uvc_control_cs_ep

Class-specific VC Interrupt Endpoint Descriptor

5.uvc_streaming_intf_alt0

Standard VC Interface Descriptor
同2.只不过这个是VS,所以
.bInterfaceNumber:设置为1

6.uvc_{fs|hs}_streaming

文中用的是uvc_streaming_cls,记录了
1. Class-specific VS Interface Input Header Descriptor
2. 具体格式描述符,比如Uncompressed Payload - 3.1.1. Uncompressed Video Format Descriptor
3. Color Matching Descriptor
可能一定要按照这个顺序排在uvc_streaming_cls中

DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 4);

static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 4) uvc_input_header = {
    .bLength        = UVC_DT_INPUT_HEADER_SIZE(1, 4),
    .bDescriptorType    = USB_DT_CS_INTERFACE,
    .bDescriptorSubType = UVC_VS_INPUT_HEADER,
    .bNumFormats        = 4,
    .wTotalLength       = 0, /* dynamic */
    .bEndpointAddress   = 0, /* dynamic */
    .bmInfo         = 0,
    .bTerminalLink      = 3,
    .bStillCaptureMethod    = 0,
    .bTriggerSupport    = 0,
    .bTriggerUsage      = 0,
    .bControlSize       = 1,
#if ENABLE_YUYV==1
    .bmaControls[INDEX_YUYV - 1][0] = 0,
#endif
#if ENABLE_YV12==1
    .bmaControls[INDEX_YV12 - 1][0] = 0,
#endif
#if ENABLE_MJPG==1
    .bmaControls[INDEX_MJPG - 1][0] = 4,
#endif
#if ENABLE_H264==1
    .bmaControls[INDEX_H264 - 1][0] = 4,
#endif
};
#if ENABLE_YUYV==1

这里设置了4个格式,bNumFormats=4;bmaControls是控制参数,大小4

结束

参考资料:
https://blog.csdn.net/lizuobin2/article/details/53149583
https://blog.csdn.net/go_str/article/details/80844175
https://blog.csdn.net/qingkongyeyue/article/details/52396933

猜你喜欢

转载自blog.csdn.net/schumi2000/article/details/82020745
今日推荐