📄 uvc_driver.c
字号:
/* * 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 + -