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

📄 uvc_v4l2.c

📁 linux camera下的uvc driver驱动源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *      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. * */#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/mm.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"/* ------------------------------------------------------------------------ * V4L2 interface *//* * Mapping V4L2 controls to UVC controls can be straighforward if done well. * Most of the UVC controls exist in V4L2, and can be mapped directly. Some * must be grouped (for instance the Red Balance, Blue Balance and Do White * Balance V4L2 controls use the White Balance Component UVC control) or * otherwise translated. The approach we take here is to use a translation * table for the controls which can be mapped directly, and handle the others * manually. */static int uvc_v4l2_query_menu(struct uvc_video_device *video, struct v4l2_querymenu *query_menu){	struct uvc_menu_info *menu_info;	struct uvc_control_mapping *mapping;	struct uvc_control *ctrl;	ctrl = uvc_find_control(video, query_menu->id, &mapping);	if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)		return -EINVAL;	if (query_menu->index >= mapping->menu_count)		return -EINVAL;	menu_info = &mapping->menu_info[query_menu->index];	strncpy(query_menu->name, menu_info->name, 32);	return 0;}/* * Find the frame interval closest to the requested frame interval for the * given frame format and size. This should be done by the device as part of * the Video Probe and Commit negotiation, but some hardware don't implement * that feature. */static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval){	unsigned int i;	if (frame->bFrameIntervalType) {		__u32 best = -1, dist;		for (i = 0; i < frame->bFrameIntervalType; ++i) {			dist = interval > frame->dwFrameInterval[i]			     ? interval - frame->dwFrameInterval[i]			     : frame->dwFrameInterval[i] - interval;			if (dist > best)				break;			best = dist;		}		interval = frame->dwFrameInterval[i-1];	}	else {		const __u32 min = frame->dwFrameInterval[0];		const __u32 max = frame->dwFrameInterval[1];		const __u32 step = frame->dwFrameInterval[2];		interval = min + (interval - min + step/2) / step * step;		if (interval > max)			interval = max;	}	return interval;}static int uvc_v4l2_try_format(struct uvc_video_device *video, struct v4l2_format *fmt,		struct uvc_streaming_control *probe, struct uvc_format **uvc_format,		struct uvc_frame **uvc_frame){	struct uvc_format *format = NULL;	struct uvc_frame *frame = NULL;	__u16 rw, rh;	unsigned int d, maxd;	unsigned int i;	__u32 interval;	int ret = 0;	__u8 *fcc;	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)		return -EINVAL;	fcc = (__u8*)&fmt->fmt.pix.pixelformat;	uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",			fmt->fmt.pix.pixelformat,			fcc[0], fcc[1], fcc[2], fcc[3],			fmt->fmt.pix.width, fmt->fmt.pix.height);	/* Check if the hardware supports the requested format. */	for (i = 0; i < video->streaming->nformats; ++i) {		format = &video->streaming->format[i];		if (format->fcc == fmt->fmt.pix.pixelformat)			break;	}	if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {		uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n",				fmt->fmt.pix.pixelformat);		return -EINVAL;	}	/* Find the closest image size. The distance between image sizes is	 * the size in pixels of the non-overlapping regions between the	 * requested size and the frame-specified size.	 */	rw = fmt->fmt.pix.width;	rh = fmt->fmt.pix.height;	maxd = (unsigned int)-1;	for (i = 0; i < format->nframes; ++i) {		__u16 w = format->frame[i].wWidth;		__u16 h = format->frame[i].wHeight;		d = min(w, rw) * min(h, rh);		d = w*h + rw*rh - 2*d;		if (d < maxd) {			maxd = d;			frame = &format->frame[i];		}		if (maxd == 0)			break;	}	if (frame == NULL) {		uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",				fmt->fmt.pix.width, fmt->fmt.pix.height);		return -EINVAL;	}	/* Use the default frame interval. */	interval = frame->dwDefaultFrameInterval;	uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "		"(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,		(100000000/interval)%10);	/* Set the format index, frame index and frame interval. */	memset(probe, 0, sizeof *probe);	probe->bmHint = 1;	/* dwFrameInterval */	probe->bFormatIndex = format->index;	probe->bFrameIndex = frame->bFrameIndex;	probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);	/* Some webcams stall the probe control set request when the	 * dwMaxVideoFrameSize field is set to zero. The UVC specification	 * clearly states that the field is read-only from the host, so this	 * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by	 * the webcam to work around the problem.	 *	 * The workaround could probably be enabled for all webcams, so the	 * quirk can be removed if needed. It's currently useful to detect	 * webcam bugs and fix them before they hit the market (providing	 * developers test their webcams with the Linux driver as well as with	 * the Windows driver).	 */	if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)		probe->dwMaxVideoFrameSize =			video->streaming->ctrl.dwMaxVideoFrameSize;	/* Probe the device */	if ((ret = uvc_probe_video(video, probe)) < 0)		goto done;	fmt->fmt.pix.width = frame->wWidth;	fmt->fmt.pix.height = frame->wHeight;	fmt->fmt.pix.field = V4L2_FIELD_NONE;	fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;	fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;	fmt->fmt.pix.colorspace = format->colorspace;	fmt->fmt.pix.priv = 0;	if (uvc_format != NULL)		*uvc_format = format;	if (uvc_frame != NULL)		*uvc_frame = frame;done:	return ret;}static int uvc_v4l2_get_format(struct uvc_video_device *video, struct v4l2_format *fmt){	struct uvc_format *format = video->streaming->cur_format;	struct uvc_frame *frame = video->streaming->cur_frame;	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)		return -EINVAL;	if (format == NULL || frame == NULL)		return -EINVAL;	fmt->fmt.pix.pixelformat = format->fcc;	fmt->fmt.pix.width = frame->wWidth;	fmt->fmt.pix.height = frame->wHeight;	fmt->fmt.pix.field = V4L2_FIELD_NONE;	fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;	fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize;	fmt->fmt.pix.colorspace = format->colorspace;	fmt->fmt.pix.priv = 0;	return 0;}static int uvc_v4l2_set_format(struct uvc_video_device *video, struct v4l2_format *fmt){	struct uvc_streaming_control probe;	struct uvc_format *format;	struct uvc_frame *frame;	int ret;	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)		return -EINVAL;	if (video->queue.streaming)		return -EBUSY;	ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);	if (ret < 0)		return ret;	if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)		return ret;	memcpy(&video->streaming->ctrl, &probe, sizeof probe);	video->streaming->cur_format = format;	video->streaming->cur_frame = frame;	return 0;}static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,		struct v4l2_streamparm *parm){	uint32_t numerator, denominator;	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)		return -EINVAL;	numerator = video->streaming->ctrl.dwFrameInterval;	denominator = 10000000;	uvc_simplify_fraction(&numerator, &denominator, 8, 333);	memset(parm, 0, sizeof *parm);	parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;	parm->parm.capture.capturemode = 0;	parm->parm.capture.timeperframe.numerator = numerator;	parm->parm.capture.timeperframe.denominator = denominator;	parm->parm.capture.extendedmode = 0;	parm->parm.capture.readbuffers = 0;	return 0;}static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,		struct v4l2_streamparm *parm){	struct uvc_frame *frame = video->streaming->cur_frame;	struct uvc_streaming_control probe;	uint32_t interval;	int ret;	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)		return -EINVAL;	if (video->queue.streaming)		return -EBUSY;	memcpy(&probe, &video->streaming->ctrl, sizeof probe);	interval = uvc_fraction_to_interval(			parm->parm.capture.timeperframe.numerator,			parm->parm.capture.timeperframe.denominator);	uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",			parm->parm.capture.timeperframe.numerator,			parm->parm.capture.timeperframe.denominator,			interval);	probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);	/* Probe the device with the new settings. */	if ((ret = uvc_probe_video(video, &probe)) < 0)		return ret;	/* Commit the new settings. */	if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)		return ret;	memcpy(&video->streaming->ctrl, &probe, sizeof probe);	/* Return the actual frame period. */	parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;	parm->parm.capture.timeperframe.denominator = 10000000;	uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,				&parm->parm.capture.timeperframe.denominator,				8, 333);	return 0;}/* ------------------------------------------------------------------------ * Privilege management *//* * Privilege management is the multiple-open implementation basis. The current * implementation is completely transparent for the end-user and doesn't * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. * Those ioctls enable finer control on the device (by making possible for a * user to request exclusive access to a device), but are not mature yet. * Switching to the V4L2 priority mechanism might be considered in the future * if this situation changes. * * Each open instance of a UVC device can either be in a privileged or * unprivileged state. Only a single instance can be in a privileged state at * a given time. Trying to perform an operation which requires privileges will * automatically acquire the required privileges if possible, or return -EBUSY * otherwise. Privileges are dismissed when closing the instance. * * Operations which require privileges are: * * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT * - VIDIOC_TRY_FMT * - VIDIOC_REQBUFS */static int uvc_acquire_privileges(struct uvc_fh *handle){	int ret = 0;	/* Always succeed if the handle is already privileged. */	if (handle->state == UVC_HANDLE_ACTIVE)		return 0;	/* Check if the device already has a privileged handle. */	mutex_lock(&uvc_driver.open_mutex);	if (atomic_inc_return(&handle->device->active) != 1) {		atomic_dec(&handle->device->active);		ret = -EBUSY;		goto done;	}	handle->state = UVC_HANDLE_ACTIVE;done:	mutex_unlock(&uvc_driver.open_mutex);	return ret;}static void uvc_dismiss_privileges(struct uvc_fh *handle){	if (handle->state == UVC_HANDLE_ACTIVE)		atomic_dec(&handle->device->active);	handle->state = UVC_HANDLE_PASSIVE;}static int uvc_has_privileges(struct uvc_fh *handle){	return handle->state == UVC_HANDLE_ACTIVE;}/* ------------------------------------------------------------------------ * V4L2 file operations */static int uvc_v4l2_open(struct inode *inode, struct file *file){	struct video_device *vdev;	struct uvc_video_device *video;	struct uvc_fh *handle;	int ret = 0;	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");	mutex_lock(&uvc_driver.open_mutex);	vdev = video_devdata(file);	video = video_get_drvdata(vdev);	if (video->dev->state & UVC_DEV_DISCONNECTED) {		ret = -ENODEV;		goto done;	}	ret = usb_autopm_get_interface(video->dev->intf);	if (ret < 0)		goto done;	/* Create the device handle. */	handle = kzalloc(sizeof *handle, GFP_KERNEL);	if (handle == NULL) {		usb_autopm_put_interface(video->dev->intf);		ret = -ENOMEM;		goto done;	}	handle->device = video;	handle->state = UVC_HANDLE_PASSIVE;	file->private_data = handle;	kref_get(&video->dev->kref);done:	mutex_unlock(&uvc_driver.open_mutex);	return ret;}static int uvc_v4l2_release(struct inode *inode, struct file *file){	struct video_device *vdev = video_devdata(file);	struct uvc_video_device *video = video_get_drvdata(vdev);	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");	/* Only free resources if this is a privileged handle. */	if (uvc_has_privileges(handle)) {		uvc_video_enable(video, 0);		mutex_lock(&video->queue.mutex);		if (uvc_free_buffers(&video->queue) < 0)			uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "					"free buffers.\n");		mutex_unlock(&video->queue.mutex);	}	/* Release the file handle. */	uvc_dismiss_privileges(handle);	kfree(handle);	file->private_data = NULL;	usb_autopm_put_interface(video->dev->intf);	kref_put(&video->dev->kref, uvc_delete);	return 0;}static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,		     unsigned int cmd, void *arg){	struct video_device *vdev = video_devdata(file);	struct uvc_video_device *video = video_get_drvdata(vdev);	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;	int ret = 0;	if (uvc_trace_param & UVC_TRACE_IOCTL)		v4l_printk_ioctl(cmd);	switch (cmd) {	/* Query capabilities */	case VIDIOC_QUERYCAP:	{		struct v4l2_capability *cap = arg;		memset(cap, 0, sizeof *cap);		strncpy(cap->driver, "uvcvideo", sizeof cap->driver);		strncpy(cap->card, vdev->name, 32);		strncpy(cap->bus_info, video->dev->udev->bus->bus_name,			sizeof cap->bus_info);		cap->version = DRIVER_VERSION_NUMBER;		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE				  | V4L2_CAP_STREAMING;	}		break;	/* Get, Set & Query control */	case VIDIOC_QUERYCTRL:		return uvc_query_v4l2_ctrl(video, (struct v4l2_queryctrl*)arg);	case VIDIOC_G_CTRL:	{		struct v4l2_control *ctrl = (struct v4l2_control*)arg;		struct v4l2_ext_control xctrl;		memset(&xctrl, 0, sizeof xctrl);		xctrl.id = ctrl->id;		uvc_ctrl_begin(video);		ret = uvc_ctrl_get(video, &xctrl);		uvc_ctrl_rollback(video);		if (ret >= 0)			ctrl->value = xctrl.value;	}		break;	case VIDIOC_S_CTRL:	{		struct v4l2_control *ctrl = (struct v4l2_control*)arg;		struct v4l2_ext_control xctrl;		memset(&xctrl, 0, sizeof xctrl);		xctrl.id = ctrl->id;		xctrl.value = ctrl->value;		uvc_ctrl_begin(video);		ret = uvc_ctrl_set(video, &xctrl);		if (ret < 0) {			uvc_ctrl_rollback(video);			return ret;		}		ret = uvc_ctrl_commit(video);	}		break;	case VIDIOC_QUERYMENU:		return uvc_v4l2_query_menu(video, (struct v4l2_querymenu*)arg);	case VIDIOC_G_EXT_CTRLS:	{		struct v4l2_ext_controls *ctrls = (struct v4l2_ext_controls*)arg;		struct v4l2_ext_control *ctrl = ctrls->controls;		unsigned int i;

⌨️ 快捷键说明

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