📄 uvc_driver.c
字号:
*/ if ((ret = uvc_video_init(&dev->video)) < 0) { uvc_printk(KERN_ERR, "Failed to initialize the device " "(%d).\n", ret); return ret; } /* Register the device with V4L. */ vdev = video_device_alloc(); if (vdev == NULL) return -1; if (dev->udev->product != NULL) strncpy(vdev->name, dev->udev->product, sizeof vdev->name); else snprintf(vdev->name, sizeof vdev->name, "UVC Camera (%04x:%04x)", le16_to_cpu(dev->udev->descriptor.idVendor), le16_to_cpu(dev->udev->descriptor.idProduct)); /* We already hold a reference to dev->udev. The video device will be * unregistered before the reference is released, so we don't need to * get another one. */ vdev->dev = &dev->intf->dev; vdev->type = 0; vdev->type2 = 0; vdev->minor = -1; vdev->fops = &uvc_fops; vdev->release = video_device_release; /* Set the driver data before calling video_register_device, otherwise * uvc_v4l2_open might race us. * * FIXME: usb_set_intfdata hasn't been called so far. Is that a * problem ? Does any function which could be called here get * a pointer to the usb_interface ? */ dev->video.vdev = vdev; video_set_drvdata(vdev, &dev->video); if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { dev->video.vdev = NULL; video_device_release(vdev); return -1; } return 0;}/* * Delete the UVC device. * * Called by the kernel when the last reference to the uvc_device structure * is released. * * Unregistering the video devices is done here because every opened instance * must be closed before the device can be unregistered. An alternative would * have been to use another reference count for uvc_v4l2_open/uvc_release, and * unregister the video devices on disconnect when that reference count drops * to zero. * * As this function is called after or during disconnect(), all URBs have * already been canceled by the USB core. There is no need to kill the * interrupt URB manually. */void uvc_delete(struct kref *kref){ struct uvc_device *dev = container_of(kref, struct uvc_device, kref); struct list_head *p, *n; /* Unregister the video device */ uvc_unregister_video(dev); usb_put_intf(dev->intf); usb_put_dev(dev->udev); uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; entity = list_entry(p, struct uvc_entity, list); kfree(entity); } list_for_each_safe(p, n, &dev->streaming) { struct uvc_streaming *streaming; streaming = list_entry(p, struct uvc_streaming, list); usb_put_intf(streaming->intf); kfree(streaming->format); kfree(streaming->header.bmaControls); kfree(streaming); } kfree(dev);}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; 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); INIT_LIST_HEAD(&dev->streaming); kref_init(&dev->kref); dev->udev = usb_get_dev(udev); dev->intf = usb_get_intf(intf); dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; dev->quirks = id->driver_info | uvc_quirks_param; /* 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_printk(KERN_INFO, "Found UVC %u.%02u 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 (uvc_quirks_param != 0) { uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module " "parameter for testing purpose.\n", uvc_quirks_param); uvc_printk(KERN_INFO, "Please report required quirks to the " "linux-uvc-devel mailing list.\n"); } /* Initialize controls */ if (uvc_ctrl_init_device(dev) < 0) goto error; /* Register the video devices */ if (uvc_register_video(dev) < 0) goto error; /* Save our data pointer in the interface data */ usb_set_intfdata(intf, dev); /* 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); } uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); return 0;error: kref_put(&dev->kref, uvc_delete); return -ENODEV;}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 == SC_VIDEOSTREAMING) return; /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide * lock is needed to prevent uvc_disconnect from releasing its * reference to the uvc_device instance after uvc_v4l2_open() received * the pointer to the device (video_devdata) but before it got the * chance to increase the reference count (kref_get). An alternative * (used by the usb-skeleton driver) would have been to use the big * kernel lock instead of a driver-specific mutex (open calls on * char devices are protected by the BKL), but that approach is not * recommended for new code. */ mutex_lock(&uvc_driver.open_mutex); dev->state |= UVC_DEV_DISCONNECTED; kref_put(&dev->kref, uvc_delete); mutex_unlock(&uvc_driver.open_mutex);}static int uvc_suspend(struct usb_interface *intf, pm_message_t message){ struct uvc_device *dev = usb_get_intfdata(intf); uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); /* Controls are cached on the fly so they don't need to be saved. */ if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) return uvc_status_suspend(dev); if (dev->video.streaming->intf != intf) { uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " "interface mismatch.\n"); return -EINVAL; } return uvc_video_suspend(&dev->video);}static int uvc_resume(struct usb_interface *intf){ struct uvc_device *dev = usb_get_intfdata(intf); int ret; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { if ((ret = uvc_ctrl_resume_device(dev)) < 0) return ret; return uvc_status_resume(dev); } if (dev->video.streaming->intf != intf) { uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " "interface mismatch.\n"); return -EINVAL; } return uvc_video_resume(&dev->video);}/* ------------------------------------------------------------------------ * Driver initialization and cleanup *//* * The Logitech cameras listed below have their interface class set to * VENDOR_SPEC because they don't announce themselves as UVC devices, even * though they are compliant. */static struct usb_device_id uvc_ids[] = { /* ALi M5606 (Clevo M540SR) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0402, .idProduct = 0x5606, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Creative Live! Optia */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x041e, .idProduct = 0x4057, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x045e, .idProduct = 0x00f8, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Logitech Quickcam Fusion */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c1, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Logitech Quickcam Orbit MP */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Logitech Quickcam Pro for Notebook */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c3, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Logitech Quickcam Pro 5000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c5, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Logitech Quickcam OEM Dell Notebook */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c6, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Logitech Quickcam OEM Cisco VT Camera II */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x046d, .idProduct = 0x08c7, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x05ac, .idProduct = 0x8501, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX | UVC_QUIRK_BUILTIN_ISIGHT }, /* Genesys Logic USB 2.0 PC Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x05e3, .idProduct = 0x0505, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Silicon Motion SM371 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x090c, .idProduct = 0xb371, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0e8d, .idProduct = 0x0004, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x174f, .idProduct = 0x5212, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x18cd, .idProduct = 0xcafe, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, /* Bodelin ProScopeHR */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x19ab, .idProduct = 0x1000, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STATUS_INTERVAL }, /* Acer OEM Webcam - Unknown vendor */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x5986, .idProduct = 0x0100, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Acer Crystal Eye webcam */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x5986, .idProduct = 0x0102, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Acer OrbiCam - Unknown vendor */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x5986, .idProduct = 0x0200, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {}};MODULE_DEVICE_TABLE(usb, uvc_ids);struct uvc_driver uvc_driver = { .driver = {#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) .owner = THIS_MODULE,#endif .name = "uvcvideo", .probe = uvc_probe, .disconnect = uvc_disconnect, .suspend = uvc_suspend, .resume = uvc_resume, .id_table = uvc_ids,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) .supports_autosuspend = 1,#endif },};static int __init uvc_init(void){ int result; INIT_LIST_HEAD(&uvc_driver.devices); INIT_LIST_HEAD(&uvc_driver.controls); mutex_init(&uvc_driver.open_mutex); mutex_init(&uvc_driver.ctrl_mutex); uvc_ctrl_init(); result = usb_register(&uvc_driver.driver); if (result == 0) printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); return result;}static void __exit uvc_cleanup(void){ usb_deregister(&uvc_driver.driver);}module_init(uvc_init);module_exit(uvc_cleanup);module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -