⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uvc_driver.c

📁 linux camera下的uvc driver驱动源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *      uvcvideo.c  --  USB Video Class driver * *      Copyright (C) 2005-2006 *          Laurent Pinchart (laurent.pinchart@skynet.be) * *      This program is free software; you can redistribute it and/or modify *      it under the terms of the GNU General Public License as published by *      the Free Software Foundation; either version 2 of the License, or *      (at your option) any later version. * *//* * WARNING: This driver is definitely *NOT* complete. It will (hopefully) * support UVC devices with a camera sensors, a processing unit and several * optional extension units. Single-input selector units are ignored. * Everything else is unsupported. * * The driver doesn't support the deprecated v4l1 interface. It implements the * mmap capture method only, and doesn't do any image format conversion in * software. If your user-space application doesn't support YUYV or MJPEG, fix * it :-). Please note that the MJPEG data have been stripped from their * Huffman tables (DHT marker), you will need to add it back if your JPEG * codec can't handle MJPEG data. * * Although the driver compiles on 2.6.12, you should use a 2.6.15 or newer * kernel because of USB issues. */#include <linux/kernel.h>#include <linux/version.h>#include <linux/list.h>#include <linux/module.h>#include <linux/usb.h>#include <linux/videodev.h>#include <linux/vmalloc.h>#include <linux/wait.h>#include <asm/atomic.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)#include <media/v4l2-common.h>#endif#include "uvcvideo.h"#include "version.h"#define DRIVER_AUTHOR		"Laurent Pinchart <laurent.pinchart@skynet.be>"#define DRIVER_DESC		"USB Video Class driver"#ifndef DRIVER_VERSION#define DRIVER_VERSION		"v0.1.0"#endifstatic unsigned int uvc_quirks_param = 0;unsigned int uvc_trace_param = 0;/* ------------------------------------------------------------------------ * Control, formats, ... */static struct uvc_format_desc uvc_fmts[] = {	{		.name		= "YUV 4:2:2 (YUYV)",		.guid		= UVC_GUID_FORMAT_YUY2,		.fcc		= V4L2_PIX_FMT_YUYV,	},	{		.name		= "YUV 4:2:0 (NV12)",		.guid		= UVC_GUID_FORMAT_NV12,		.fcc		= V4L2_PIX_FMT_NV12,	},	{		.name		= "MJPEG",		.guid		= UVC_GUID_FORMAT_MJPEG,		.fcc		= V4L2_PIX_FMT_MJPEG,	},	{		.name		= "YVU 4:2:0 (YV12)",		.guid		= UVC_GUID_FORMAT_YV12,		.fcc		= V4L2_PIX_FMT_YVU420,	},	{		.name		= "YUV 4:2:0 (I420)",		.guid		= UVC_GUID_FORMAT_I420,		.fcc		= V4L2_PIX_FMT_YUV420,	},	{		.name		= "YUV 4:2:2 (UYVY)",		.guid		= UVC_GUID_FORMAT_UYVY,		.fcc		= V4L2_PIX_FMT_UYVY,	},	{		.name		= "Greyscale",		.guid		= UVC_GUID_FORMAT_Y800,		.fcc		= V4L2_PIX_FMT_GREY,	},	{		.name		= "RGB Bayer",		.guid		= UVC_GUID_FORMAT_BY8,		.fcc		= V4L2_PIX_FMT_SBGGR8,	},};#if 0static void uvc_print_streaming_control(struct uvc_streaming_control *ctrl){	printk(KERN_DEBUG "bmHint:                      0x%04x\n", ctrl->bmHint);	printk(KERN_DEBUG "bFormatIndex:                   %3u\n", ctrl->bFormatIndex);	printk(KERN_DEBUG "bFrameIndex:                    %3u\n", ctrl->bFrameIndex);	printk(KERN_DEBUG "dwFrameInterval:          %9u\n", ctrl->dwFrameInterval);	printk(KERN_DEBUG "wKeyFrameRate:                %5u\n", ctrl->wKeyFrameRate);	printk(KERN_DEBUG "wPFrameRate:                  %5u\n", ctrl->wPFrameRate);	printk(KERN_DEBUG "wCompQuality:                 %5u\n", ctrl->wCompQuality);	printk(KERN_DEBUG "wCompWindowSize:              %5u\n", ctrl->wCompWindowSize);	printk(KERN_DEBUG "wDelay:                       %5u\n", ctrl->wDelay);	printk(KERN_DEBUG "dwMaxVideoFrameSize:      %9u\n", ctrl->dwMaxVideoFrameSize);	printk(KERN_DEBUG "dwMaxPayloadTransferSize: %9u\n", ctrl->dwMaxPayloadTransferSize);	printk(KERN_DEBUG "dwClockFrequency:         %9u\n", ctrl->dwClockFrequency);	printk(KERN_DEBUG "bmFramingInfo:                 0x%02x\n", ctrl->bmFramingInfo);	printk(KERN_DEBUG "bPreferedVersion:               %3u\n", ctrl->bPreferedVersion);	printk(KERN_DEBUG "bMinVersion:                    %3u\n", ctrl->bMinVersion);	printk(KERN_DEBUG "bMaxVersion:                    %3u\n", ctrl->bMaxVersion);}#endif/* ------------------------------------------------------------------------ * Utility functions */struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,		__u8 epaddr){	struct usb_host_endpoint *ep;	unsigned int i;	for (i = 0; i < alts->desc.bNumEndpoints; ++i) {		ep = &alts->endpoint[i];		if (ep->desc.bEndpointAddress == epaddr)			return ep;	}	return NULL;}static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]){	unsigned int len = ARRAY_SIZE(uvc_fmts);	unsigned int i;	for (i = 0; i < len; ++i) {		if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)			return &uvc_fmts[i];	}	return NULL;}static __u32 uvc_colorspace(const __u8 primaries){	static const __u8 colorprimaries[] = {		0,		V4L2_COLORSPACE_SRGB,		V4L2_COLORSPACE_470_SYSTEM_M,		V4L2_COLORSPACE_470_SYSTEM_BG,		V4L2_COLORSPACE_SMPTE170M,		V4L2_COLORSPACE_SMPTE240M,	};	if (primaries < ARRAY_SIZE(colorprimaries))		return colorprimaries[primaries];	return 0;}/* Simplify a fraction using a simple continued fraction decomposition. The * idea here is to convert fractions such as 333333/10000000 to 1/30 using * 32 bit arithmetic only. The algorithm is not perfect and relies upon two * arbitrary parameters to remove non-significative terms from the simple * continued fraction decomposition. Using 8 and 333 for n_terms and threshold * respectively seems to give nice results. */void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,		unsigned int n_terms, unsigned int threshold){	uint32_t *an;	uint32_t x, y, r;	unsigned int i, n;	an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);	if (an == NULL)		return;	/* Convert the fraction to a simple continued fraction. See	 * http://mathforum.org/dr.math/faq/faq.fractions.html	 * Stop if the current term is bigger than or equal to the given	 * threshold.	 */	x = *numerator;	y = *denominator;	for (n = 0; n < n_terms && y != 0; ++n) {		an[n] = x / y;		if (an[n] >= threshold) {			if (n < 2)				n++;			break;		}		r = x - an[n] * y;		x = y;		y = r;	}	/* Expand the simple continued fraction back to an integer fraction. */	x = 0;	y = 1;	for (i = n; i > 0; --i) {		r = y;		y = an[i-1] * y + x;		x = r;	}	*numerator = y;	*denominator = x;	kfree(an);}/* Convert a fraction to a frame interval in 100ns multiples. The idea here is * to compute numerator / denominator * 10000000 using 32 bit fixed point * arithmetic only. */uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator){	uint32_t multiplier;	/* Saturate the result if the operation would overflow. */	if (denominator == 0 ||	    numerator/denominator >= ((uint32_t)-1)/10000000)		return (uint32_t)-1;	/* Divide both the denominator and the multiplier by two until	 * numerator * multiplier doesn't overflow. If anyone knows a better	 * algorithm please let me know.	 */	multiplier = 10000000;	while (numerator > ((uint32_t)-1)/multiplier) {		multiplier /= 2;		denominator /= 2;	}	return denominator ? numerator * multiplier / denominator : 0;}/* ------------------------------------------------------------------------ * Terminal and unit management */static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id){	struct uvc_entity *entity;	list_for_each_entry(entity, &dev->entities, list) {		if (entity->id == id)			return entity;	}	return NULL;}static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,	int id, struct uvc_entity *entity){	unsigned int i;	if (entity == NULL)		entity = list_entry(&dev->entities, struct uvc_entity, list);	list_for_each_entry_continue(entity, &dev->entities, list) {		switch (UVC_ENTITY_TYPE(entity)) {		case TT_STREAMING:			if (entity->output.bSourceID == id)				return entity;			break;		case VC_PROCESSING_UNIT:			if (entity->processing.bSourceID == id)				return entity;			break;		case VC_SELECTOR_UNIT:			for (i = 0; i < entity->selector.bNrInPins; ++i)				if (entity->selector.baSourceID[i] == id)					return entity;			break;		case VC_EXTENSION_UNIT:			for (i = 0; i < entity->extension.bNrInPins; ++i)				if (entity->extension.baSourceID[i] == id)					return entity;			break;		}	}	return NULL;}/* ------------------------------------------------------------------------ * Descriptors handling */static int uvc_parse_format(struct uvc_device *dev,	struct uvc_streaming *streaming, struct uvc_format *format,	__u32 **intervals, unsigned char *buffer, int buflen){	struct usb_interface *intf = streaming->intf;	struct usb_host_interface *alts = intf->cur_altsetting;	struct uvc_format_desc *fmtdesc;	struct uvc_frame *frame;	const unsigned char *start = buffer;	unsigned int interval;	unsigned int i, n;	__u8 ftype;	format->type = buffer[2];	format->index = buffer[3];	switch (buffer[2]) {	case VS_FORMAT_UNCOMPRESSED:	case VS_FORMAT_FRAME_BASED:		if (buflen < 27) {			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"			       "interface %d FORMAT error\n",			       dev->udev->devnum,			       alts->desc.bInterfaceNumber);			return -EINVAL;		}		/* Find the format descriptor from its GUID. */		fmtdesc = uvc_format_by_guid(&buffer[5]);		if (fmtdesc != NULL) {			strncpy(format->name, fmtdesc->name, sizeof format->name);			format->fcc = fmtdesc->fcc;		} else {			uvc_printk(KERN_INFO, "Unknown video format "				UVC_GUID_FORMAT "\n",				UVC_GUID_ARGS(&buffer[5]));			snprintf(format->name, sizeof format->name,				UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5]));			format->fcc = 0;		}		format->bpp = buffer[21];		if (buffer[2] == VS_FORMAT_UNCOMPRESSED) {			ftype = VS_FRAME_UNCOMPRESSED;		} else {			ftype = VS_FRAME_FRAME_BASED;			if (buffer[27])				format->flags = UVC_FMT_FLAG_COMPRESSED;		}		break;	case VS_FORMAT_MJPEG:		if (buflen < 11) {			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"			       "interface %d FORMAT error\n",			       dev->udev->devnum,			       alts->desc.bInterfaceNumber);			return -EINVAL;		}		strncpy(format->name, "MJPEG", sizeof format->name);		format->fcc = V4L2_PIX_FMT_MJPEG;		format->flags = UVC_FMT_FLAG_COMPRESSED;		format->bpp = 0;		ftype = VS_FRAME_MJPEG;		break;	case VS_FORMAT_DV:		if (buflen < 9) {			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"			       "interface %d FORMAT error\n",			       dev->udev->devnum,			       alts->desc.bInterfaceNumber);			return -EINVAL;		}		switch (buffer[8] & 0x7f) {		case 0:			strncpy(format->name, "SD-DV", sizeof format->name);			break;		case 1:			strncpy(format->name, "SDL-DV", sizeof format->name);			break;		case 2:			strncpy(format->name, "HD-DV", sizeof format->name);			break;		default:			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"			       "interface %d: unknown DV format %u\n",			       dev->udev->devnum,			       alts->desc.bInterfaceNumber, buffer[8]);			return -EINVAL;		}		strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",			sizeof format->name);		format->fcc = V4L2_PIX_FMT_DV;		format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;		format->bpp = 0;		ftype = 0;		/* Create a dummy frame descriptor. */		frame = &format->frame[0];		memset(&format->frame[0], 0, sizeof format->frame[0]);		frame->bFrameIntervalType = 1;		frame->dwDefaultFrameInterval = 1;		frame->dwFrameInterval = *intervals;		*(*intervals)++ = 1;		format->nframes = 1;		break;	case VS_FORMAT_MPEG2TS:	case VS_FORMAT_STREAM_BASED:		/* Not supported yet. */	default:		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"		       "interface %d unsupported format %u\n",		       dev->udev->devnum, alts->desc.bInterfaceNumber,		       buffer[2]);		return -EINVAL;	}	uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);	buflen -= buffer[0];	buffer += buffer[0];	/* Parse the frame descriptors. Only uncompressed, MJPEG and frame	 * based formats have frame descriptors.	 */	while (buflen > 2 && buffer[2] == ftype) {		frame = &format->frame[format->nframes];		if (ftype != VS_FRAME_FRAME_BASED)			n = buflen > 25 ? buffer[25] : 0;		else			n = buflen > 21 ? buffer[21] : 0;		n = n ? n : 3;		if (buflen < 26 + 4*n) {			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"			       "interface %d FRAME error\n", dev->udev->devnum,			       alts->desc.bInterfaceNumber);			return -EINVAL;		}		frame->bFrameIndex = buffer[3];		frame->bmCapabilities = buffer[4];		frame->wWidth = le16_to_cpup((__le16*)&buffer[5]);		frame->wHeight = le16_to_cpup((__le16*)&buffer[7]);		frame->dwMinBitRate = le32_to_cpup((__le32*)&buffer[9]);		frame->dwMaxBitRate = le32_to_cpup((__le32*)&buffer[13]);		if (ftype != VS_FRAME_FRAME_BASED) {			frame->dwMaxVideoFrameBufferSize =				le32_to_cpup((__le32*)&buffer[17]);			frame->dwDefaultFrameInterval =				le32_to_cpup((__le32*)&buffer[21]);			frame->bFrameIntervalType = buffer[25];		} else {			frame->dwMaxVideoFrameBufferSize = 0;			frame->dwDefaultFrameInterval =				le32_to_cpup((__le32*)&buffer[17]);			frame->bFrameIntervalType = buffer[21];

⌨️ 快捷键说明

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