📄 uvc_driver.c
字号:
buflen -= buffer[0]; buffer += buffer[0]; } /* Check if the optional status endpoint is present. */ if (alts->desc.bNumEndpoints == 1) { struct usb_host_endpoint *ep = &alts->endpoint[0]; struct usb_endpoint_descriptor *desc = &ep->desc; if (usb_endpoint_is_int_in(desc) && le16_to_cpu(desc->wMaxPacketSize) >= 8 && desc->bInterval != 0) { uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " "(addr %02x).\n", desc->bEndpointAddress); dev->int_ep = ep; } } return 0;}/* ------------------------------------------------------------------------ * USB probe and disconnect *//* * Unregister the video devices. */static void uvc_unregister_video(struct uvc_device *dev){ if (dev->video.vdev) { if (dev->video.vdev->minor == -1) video_device_release(dev->video.vdev); else video_unregister_device(dev->video.vdev); dev->video.vdev = NULL; }}/* * Scan the UVC descriptors to locate a chain starting at an Output Terminal * and containing only a Processing Unit, optional Extension Units and an * Input Terminal. A side forward scan is made on each detected entity to * check for additional extension units. */static int uvc_scan_chain(struct uvc_video_device *video){ struct uvc_entity *entity, *forward; unsigned int ext = 0; int id, found; entity = video->oterm; uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); id = entity->output.bSourceID; while (1) { /* Forward scan */ forward = NULL; found = 0; while (1) { forward = uvc_entity_by_reference(video->dev, id, forward); if (forward == NULL) break; if (forward == entity || forward->type != VC_EXTENSION_UNIT) continue; if (forward->extension.bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" "more than 1 input pin.\n", id); return -1; } video->extension[ext++] = forward; if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); printk(" %d", forward->id); found = 1; } } if (found) printk(")"); /* Backward scan */ entity = uvc_entity_by_id(video->dev, id); if (entity == NULL || !UVC_ENTITY_IS_UNIT(entity)) break; if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- Unit %d", entity->id); switch (entity->type) { case VC_EXTENSION_UNIT: if (entity->extension.bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" "more than 1 input pin.\n", id); return -1; } video->extension[ext++] = entity; id = entity->extension.baSourceID[0]; break; case VC_PROCESSING_UNIT: if (video->processing != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple " "Processing Units in chain.\n"); return -1; } video->processing = entity; id = entity->processing.bSourceID; break; case VC_SELECTOR_UNIT: uvc_trace(UVC_TRACE_DESCR, "Selector units are not " "supported yet.\n"); return -1; default: uvc_trace(UVC_TRACE_DESCR, "Unsupported unit type " "0x%04x found in chain.n", entity->type); return -1; } } if (entity == NULL || !UVC_ENTITY_IS_ITERM(entity)) { uvc_trace(UVC_TRACE_DESCR, "Input terminal %d not found.\n", id); return -1; } if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); video->iterm = entity; /* Initialize the video buffers queue. */ uvc_queue_init(&video->queue); return 0;}/* * Register the video devices. * * The driver currently supports a single video device per control interface * only. The terminal and units must match the following structure: * * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING * * The Extension Units, if present, must have a single input pin. The * Processing Unit and Extension Units can be in any order. Additional * Extension Units connected to the main chain as single-unit branches are * also supported. */static int uvc_register_video(struct uvc_device *dev){ struct video_device *vdev; struct uvc_entity *term; int found = 0, ret; /* Check if the control interface matches the structure we expect. */ list_for_each_entry(term, &dev->entities, list) { struct list_head *ps; if (term->type != TT_STREAMING) continue; memset(&dev->video, 0, sizeof dev->video); mutex_init(&dev->video.ctrl_mutex); dev->video.oterm = term; dev->video.dev = dev; if (uvc_scan_chain(&dev->video) < 0) continue; list_for_each(ps, &dev->streaming) { struct uvc_streaming *streaming; streaming = list_entry(ps, struct uvc_streaming, list); if (streaming->input.bTerminalLink == term->id) { dev->video.streaming = streaming; found = 1; break; } } if (found) break; } if (!found) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; } uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%d -> %d).\n", dev->video.iterm->id, dev->video.oterm->id); /* Initialize the streaming interface with default streaming * parameters. */ 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; sprintf(vdev->name, "USB Video Class"); /* 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->udev->dev; vdev->type = 0; vdev->type2 = 0; vdev->hardware = 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); // [wlq] usb_put_dev(dev->udev); 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); // [wlq] kfree(streaming->format); kfree(streaming->input.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; printk(KERN_INFO "Probing known UVC device (%04x, %04x)\n", udev->descriptor.idVendor, udev->descriptor.idProduct); /* 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 = intf; //usb_get_intf(intf); // [wlq] dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; dev->quirks = id->driver_info; /* 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; } /* 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 (dev->int_ep != NULL && (ret = uvc_init_status(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); if (dev == (void*)-1) return; /* 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); /* 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);}/* ------------------------------------------------------------------------ * 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[] = { /* 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, .id_table = uvc_ids, },};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); printk(KERN_INFO "trace = %d\n", uvc_trace_param); uvc_ctrl_init(); result = usb_register(&uvc_driver.driver); if (result == 0) printk(KERN_INFO DRIVER_DESC " (v" DRIVER_VERSION ")\n"); return result;}static void __exit uvc_cleanup(void){ usb_deregister(&uvc_driver.driver);}module_init(uvc_init);module_exit(uvc_cleanup);unsigned int uvc_trace_param = 0;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 + -