uvc_driver.c

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

C
2,046
字号
	return buffer - start;}static int uvc_parse_streaming(struct uvc_device *dev,	struct usb_interface *intf){	struct uvc_streaming *streaming = NULL;	struct uvc_format *format;	struct uvc_frame *frame;	struct usb_host_interface *alts = &intf->altsetting[0];	unsigned char *_buffer, *buffer = alts->extra;	int _buflen, buflen = alts->extralen;	unsigned int nformats = 0, nframes = 0, nintervals = 0;	unsigned int size, i, n, p;	__u32 *interval;	__u16 psize;	int ret = -EINVAL;	if (intf->cur_altsetting->desc.bInterfaceSubClass		!= SC_VIDEOSTREAMING) {		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "			"video streaming interface\n", dev->udev->devnum,			intf->altsetting[0].desc.bInterfaceNumber);		return -EINVAL;	}	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "			"claimed\n", dev->udev->devnum,			intf->altsetting[0].desc.bInterfaceNumber);		return -EINVAL;	}	streaming = kzalloc(sizeof *streaming, GFP_KERNEL);	if (streaming == NULL) {		usb_driver_release_interface(&uvc_driver.driver, intf);		return -EINVAL;	}	mutex_init(&streaming->mutex);	streaming->intf = usb_get_intf(intf);	streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;	/* The Pico iMage webcam has its class-specific interface descriptors	 * after the endpoint descriptors.	 */	if (buflen == 0) {		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {			struct usb_host_endpoint *ep = &alts->endpoint[i];			if (ep->extralen == 0)				continue;			if (ep->extralen > 2 &&			    ep->extra[1] == USB_DT_CS_INTERFACE) {				uvc_trace(UVC_TRACE_DESCR, "trying extra data "					"from endpoint %u.\n", i);				buffer = alts->endpoint[i].extra;				buflen = alts->endpoint[i].extralen;				break;			}		}	}	/* Skip the standard interface descriptors. */	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {		buflen -= buffer[0];		buffer += buffer[0];	}	if (buflen <= 2) {		uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "			"interface descriptors found.\n");		goto error;	}	/* Parse the header descriptor. */	if (buffer[2] == VS_OUTPUT_HEADER) {		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "			"%d OUTPUT HEADER descriptor is not supported.\n",			dev->udev->devnum, alts->desc.bInterfaceNumber);		goto error;	} else if (buffer[2] == VS_INPUT_HEADER) {		p = buflen >= 5 ? buffer[3] : 0;		n = buflen >= 12 ? buffer[12] : 0;		if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "				"interface %d INPUT HEADER descriptor is "				"invalid.\n", dev->udev->devnum,				alts->desc.bInterfaceNumber);			goto error;		}		streaming->header.bNumFormats = p;		streaming->header.bEndpointAddress = buffer[6];		streaming->header.bmInfo = buffer[7];		streaming->header.bTerminalLink = buffer[8];		streaming->header.bStillCaptureMethod = buffer[9];		streaming->header.bTriggerSupport = buffer[10];		streaming->header.bTriggerUsage = buffer[11];		streaming->header.bControlSize = n;		streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);		if (streaming->header.bmaControls == NULL) {			ret = -ENOMEM;			goto error;		}		memcpy(streaming->header.bmaControls, &buffer[13], p*n);	} else {		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "			"%d HEADER descriptor not found.\n", dev->udev->devnum,			alts->desc.bInterfaceNumber);		goto error;	}	buflen -= buffer[0];	buffer += buffer[0];	_buffer = buffer;	_buflen = buflen;	/* Count the format and frame descriptors. */	while (_buflen > 2) {		switch (_buffer[2]) {		case VS_FORMAT_UNCOMPRESSED:		case VS_FORMAT_MJPEG:		case VS_FORMAT_FRAME_BASED:			nformats++;			break;		case VS_FORMAT_DV:			/* DV format has no frame descriptor. We will create a			 * dummy frame descriptor with a dummy frame interval.			 */			nformats++;			nframes++;			nintervals++;			break;		case VS_FORMAT_MPEG2TS:		case VS_FORMAT_STREAM_BASED:			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "				"interface %d FORMAT %u is not supported.\n",				dev->udev->devnum,				alts->desc.bInterfaceNumber, _buffer[2]);			break;		case VS_FRAME_UNCOMPRESSED:		case VS_FRAME_MJPEG:			nframes++;			if (_buflen > 25)				nintervals += _buffer[25] ? _buffer[25] : 3;			break;		case VS_FRAME_FRAME_BASED:			nframes++;			if (_buflen > 21)				nintervals += _buffer[21] ? _buffer[21] : 3;			break;		}		_buflen -= _buffer[0];		_buffer += _buffer[0];	}	if (nformats == 0) {		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "			"%d has no supported formats defined.\n",			dev->udev->devnum, alts->desc.bInterfaceNumber);		goto error;	}	size = nformats * sizeof *format + nframes * sizeof *frame	     + nintervals * sizeof *interval;	format = kzalloc(size, GFP_KERNEL);	if (format == NULL) {		ret = -ENOMEM;		goto error;	}	frame = (struct uvc_frame *)&format[nformats];	interval = (__u32 *)&frame[nframes];	streaming->format = format;	streaming->nformats = nformats;	/* Parse the format descriptors. */	while (buflen > 2) {		switch (buffer[2]) {		case VS_FORMAT_UNCOMPRESSED:		case VS_FORMAT_MJPEG:		case VS_FORMAT_DV:		case VS_FORMAT_FRAME_BASED:			format->frame = frame;			ret = uvc_parse_format(dev, streaming, format,				&interval, buffer, buflen);			if (ret < 0)				goto error;			frame += format->nframes;			format++;			buflen -= ret;			buffer += ret;			continue;		default:			break;		}		buflen -= buffer[0];		buffer += buffer[0];	}	/* Parse the alternate settings to find the maximum bandwidth. */	for (i = 0; i < intf->num_altsetting; ++i) {		struct usb_host_endpoint *ep;		alts = &intf->altsetting[i];		ep = uvc_find_endpoint(alts,				streaming->header.bEndpointAddress);		if (ep == NULL)			continue;		psize = le16_to_cpu(ep->desc.wMaxPacketSize);		psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));		if (psize > streaming->maxpsize)			streaming->maxpsize = psize;	}	list_add_tail(&streaming->list, &dev->streaming);	return 0;error:	usb_driver_release_interface(&uvc_driver.driver, intf);	usb_put_intf(intf);	kfree(streaming->format);	kfree(streaming->header.bmaControls);	kfree(streaming);	return ret;}/* Parse vendor-specific extensions. */static int uvc_parse_vendor_control(struct uvc_device *dev,	const unsigned char *buffer, int buflen){	struct usb_device *udev = dev->udev;	struct usb_host_interface *alts = dev->intf->cur_altsetting;	struct uvc_entity *unit;	unsigned int n, p;	int handled = 0;	switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {	case 0x046d:		/* Logitech */		if (buffer[1] != 0x41 || buffer[2] != 0x01)			break;		/* Logitech implements several vendor specific functions		 * through vendor specific extension units (LXU).		 *		 * The LXU descriptors are similar to XU descriptors		 * (see "USB Device Video Class for Video Devices", section		 * 3.7.2.6 "Extension Unit Descriptor") with the following		 * differences:		 *		 * ----------------------------------------------------------		 * 0		bLength		1	 Number		 *	Size of this descriptor, in bytes: 24+p+n*2		 * ----------------------------------------------------------		 * 23+p+n	bmControlsType	N	Bitmap		 * 	Individual bits in the set are defined:		 * 	0: Absolute		 * 	1: Relative		 *		 * 	This bitset is mapped exactly the same as bmControls.		 * ----------------------------------------------------------		 * 23+p+n*2	bReserved	1	Boolean		 * ----------------------------------------------------------		 * 24+p+n*2	iExtension	1	Index		 *	Index of a string descriptor that describes this		 *	extension unit.		 * ----------------------------------------------------------		 */		p = buflen >= 22 ? buffer[21] : 0;		n = buflen >= 25 + p ? buffer[22+p] : 0;		if (buflen < 25 + p + 2*n) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d EXTENSION_UNIT error\n",				udev->devnum, alts->desc.bInterfaceNumber);			break;		}		unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL);		if (unit == NULL)			return -ENOMEM;		unit->id = buffer[3];		unit->type = VC_EXTENSION_UNIT;		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;		unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit					       + p + n;		memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);		if (buffer[24+p+2*n] != 0)			usb_string(udev, buffer[24+p+2*n], unit->name,				   sizeof unit->name);		else			sprintf(unit->name, "Extension %u", buffer[3]);		list_add_tail(&unit->list, &dev->entities);		handled = 1;		break;	}	return handled;}static int uvc_parse_standard_control(struct uvc_device *dev,	const unsigned char *buffer, int buflen){	struct usb_device *udev = dev->udev;	struct uvc_entity *unit, *term;	struct usb_interface *intf;	struct usb_host_interface *alts = dev->intf->cur_altsetting;	unsigned int i, n, p, len;	__u16 type;	switch (buffer[2]) {	case VC_HEADER:		n = buflen >= 12 ? buffer[11] : 0;		if (buflen < 12 || buflen < 12 + n) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d HEADER error\n", udev->devnum,				alts->desc.bInterfaceNumber);			return -EINVAL;		}		dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);		dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);		/* Parse all USB Video Streaming interfaces. */		for (i = 0; i < n; ++i) {			intf = usb_ifnum_to_if(udev, buffer[12+i]);			if (intf == NULL) {				uvc_trace(UVC_TRACE_DESCR, "device %d "					"interface %d doesn't exists\n",					udev->devnum, i);				continue;			}			uvc_parse_streaming(dev, intf);		}		break;	case VC_INPUT_TERMINAL:		if (buflen < 8) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d INPUT_TERMINAL error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		/* Make sure the terminal type MSB is not null, otherwise it		 * could be confused with a unit.		 */		type = le16_to_cpup((__le16 *)&buffer[4]);		if ((type & 0xff00) == 0) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d INPUT_TERMINAL %d has invalid "				"type 0x%04x, skipping\n", udev->devnum,				alts->desc.bInterfaceNumber,				buffer[3], type);			return 0;		}		n = 0;		p = 0;		len = 8;		if (type == ITT_CAMERA) {			n = buflen >= 15 ? buffer[14] : 0;			len = 15;		} else if (type == ITT_MEDIA_TRANSPORT_INPUT) {			n = buflen >= 9 ? buffer[8] : 0;			p = buflen >= 10 + n ? buffer[9+n] : 0;			len = 10;		}		if (buflen < len + n + p) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d INPUT_TERMINAL error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		term = kzalloc(sizeof *term + n + p, GFP_KERNEL);		if (term == NULL)			return -ENOMEM;		term->id = buffer[3];		term->type = type | UVC_TERM_INPUT;		if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) {			term->camera.bControlSize = n;			term->camera.bmControls = (__u8 *)term + sizeof *term;			term->camera.wObjectiveFocalLengthMin =				le16_to_cpup((__le16 *)&buffer[8]);			term->camera.wObjectiveFocalLengthMax =				le16_to_cpup((__le16 *)&buffer[10]);			term->camera.wOcularFocalLength =				le16_to_cpup((__le16 *)&buffer[12]);			memcpy(term->camera.bmControls, &buffer[15], n);		} else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {			term->media.bControlSize = n;			term->media.bmControls = (__u8 *)term + sizeof *term;			term->media.bTransportModeSize = p;			term->media.bmTransportModes = (__u8 *)term						     + sizeof *term + n;			memcpy(term->media.bmControls, &buffer[9], n);			memcpy(term->media.bmTransportModes, &buffer[10+n], p);		}		if (buffer[7] != 0)			usb_string(udev, buffer[7], term->name,				   sizeof term->name);		else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA)			sprintf(term->name, "Camera %u", buffer[3]);		else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT)			sprintf(term->name, "Media %u", buffer[3]);		else			sprintf(term->name, "Input %u", buffer[3]);		list_add_tail(&term->list, &dev->entities);		break;	case VC_OUTPUT_TERMINAL:		if (buflen < 9) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d OUTPUT_TERMINAL error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		/* Make sure the terminal type MSB is not null, otherwise it		 * could be confused with a unit.		 */		type = le16_to_cpup((__le16 *)&buffer[4]);		if ((type & 0xff00) == 0) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d OUTPUT_TERMINAL %d has invalid "				"type 0x%04x, skipping\n", udev->devnum,				alts->desc.bInterfaceNumber, buffer[3], type);			return 0;		}		term = kzalloc(sizeof *term, GFP_KERNEL);		if (term == NULL)			return -ENOMEM;		term->id = buffer[3];		term->type = type | UVC_TERM_OUTPUT;		term->output.bSourceID = buffer[7];		if (buffer[8] != 0)			usb_string(udev, buffer[8], term->name,				   sizeof term->name);		else			sprintf(term->name, "Output %u", buffer[3]);		list_add_tail(&term->list, &dev->entities);		break;	case VC_SELECTOR_UNIT:		p = buflen >= 5 ? buffer[4] : 0;		if (buflen < 5 || buflen < 6 + p) {			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "				"interface %d SELECTOR_UNIT error\n",				udev->devnum, alts->desc.bInterfaceNumber);			return -EINVAL;		}		unit = kzalloc(sizeof *unit + p, GFP_KERNEL);		if (unit == NULL)			return -ENOMEM;		unit->id = buffer[3];		unit->type = buffer[2];		unit->selector.bNrInPins = buffer[4];		unit->selector.baSourceID = (__u8 *)unit + sizeof *unit;		memcpy(unit->selector.baSourceID, &buffer[5], p);		if (buffer[5+p] != 0)			usb_string(udev, buffer[5+p], unit->name,				   sizeof unit->name);		else			sprintf(unit->name, "Selector %u", buffer[3]);		list_add_tail(&unit->list, &dev->entities);		break;

⌨️ 快捷键说明

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