usb设备驱动之uvc设备

声明:涉及相关内容包括v4l2框架/drivers/media/v4l2-core/,usb设备控制器驱动/drivers/usb/dwc3/,usb composite驱动(webcam)/drivers/usb/gadget目录下基本上文件都用了,包括function目录与v4l2相关,/drivers/usb/gadget/legacy/webcam.c通过composite.c注册uvc驱动,core等。
1、usb设备驱动控制器注册
驱动路径:/drivers/usb/dwc3/core.c—>platform_driver

static const struct dev_pm_ops dwc3_dev_pm_ops = {SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,dwc3_runtime_idle)
};#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {{.compatible = "snps,dwc3"},{.compatible = "synopsys,dwc3"},{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_match);
#endif#ifdef CONFIG_ACPI#define ACPI_ID_INTEL_BSW   "808622B7"static const struct acpi_device_id dwc3_acpi_match[] = {{ ACPI_ID_INTEL_BSW, 0 },{ },
};
MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
#endifstatic struct platform_driver dwc3_driver = {.probe      = dwc3_probe,.remove       = dwc3_remove,.driver      = {.name   = "dwc3",.of_match_table = of_match_ptr(of_dwc3_match),.acpi_match_table = ACPI_PTR(dwc3_acpi_match),.pm   = &dwc3_dev_pm_ops,},
};module_platform_driver(dwc3_driver);

2、probe函数

static int dwc3_probe(struct platform_device *pdev)
{
省略部分代码。。。。。。。ret = dwc3_get_dr_mode(dwc);if (ret)goto err3;ret = dwc3_alloc_scratch_buffers(dwc);if (ret)goto err3;ret = dwc3_core_init(dwc);  //配置usb控制器相关寄存器并配置为usb device模式if (ret) {dev_err(dev, "failed to initialize core\n");goto err4;}dwc3_outstanding_pipe_choose(dwc);ret = dwc3_core_init_mode(dwc);if (ret)goto err5;dwc3_debugfs_init(dwc);pm_runtime_put(dev);return 0;
}

3、dwc3_core_init_mode //注册对应模式的设备驱动,usb device注册dwc3_gadget_init

static int dwc3_core_init_mode(struct dwc3 *dwc)
{struct device *dev = dwc->dev;int ret;switch (dwc->dr_mode) {case USB_DR_MODE_PERIPHERAL:ret = dwc3_gadget_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize gadget\n");return ret;}break;case USB_DR_MODE_HOST:ret = dwc3_host_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize host\n");return ret;}break;case USB_DR_MODE_OTG:ret = dwc3_host_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize host\n");return ret;}ret = dwc3_gadget_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize gadget\n");return ret;}break;default:dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);return -EINVAL;}return 0;
}

4、注册usb设备驱动看 路径:/drivers/usb/dwc3/gadget.c —>dwc3_gadget_init

int dwc3_gadget_init(struct dwc3 *dwc)
{
省略代码。。。。。。。ret = dwc3_gadget_init_endpoints(dwc);  //初始化usb端点,主要用来传输数据if (ret)goto err5;dwc3_proc_init(dwc);ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);  //添加一个gadget驱动到udcif (ret) {dev_err(dwc->dev, "failed to register udc\n");goto err5;}return 0;
}

5、dwc3_gadget_init_endpoints初始化输入输出端点及0端点—>dwc3_gadget_init_hw_endpoints//主要分析该函数与uvc收发数据相关其他不做分析

static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {.bLength  = USB_DT_ENDPOINT_SIZE,.bDescriptorType = USB_DT_ENDPOINT,.bmAttributes   = USB_ENDPOINT_XFER_CONTROL,
};static const struct usb_ep_ops dwc3_gadget_ep0_ops = {.enable        = dwc3_gadget_ep0_enable,.disable  = dwc3_gadget_ep0_disable,.alloc_request   = dwc3_gadget_ep_alloc_request,.free_request   = dwc3_gadget_ep_free_request,.queue       = dwc3_gadget_ep0_queue,.dequeue   = dwc3_gadget_ep_dequeue,.set_halt = dwc3_gadget_ep0_set_halt,.set_wedge  = dwc3_gadget_ep_set_wedge,
};static const struct usb_ep_ops dwc3_gadget_ep_ops = {.enable     = dwc3_gadget_ep_enable,.disable   = dwc3_gadget_ep_disable,.alloc_request    = dwc3_gadget_ep_alloc_request,.free_request   = dwc3_gadget_ep_free_request,.queue       = dwc3_gadget_ep_queue,.dequeue    = dwc3_gadget_ep_dequeue,.set_halt = dwc3_gadget_ep_set_halt,.set_wedge   = dwc3_gadget_ep_set_wedge,
};static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,u8 num, u32 direction)
{struct dwc3_ep         *dep;u8             i;for (i = 0; i < num; i++) {u8 epnum = (i << 1) | (direction ? 1 : 0);dep = kzalloc(sizeof(*dep), GFP_KERNEL);if (!dep)return -ENOMEM;dep->dwc = dwc;dep->number = epnum;dep->direction = !!direction;dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);dwc->eps[epnum] = dep;snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,(epnum & 1) ? "in" : "out");dep->endpoint.name = dep->name;spin_lock_init(&dep->lock);dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);if (epnum == 0 || epnum == 1) {usb_ep_set_maxpacket_limit(&dep->endpoint, 512);dep->endpoint.maxburst = 1;dep->endpoint.ops = &dwc3_gadget_ep0_ops;if (!epnum)dwc->gadget.ep0 = &dep->endpoint;} else {int     ret;usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);dep->endpoint.max_streams = 15;dep->endpoint.ops = &dwc3_gadget_ep_ops;   //实际usb端点的收发数据等函数list_add_tail(&dep->endpoint.ep_list,&dwc->gadget.ep_list);   //主要这里加入链表供绑定uvc驱动的时候用ret = dwc3_alloc_trb_pool(dep);if (ret)return ret;}if (epnum == 0 || epnum == 1) {dep->endpoint.caps.type_control = true;} else {dep->endpoint.caps.type_iso = true;dep->endpoint.caps.type_bulk = true;dep->endpoint.caps.type_int = true;}dep->endpoint.caps.dir_in = !!direction;dep->endpoint.caps.dir_out = !direction;INIT_LIST_HEAD(&dep->pending_list);INIT_LIST_HEAD(&dep->started_list);}return 0;
}

6、uvc 之usb_composite_driver驱动 ,路径:/drivers/usb/gadget/legacy/webcam.c

static struct usb_composite_driver webcam_driver = {.name       = "g_webcam",.dev        = &webcam_device_descriptor,.strings   = webcam_device_strings,.max_speed = USB_SPEED_SUPER,.bind        = webcam_bind,.unbind      = webcam_unbind,
};module_usb_composite_driver(webcam_driver);#define module_usb_composite_driver(__usb_composite_driver) \module_driver(__usb_composite_driver, usb_composite_probe, \usb_composite_unregister)//会调用usb_composite_probe函数#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

7、usb_composite_probe---->usb_gadget_probe_driver—>udc_bind_to_driver—>composite_bind—>webcam_bind—>usb_add_config—>webcam_config_bind—>usb_add_function—>uvc_function_bind重点这个函数 路径:/drivers/usb/gadget/function/f_uvc.c

static int
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{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;if (gadget_is_superspeed(c->cdev->gadget))ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,&uvc_ss_streaming_comp);  **//该处通过cdev->gadget链表获取到了前面usb加入链表的gadget端点用于uvc设备数据传输**else if (gadget_is_dualspeed(cdev->gadget))ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);elseep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);if (!ep) {INFO(cdev, "Unable to allocate streaming EP\n");goto error;}uvc->video.ep = ep;这里赋值 实际数据传是的时候用到uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;us = usb_gstrings_attach(cdev, uvc_function_strings,ARRAY_SIZE(uvc_en_us_strings));if (IS_ERR(us)) {ret = PTR_ERR(us);goto error;}uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;ret = us[UVC_STRING_STREAMING_IDX].id;uvc_streaming_intf_alt0.iInterface = ret;uvc_streaming_intf_alt1.iInterface = ret;/* 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->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);if (IS_ERR(f->fs_descriptors)) {ret = PTR_ERR(f->fs_descriptors);f->fs_descriptors = NULL;goto error;}if (gadget_is_dualspeed(cdev->gadget)) {f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);if (IS_ERR(f->hs_descriptors)) {ret = PTR_ERR(f->hs_descriptors);f->hs_descriptors = NULL;goto error;}}if (gadget_is_superspeed(c->cdev->gadget)) {f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);if (IS_ERR(f->ss_descriptors)) {ret = PTR_ERR(f->ss_descriptors);f->ss_descriptors = NULL;goto error;}}/* 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;
//以下几个函数将gadget注册进内核v4l2框架if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {printk(KERN_INFO "v4l2_device_register failed\n");goto error;}/* Initialise video. */ret = uvcg_video_init(&uvc->video, uvc);if (ret < 0)goto error;/* Register a V4L2 device. */ret = uvc_register_video(uvc);//主要该函数注册了相关操作函数,供v4l2回调用if (ret < 0) {printk(KERN_INFO "Unable to register video device\n");goto error;}return 0;error:v4l2_device_unregister(&uvc->v4l2_dev);if (uvc->control_req)usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);kfree(uvc->control_buf);usb_free_all_descriptors(f);return ret;
}
实际获取ep端点的函数
struct usb_ep *usb_ep_autoconfig_ss(struct usb_gadget   *gadget, struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp)
{struct usb_ep  *ep;u8      type;type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;if (gadget->ops->match_ep) {ep = gadget->ops->match_ep(gadget, desc, ep_comp);if (ep)goto found_ep;}/* Second, look at endpoints until an unclaimed one looks usable */if (type == USB_ENDPOINT_XFER_INT) {list_for_each_entry_reverse(ep, &gadget->ep_list, ep_list) {if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))goto found_ep;}} else {list_for_each_entry(ep, &gadget->ep_list, ep_list) {if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))//通过前面加入的链表gadget->ep_list查找epgoto found_ep;}}/* Fail */return NULL;
found_ep:/** If the protocol driver hasn't yet decided on wMaxPacketSize* and wants to know the maximum possible, provide the info.*/if (desc->wMaxPacketSize == 0)desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);/* report address */desc->bEndpointAddress &= USB_DIR_IN;if (isdigit(ep->name[2])) {u8 num = simple_strtoul(&ep->name[2], NULL, 10);desc->bEndpointAddress |= num;} else if (desc->bEndpointAddress & USB_DIR_IN) {if (++gadget->in_epnum > 15)return NULL;desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;} else {if (++gadget->out_epnum > 15)return NULL;desc->bEndpointAddress |= gadget->out_epnum;}/* report (variable) full speed bulk maxpacket */if ((type == USB_ENDPOINT_XFER_BULK) && !ep_comp) {int size = ep->maxpacket_limit;/* min() doesn't work on bitfields with gcc-3.5 */if (size > 64)size = 64;desc->wMaxPacketSize = cpu_to_le16(size);}ep->address = desc->bEndpointAddress;ep->desc = NULL;ep->comp_desc = NULL;ep->claimed = true;return ep;
}

8、uvc_register_video

————————————————————————————
路径:/drivers/usb/gadget/function/uvc_v4l2.c
struct v4l2_file_operations uvc_v4l2_fops = {.owner        = THIS_MODULE,.open        = uvc_v4l2_open,.release   = uvc_v4l2_release,.unlocked_ioctl = video_ioctl2,.mmap       = uvc_v4l2_mmap,.poll      = uvc_v4l2_poll,
#ifndef CONFIG_MMU.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {.vidioc_querycap = uvc_v4l2_querycap,.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,.vidioc_reqbufs = uvc_v4l2_reqbufs,.vidioc_querybuf = uvc_v4l2_querybuf,.vidioc_qbuf = uvc_v4l2_qbuf,.vidioc_dqbuf = uvc_v4l2_dqbuf,.vidioc_streamon = uvc_v4l2_streamon,.vidioc_streamoff = uvc_v4l2_streamoff,.vidioc_subscribe_event = uvc_v4l2_subscribe_event,.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,.vidioc_default = uvc_v4l2_ioctl_default,
};
————————————————————————————
static int uvc_register_video(struct uvc_device *uvc)
{struct usb_composite_dev *cdev = uvc->func.config->cdev;/* TODO reference counting. */uvc->vdev.v4l2_dev = &uvc->v4l2_dev;uvc->vdev.fops = &uvc_v4l2_fops;uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;uvc->vdev.release = video_device_release_empty;uvc->vdev.vfl_dir = VFL_DIR_TX;uvc->vdev.lock = &uvc->video.mutex;strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));video_set_drvdata(&uvc->vdev, uvc);return video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
}

9、video_register_device—>__video_register_device调用v4l2框架内函数注册进内核v4l2框架 路径:/drivers/media/v4l2-core/v4l2-dev.c

int __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner)
{int i = 0;int ret;int minor_offset = 0;int minor_cnt = VIDEO_NUM_DEVICES;const char *name_base;/* A minor value of -1 marks this video device as neverhaving been registered */vdev->minor = -1;/* the release callback MUST be present */if (WARN_ON(!vdev->release))return -EINVAL;/* the v4l2_dev pointer MUST be present */if (WARN_ON(!vdev->v4l2_dev))return -EINVAL;/* v4l2_fh support */spin_lock_init(&vdev->fh_lock);INIT_LIST_HEAD(&vdev->fh_list);/* Part 1: check device type */switch (type) {case VFL_TYPE_GRABBER:name_base = "video";break;case VFL_TYPE_VBI:name_base = "vbi";break;case VFL_TYPE_RADIO:name_base = "radio";break;case VFL_TYPE_SUBDEV:name_base = "v4l-subdev";break;case VFL_TYPE_SDR:/* Use device name 'swradio' because 'sdr' was already taken. */name_base = "swradio";break;case VFL_TYPE_TOUCH:name_base = "v4l-touch";break;default:printk(KERN_ERR "%s called with unknown type: %d\n",__func__, type);return -EINVAL;}vdev->vfl_type = type;vdev->cdev = NULL;if (vdev->dev_parent == NULL)vdev->dev_parent = vdev->v4l2_dev->dev;if (vdev->ctrl_handler == NULL)vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;/* If the prio state pointer is NULL, then use the v4l2_deviceprio state. */if (vdev->prio == NULL)vdev->prio = &vdev->v4l2_dev->prio;/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES/* Keep the ranges for the first four types for historical* reasons.* Newer devices (not yet in place) should use the range* of 128-191 and just pick the first free minor there* (new style). */switch (type) {case VFL_TYPE_GRABBER:minor_offset = 0;minor_cnt = 64;break;case VFL_TYPE_RADIO:minor_offset = 64;minor_cnt = 64;break;case VFL_TYPE_VBI:minor_offset = 224;minor_cnt = 32;break;default:minor_offset = 128;minor_cnt = 64;break;}
#endif/* Pick a device node number */mutex_lock(&videodev_lock);nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);if (nr == minor_cnt)nr = devnode_find(vdev, 0, minor_cnt);if (nr == minor_cnt) {printk(KERN_ERR "could not get a free device node number\n");mutex_unlock(&videodev_lock);return -ENFILE;}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES/* 1-on-1 mapping of device node number to minor number */i = nr;
#else/* The device node number and minor numbers are independent, sowe just find the first free minor number. */for (i = 0; i < VIDEO_NUM_DEVICES; i++)if (video_device[i] == NULL)break;if (i == VIDEO_NUM_DEVICES) {mutex_unlock(&videodev_lock);printk(KERN_ERR "could not get a free minor\n");return -ENFILE;}
#endifvdev->minor = i + minor_offset;vdev->num = nr;devnode_set(vdev);/* Should not happen since we thought this minor was free */WARN_ON(video_device[vdev->minor] != NULL);vdev->index = get_index(vdev);video_device[vdev->minor] = vdev;mutex_unlock(&videodev_lock);if (vdev->ioctl_ops)determine_valid_ioctls(vdev);  //将前面注册的ioctl与v4l2框架的ioctl 一 一对应起来/* Part 3: Initialize the character device */vdev->cdev = cdev_alloc();if (vdev->cdev == NULL) {ret = -ENOMEM;goto cleanup;}vdev->cdev->ops = &v4l2_fops;vdev->cdev->owner = owner;ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);if (ret < 0) {printk(KERN_ERR "%s: cdev_add failed\n", __func__);kfree(vdev->cdev);vdev->cdev = NULL;goto cleanup;}/* Part 4: register the device with sysfs */vdev->dev.class = &video_class;vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);vdev->dev.parent = vdev->dev_parent;dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);ret = device_register(&vdev->dev);//注册相关驱动if (ret < 0) {printk(KERN_ERR "%s: device_register failed\n", __func__);goto cleanup;}/* Register the release callback that will be called when the lastreference to the device goes away. */vdev->dev.release = v4l2_device_release;if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,name_base, nr, video_device_node_name(vdev));/* Increase v4l2_device refcount */v4l2_device_get(vdev->v4l2_dev);/* Part 5: Register the entity. */ret = video_register_media_controller(vdev, type);/* Part 6: Activate this minor. The char device can now be used. */set_bit(V4L2_FL_REGISTERED, &vdev->flags);return 0;cleanup:mutex_lock(&videodev_lock);if (vdev->cdev)cdev_del(vdev->cdev);video_device[vdev->minor] = NULL;devnode_clear(vdev);mutex_unlock(&videodev_lock);/* Mark this video device as never having been registered. */vdev->minor = -1;return ret;
}

例 ioctl(dev->fd, VIDIOC_S_FMT, &fmt) 调用流程说明
v4l2最顶层ioctl数组
static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0),
IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)),
IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
};

ioctl—>/drivers/usb/gadget/function/uvc_v4l2.c:v4l2_file_operations uvc_v4l2_fops—>video_ioctl2(该函数在/drivers/media/v4l2-core/v4l2-ioctl.c)—>video_usercopy—>__video_do_ioctl—>v4l2_ioctls(这是个数组,找到对应的cmd执行对应的ioctl)—>v4l_qbuf(入队ioctl)—>ops->vidioc_qbuf(file, fh, p)—>uvc_v4l2_qbuf—>uvcg_video_pump—>uvcg_video_ep_queue—>usb_ep_queue—>ep->ops->queue(ep, req, gfp_flags)—>dwc3_gadget_ep_queue—>__dwc3_gadget_ep_queue(相关dma数据拷贝操作之后调用后面函数使能发送)—>__dwc3_gadget_kick_transfer

usb设备驱动之uvc设备相关推荐

  1. 首创STM32 USB主机驱动4G rndis设备

    1.4G模块使用技术现状与难题 4G模块支持的对外通信接口有串口和USB接口,串口一般使用AT指令的方式,这个在MCU类产品上面使用的很多,USB接口主用于在连接APU应用处理器上面,由LINUX操作 ...

  2. 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动

    以下内容转载于微信公众号:嵌入式企鹅圈.如有侵权,请告知删除. 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术. 对于初学者来说会非常 ...

  3. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    参考原文:https://www.kancloud.cn/yueqian_scut/emlinux/106829 对原文笔误地方做了修改.重新排版 目录 字符设备驱动.平台设备驱动.设备驱动模型.sy ...

  4. Linux设备驱动篇——[I2C设备驱动-1]

    Linux 设备驱动篇之I2c设备驱动 fulinux 一.I2C驱动体系 虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却有相当的复杂度,它主要由3部分组成,即I2C设 ...

  5. linux中流设备_[快速上手Linux设备驱动]之块设备驱动流程详解一

    [快速上手Linux设备驱动]之块设备驱动流程详解一 walfred已经在[快速上手Linux设备驱动]之我看字符设备驱动一 文中详细讲解了linux下字符设备驱动,并紧接着用四篇文章描述了Linux ...

  6. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  7. linux 设备驱动 ppt,LINUX设备驱动开发3.ppt

    <LINUX设备驱动开发3.ppt>由会员分享,可在线阅读,更多相关<LINUX设备驱动开发3.ppt(30页珍藏版)>请在人人文库网上搜索. 1.第三天课程,FLASH设备驱 ...

  8. linux设备驱动子系统,Linux设备驱动子系统终极弹 - USB

    0. 预备理论 1. USB Core 2. USB Hub 3. USB OTG 4. USB Host 5. USB Gadget 6. USB Mass Storage USB博大精深,不是一两 ...

  9. usb免驱动之UVC

    在买一些usb设备的时候,很多商家都有标出"免驱动,即插即用"的字样,大家都知道有一些usb设备,如鼠标等,只要插上不用安装驱动就可以用了,但是有的就需要安装驱动之后才可以用,这是 ...

最新文章

  1. C++ Primer 5th笔记(chap 16 模板和泛型编程)类型无关和模板编译
  2. cookie+memcached实现单点登陆
  3. git 创建webpack项目_Webpack入门:从安装到配置
  4. Java中通过代理对类进行修改
  5. 该文件可能是只读的 或者您要访问的位置_喔噢小贴士:如何保护PPT不被更改,将其设为只读...
  6. 用cloud-zoom做一个仿淘宝的宝贝放大镜查看功能
  7. Sql server备份数据库不执行
  8. PRML exercises 10.3 解析
  9. 基于min-max搜索和alpha-beta(α-β)剪枝的五子棋的c语言实现(带简单禁手)
  10. 基于眨眼状态的在线疲劳检测系统(Matlab-GUI设计)
  11. Python计算温度植被干旱指数(TVDI)
  12. android中LitePal的使用
  13. getattr()函数解析
  14. 程序的可重用性的概念_可重用的Web应用程序策略:在多个位置运行同一应用程序的三种模式
  15. 28.Android 十分方便的EasyRecyclerView
  16. 50、诗句按照特定个数输出
  17. caffe HDF5Data 层使用及数据生成
  18. 开发人员被苹果公司拒绝删除 Apple ID 数据
  19. Android 下拉控件Spinner
  20. 【MOOC浙大翁恺】C语言学习笔记

热门文章

  1. 名帖109 鲜于枢 小楷《老子道德经卷》
  2. seller_info - 获得义乌购店铺详情
  3. 面试题--5个数能组成多少中不同的二叉搜索树的结构--卡特兰数
  4. 机器人兴趣班奖状_美术兴趣班奖状称号
  5. 修改win7锁屏壁纸,突破壁纸大小256Kb限制
  6. 最新获取iOS设备型号大全(包括iphone7,iphone7 Plus)
  7. linux数据异地备份,linux异地实时备份
  8. 【Linux下的性能测试】(三) : nmon图形分析
  9. UE4 如何开启 EQS
  10. 虚拟机屏幕太小 如何调整?