uvc_driver.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 2,046 行 · 第 1/4 页

C
2,046
字号
	case VC_PROCESSING_UNIT:		n = buflen >= 8 ? buffer[7] : 0;		p = dev->uvc_version >= 0x0110 ? 10 : 9;		if (buflen < p + n) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d PROCESSING_UNIT error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		unit = kzalloc(sizeof *unit + n, GFP_KERNEL);		if (unit == NULL)			return -ENOMEM;		unit->id = buffer[3];		unit->type = buffer[2];		unit->processing.bSourceID = buffer[4];		unit->processing.wMaxMultiplier =			le16_to_cpup((__le16 *)&buffer[5]);		unit->processing.bControlSize = buffer[7];		unit->processing.bmControls = (__u8 *)unit + sizeof *unit;		memcpy(unit->processing.bmControls, &buffer[8], n);		if (dev->uvc_version >= 0x0110)			unit->processing.bmVideoStandards = buffer[9+n];		if (buffer[8+n] != 0)			usb_string(udev, buffer[8+n], unit->name,				   sizeof unit->name);		else			sprintf(unit->name, "Processing %u", buffer[3]);		list_add_tail(&unit->list, &dev->entities);		break;	case VC_EXTENSION_UNIT:		p = buflen >= 22 ? buffer[21] : 0;		n = buflen >= 24 + p ? buffer[22+p] : 0;		if (buflen < 24 + p + n) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d EXTENSION_UNIT error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL);		if (unit == NULL)			return -ENOMEM;		unit->id = buffer[3];		unit->type = buffer[2];		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);		unit->extension.bNumControls = buffer[20];		unit->extension.bNrInPins =			le16_to_cpup((__le16 *)&buffer[21]);		unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;		memcpy(unit->extension.baSourceID, &buffer[22], p);		unit->extension.bControlSize = buffer[22+p];		unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;		memcpy(unit->extension.bmControls, &buffer[23+p], n);		if (buffer[23+p+n] != 0)			usb_string(udev, buffer[23+p+n], unit->name,				   sizeof unit->name);		else			sprintf(unit->name, "Extension %u", buffer[3]);		list_add_tail(&unit->list, &dev->entities);		break;	default:		uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "			"descriptor (%u)\n", buffer[2]);		break;	}	return 0;}static int uvc_parse_control(struct uvc_device *dev){	struct usb_host_interface *alts = dev->intf->cur_altsetting;	unsigned char *buffer = alts->extra;	int buflen = alts->extralen;	int ret;	/* Parse the default alternate setting only, as the UVC specification	 * defines a single alternate setting, the default alternate setting	 * zero.	 */	while (buflen > 2) {		if (uvc_parse_vendor_control(dev, buffer, buflen) ||		    buffer[1] != USB_DT_CS_INTERFACE)			goto next_descriptor;		if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)			return ret;next_descriptor:		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 the following units: * * - a USB Streaming Output Terminal * - zero or one Processing Unit * - zero, one or mode single-input Selector Units * - zero or one multiple-input Selector Units, provided all inputs are *   connected to input terminals * - zero, one or mode single-input Extension Units * - one Camera Input Terminal, or one or more External terminals. * * A side forward scan is made on each detected entity to check for additional * extension units. */static int uvc_scan_chain_entity(struct uvc_video_device *video,	struct uvc_entity *entity){	switch (UVC_ENTITY_TYPE(entity)) {	case VC_EXTENSION_UNIT:		if (uvc_trace_param & UVC_TRACE_PROBE)			printk(" <- XU %d", entity->id);		if (entity->extension.bNrInPins != 1) {			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "				"than 1 input pin.\n", entity->id);			return -1;		}		list_add_tail(&entity->chain, &video->extensions);		break;	case VC_PROCESSING_UNIT:		if (uvc_trace_param & UVC_TRACE_PROBE)			printk(" <- PU %d", entity->id);		if (video->processing != NULL) {			uvc_trace(UVC_TRACE_DESCR, "Found multiple "				"Processing Units in chain.\n");			return -1;		}		video->processing = entity;		break;	case VC_SELECTOR_UNIT:		if (uvc_trace_param & UVC_TRACE_PROBE)			printk(" <- SU %d", entity->id);		/* Single-input selector units are ignored. */		if (entity->selector.bNrInPins == 1)			break;		if (video->selector != NULL) {			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "				"Units in chain.\n");			return -1;		}		video->selector = entity;		break;	case ITT_VENDOR_SPECIFIC:	case ITT_CAMERA:	case ITT_MEDIA_TRANSPORT_INPUT:		if (uvc_trace_param & UVC_TRACE_PROBE)			printk(" <- IT %d\n", entity->id);		list_add_tail(&entity->chain, &video->iterms);		break;	default:		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));		return -1;	}	return 0;}static int uvc_scan_chain_forward(struct uvc_video_device *video,	struct uvc_entity *entity, struct uvc_entity *prev){	struct uvc_entity *forward;	int found;	/* Forward scan */	forward = NULL;	found = 0;	while (1) {		forward = uvc_entity_by_reference(video->dev, entity->id,			forward);		if (forward == NULL)			break;		if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT ||		    forward == prev)			continue;		if (forward->extension.bNrInPins != 1) {			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has"				"more than 1 input pin.\n", entity->id);			return -1;		}		list_add_tail(&forward->chain, &video->extensions);		if (uvc_trace_param & UVC_TRACE_PROBE) {			if (!found)				printk(" (-> XU");			printk(" %d", forward->id);			found = 1;		}	}	if (found)		printk(")");	return 0;}static int uvc_scan_chain_backward(struct uvc_video_device *video,	struct uvc_entity *entity){	struct uvc_entity *term;	int id = -1, i;	switch (UVC_ENTITY_TYPE(entity)) {	case VC_EXTENSION_UNIT:		id = entity->extension.baSourceID[0];		break;	case VC_PROCESSING_UNIT:		id = entity->processing.bSourceID;		break;	case VC_SELECTOR_UNIT:		/* Single-input selector units are ignored. */		if (entity->selector.bNrInPins == 1) {			id = entity->selector.baSourceID[0];			break;		}		if (uvc_trace_param & UVC_TRACE_PROBE)			printk(" <- IT");		video->selector = entity;		for (i = 0; i < entity->selector.bNrInPins; ++i) {			id = entity->selector.baSourceID[i];			term = uvc_entity_by_id(video->dev, id);			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {				uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "					"input %d isn't connected to an "					"input terminal\n", entity->id, i);				return -1;			}			if (uvc_trace_param & UVC_TRACE_PROBE)				printk(" %d", term->id);			list_add_tail(&term->chain, &video->iterms);			uvc_scan_chain_forward(video, term, entity);		}		if (uvc_trace_param & UVC_TRACE_PROBE)			printk("\n");		id = 0;		break;	}	return id;}static int uvc_scan_chain(struct uvc_video_device *video){	struct uvc_entity *entity, *prev;	int id;	entity = video->oterm;	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);	id = entity->output.bSourceID;	while (id != 0) {		prev = entity;		entity = uvc_entity_by_id(video->dev, id);		if (entity == NULL) {			uvc_trace(UVC_TRACE_DESCR, "Found reference to "				"unknown entity %d.\n", id);			return -1;		}		/* Process entity */		if (uvc_scan_chain_entity(video, entity) < 0)			return -1;		/* Forward scan */		if (uvc_scan_chain_forward(video, entity, prev) < 0)			return -1;		/* Stop when a terminal is found. */		if (!UVC_ENTITY_IS_UNIT(entity))			break;		/* Backward scan */		id = uvc_scan_chain_backward(video, entity);		if (id < 0)			return id;	}	/* 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 uvc_streaming *streaming;		if (UVC_ENTITY_TYPE(term) != TT_STREAMING)			continue;		memset(&dev->video, 0, sizeof dev->video);		mutex_init(&dev->video.ctrl_mutex);		INIT_LIST_HEAD(&dev->video.iterms);		INIT_LIST_HEAD(&dev->video.extensions);		dev->video.oterm = term;		dev->video.dev = dev;		if (uvc_scan_chain(&dev->video) < 0)			continue;		list_for_each_entry(streaming, &dev->streaming, list) {			if (streaming->header.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;	}	if (uvc_trace_param & UVC_TRACE_PROBE) {		uvc_printk(KERN_INFO, "Found a valid video chain (");		list_for_each_entry(term, &dev->video.iterms, chain) {			printk("%d", term->id);			if (term->chain.next != &dev->video.iterms)				printk(",");		}		printk(" -> %d).\n", 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;	/* 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->parent = &dev->intf->dev;	vdev->minor = -1;	vdev->fops = &uvc_fops;	vdev->release = video_device_release;	strncpy(vdev->name, dev->name, sizeof vdev->name);	/* 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_driver_release_interface(&uvc_driver.driver,			streaming->intf);		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)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?