Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0-only
config USB_VIDEO_CLASS
tristate "USB Video Class (UVC)"
depends on VIDEO_DEV
select VIDEOBUF2_VMALLOC
select UVC_COMMON
help
Support for the USB Video Class (UVC). Currently only video
input devices, such as webcams, are supported.
For more information see: <http://linux-uvc.berlios.de/>
config USB_VIDEO_CLASS_INPUT_EVDEV
bool "UVC input events device support"
default y
depends on USB_VIDEO_CLASS
depends on USB_VIDEO_CLASS=INPUT || INPUT=y
help
This option makes USB Video Class devices register an input device
to report button events.
If you are in doubt, say Y.
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
uvc_status.o uvc_isight.o uvc_debugfs.o uvc_metadata.o
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
uvcvideo-objs += uvc_entity.o
endif
obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_debugfs.c -- USB Video Class driver - Debugging support
*
* Copyright (C) 2011
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include "uvcvideo.h"
/* -----------------------------------------------------------------------------
* Statistics
*/
#define UVC_DEBUGFS_BUF_SIZE 1024
struct uvc_debugfs_buffer {
size_t count;
char data[UVC_DEBUGFS_BUF_SIZE];
};
static int uvc_debugfs_stats_open(struct inode *inode, struct file *file)
{
struct uvc_streaming *stream = inode->i_private;
struct uvc_debugfs_buffer *buf;
buf = kmalloc_obj(*buf);
if (buf == NULL)
return -ENOMEM;
buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data));
file->private_data = buf;
return 0;
}
static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf,
size_t nbytes, loff_t *ppos)
{
struct uvc_debugfs_buffer *buf = file->private_data;
return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data,
buf->count);
}
static int uvc_debugfs_stats_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
static const struct file_operations uvc_debugfs_stats_fops = {
.owner = THIS_MODULE,
.open = uvc_debugfs_stats_open,
.read = uvc_debugfs_stats_read,
.release = uvc_debugfs_stats_release,
};
/* -----------------------------------------------------------------------------
* Global and stream initialization/cleanup
*/
static struct dentry *uvc_debugfs_root_dir;
void uvc_debugfs_init_stream(struct uvc_streaming *stream)
{
struct usb_device *udev = stream->dev->udev;
char dir_name[33];
if (uvc_debugfs_root_dir == NULL)
return;
snprintf(dir_name, sizeof(dir_name), "%u-%u-%u", udev->bus->busnum,
udev->devnum, stream->intfnum);
stream->debugfs_dir = debugfs_create_dir(dir_name,
uvc_debugfs_root_dir);
debugfs_create_file("stats", 0444, stream->debugfs_dir, stream,
&uvc_debugfs_stats_fops);
}
void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream)
{
debugfs_remove_recursive(stream->debugfs_dir);
stream->debugfs_dir = NULL;
}
void uvc_debugfs_init(void)
{
uvc_debugfs_root_dir = debugfs_create_dir("uvcvideo", usb_debug_root);
}
void uvc_debugfs_cleanup(void)
{
debugfs_remove_recursive(uvc_debugfs_root_dir);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_entity.c -- USB Video Class driver
*
* Copyright (C) 2005-2011
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include "uvcvideo.h"
static int uvc_mc_create_links(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
struct media_entity *sink;
unsigned int i;
int ret;
sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
? (entity->vdev ? &entity->vdev->entity : NULL)
: &entity->subdev.entity;
if (sink == NULL)
return 0;
for (i = 0; i < entity->num_pads; ++i) {
struct media_entity *source;
struct uvc_entity *remote;
u8 remote_pad;
if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
continue;
remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
if (remote == NULL || remote->num_pads == 0)
return -EINVAL;
source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
? (remote->vdev ? &remote->vdev->entity : NULL)
: &remote->subdev.entity;
if (source == NULL)
continue;
remote_pad = remote->num_pads - 1;
ret = media_create_pad_link(source, remote_pad,
sink, i, flags);
if (ret < 0)
return ret;
}
return 0;
}
static const struct v4l2_subdev_ops uvc_subdev_ops = {
};
void uvc_mc_cleanup_entity(struct uvc_entity *entity)
{
if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
media_entity_cleanup(&entity->subdev.entity);
else if (entity->vdev != NULL)
media_entity_cleanup(&entity->vdev->entity);
}
static int uvc_mc_init_entity(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
int ret;
if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
u32 function;
v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
strscpy(entity->subdev.name, entity->name,
sizeof(entity->subdev.name));
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_SELECTOR_UNIT:
function = MEDIA_ENT_F_VID_MUX;
break;
case UVC_VC_PROCESSING_UNIT:
case UVC_VC_EXTENSION_UNIT:
/* For lack of a better option. */
function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
break;
case UVC_COMPOSITE_CONNECTOR:
case UVC_COMPONENT_CONNECTOR:
function = MEDIA_ENT_F_CONN_COMPOSITE;
break;
case UVC_SVIDEO_CONNECTOR:
function = MEDIA_ENT_F_CONN_SVIDEO;
break;
case UVC_ITT_CAMERA:
function = MEDIA_ENT_F_CAM_SENSOR;
break;
case UVC_TT_VENDOR_SPECIFIC:
case UVC_ITT_VENDOR_SPECIFIC:
case UVC_ITT_MEDIA_TRANSPORT_INPUT:
case UVC_OTT_VENDOR_SPECIFIC:
case UVC_OTT_DISPLAY:
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
case UVC_EXTERNAL_VENDOR_SPECIFIC:
case UVC_EXT_GPIO_UNIT:
default:
function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
break;
}
entity->subdev.entity.function = function;
ret = media_entity_pads_init(&entity->subdev.entity,
entity->num_pads, entity->pads);
if (ret < 0)
return ret;
ret = v4l2_device_register_subdev(&chain->dev->vdev,
&entity->subdev);
} else if (entity->vdev != NULL) {
ret = media_entity_pads_init(&entity->vdev->entity,
entity->num_pads, entity->pads);
if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
} else
ret = 0;
return ret;
}
int uvc_mc_register_entities(struct uvc_video_chain *chain)
{
struct uvc_entity *entity;
int ret;
list_for_each_entry(entity, &chain->entities, chain) {
ret = uvc_mc_init_entity(chain, entity);
if (ret < 0) {
dev_info(&chain->dev->intf->dev,
"Failed to initialize entity for entity %u\n",
entity->id);
return ret;
}
}
list_for_each_entry(entity, &chain->entities, chain) {
ret = uvc_mc_create_links(chain, entity);
if (ret < 0) {
dev_info(&chain->dev->intf->dev,
"Failed to create links for entity %u\n",
entity->id);
return ret;
}
}
return 0;
}
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_isight.c -- USB Video Class driver - iSight support
*
* Copyright (C) 2006-2007
* Ivan N. Zlatev <contact@i-nz.net>
* Copyright (C) 2008-2009
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/usb.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include "uvcvideo.h"
/*
* Built-in iSight webcams implements most of UVC 1.0 except a
* different packet format. Instead of sending a header at the
* beginning of each isochronous transfer payload, the webcam sends a
* single header per image (on its own in a packet), followed by
* packets containing data only.
*
* Offset Size (bytes) Description
* ------------------------------------------------------------------
* 0x00 1 Header length
* 0x01 1 Flags (UVC-compliant)
* 0x02 4 Always equal to '11223344'
* 0x06 8 Always equal to 'deadbeefdeadface'
* 0x0e 16 Unknown
*
* The header can be prefixed by an optional, unknown-purpose byte.
*/
static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
const u8 *data, unsigned int len)
{
static const u8 hdr[] = {
0x11, 0x22, 0x33, 0x44,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xfa, 0xce
};
struct uvc_streaming *stream = queue->stream;
unsigned int maxlen, nbytes;
u8 *mem;
int is_header = 0;
if (buf == NULL)
return 0;
if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
(len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
uvc_dbg(stream->dev, FRAME, "iSight header found\n");
is_header = 1;
}
/* Synchronize to the input stream by waiting for a header packet. */
if (buf->state != UVC_BUF_STATE_ACTIVE) {
if (!is_header) {
uvc_dbg(stream->dev, FRAME,
"Dropping packet (out of sync)\n");
return 0;
}
buf->state = UVC_BUF_STATE_ACTIVE;
}
/*
* Mark the buffer as done if we're at the beginning of a new frame.
*
* Empty buffers (bytesused == 0) don't trigger end of frame detection
* as it doesn't make sense to return an empty buffer.
*/
if (is_header && buf->bytesused != 0) {
buf->state = UVC_BUF_STATE_DONE;
return -EAGAIN;
}
/*
* Copy the video data to the buffer. Skip header packets, as they
* contain no data.
*/
if (!is_header) {
maxlen = buf->length - buf->bytesused;
mem = buf->mem + buf->bytesused;
nbytes = min(len, maxlen);
memcpy(mem, data, nbytes);
buf->bytesused += nbytes;
if (len > maxlen || buf->bytesused == buf->length) {
uvc_dbg(stream->dev, FRAME,
"Frame complete (overflow)\n");
buf->state = UVC_BUF_STATE_DONE;
}
}
return 0;
}
void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
struct uvc_buffer *meta_buf)
{
struct urb *urb = uvc_urb->urb;
struct uvc_streaming *stream = uvc_urb->stream;
int ret, i;
for (i = 0; i < urb->number_of_packets; ++i) {
if (urb->iso_frame_desc[i].status < 0) {
uvc_dbg(stream->dev, FRAME,
"USB isochronous frame lost (%d)\n",
urb->iso_frame_desc[i].status);
}
/*
* Decode the payload packet.
*
* uvc_video_decode is entered twice when a frame transition
* has been detected because the end of frame can only be
* reliably detected when the first packet of the new frame
* is processed. The first pass detects the transition and
* closes the previous frame's buffer, the second pass
* processes the data of the first payload of the new frame.
*/
do {
ret = isight_decode(&stream->queue, buf,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
if (buf == NULL)
break;
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
buf = uvc_queue_next_buffer(&stream->queue,
buf);
} while (ret == -EAGAIN);
}
}
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_metadata.c -- USB Video Class driver - Metadata handling
*
* Copyright (C) 2016
* Guennadi Liakhovetski (guennadi.liakhovetski@intel.com)
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
#include "uvcvideo.h"
/* -----------------------------------------------------------------------------
* V4L2 ioctls
*/
static int uvc_meta_v4l2_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_video_chain *chain = stream->chain;
strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
strscpy(cap->card, stream->dev->name, sizeof(cap->card));
usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
| chain->caps;
return 0;
}
static int uvc_meta_v4l2_get_format(struct file *file, void *priv,
struct v4l2_format *format)
{
struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct v4l2_meta_format *fmt = &format->fmt.meta;
if (format->type != vfh->vdev->queue->type)
return -EINVAL;
fmt->dataformat = stream->meta.format;
fmt->buffersize = stream->meta.buffersize;
return 0;
}
static int uvc_meta_v4l2_try_format(struct file *file, void *priv,
struct v4l2_format *format)
{
struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
struct v4l2_meta_format *fmt = &format->fmt.meta;
u32 fmeta = V4L2_META_FMT_UVC;
u32 buffersize;
if (format->type != vfh->vdev->queue->type)
return -EINVAL;
for (unsigned int i = 0; i < dev->nmeta_formats; i++) {
if (dev->meta_formats[i] == fmt->dataformat) {
fmeta = fmt->dataformat;
break;
}
}
buffersize = max(UVC_METADATA_BUF_MIN_SIZE, fmt->buffersize);
memset(fmt, 0, sizeof(*fmt));
fmt->dataformat = fmeta;
fmt->buffersize = buffersize;
return 0;
}
static int uvc_meta_v4l2_set_format(struct file *file, void *priv,
struct v4l2_format *format)
{
struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct v4l2_meta_format *fmt = &format->fmt.meta;
int ret;
ret = uvc_meta_v4l2_try_format(file, priv, format);
if (ret < 0)
return ret;
/*
* We could in principle switch at any time, also during streaming.
* Metadata buffers would still be perfectly parseable, but it's more
* consistent and cleaner to disallow that.
*/
if (vb2_is_busy(&stream->meta.queue.queue))
return -EBUSY;
stream->meta.format = fmt->dataformat;
stream->meta.buffersize = fmt->buffersize;
return 0;
}
static int uvc_meta_v4l2_enum_formats(struct file *file, void *priv,
struct v4l2_fmtdesc *fdesc)
{
struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
if (fdesc->type != vfh->vdev->queue->type)
return -EINVAL;
if (fdesc->index >= dev->nmeta_formats)
return -EINVAL;
fdesc->pixelformat = dev->meta_formats[fdesc->index];
return 0;
}
static const struct v4l2_ioctl_ops uvc_meta_ioctl_ops = {
.vidioc_querycap = uvc_meta_v4l2_querycap,
.vidioc_g_fmt_meta_cap = uvc_meta_v4l2_get_format,
.vidioc_s_fmt_meta_cap = uvc_meta_v4l2_set_format,
.vidioc_try_fmt_meta_cap = uvc_meta_v4l2_try_format,
.vidioc_enum_fmt_meta_cap = uvc_meta_v4l2_enum_formats,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
};
/* -----------------------------------------------------------------------------
* V4L2 File Operations
*/
static const struct v4l2_file_operations uvc_meta_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
};
static struct uvc_entity *uvc_meta_find_msxu(struct uvc_device *dev)
{
static const u8 uvc_msxu_guid[16] = UVC_GUID_MSXU_1_5;
struct uvc_entity *entity;
list_for_each_entry(entity, &dev->entities, list) {
if (!memcmp(entity->guid, uvc_msxu_guid, sizeof(entity->guid)))
return entity;
}
return NULL;
}
static int uvc_meta_detect_msxu(struct uvc_device *dev)
{
u32 *data __free(kfree) = NULL;
struct uvc_entity *entity;
int ret;
entity = uvc_meta_find_msxu(dev);
if (!entity)
return 0;
/*
* USB requires buffers aligned in a special way, simplest way is to
* make sure that query_ctrl will work is to kmalloc() them.
*/
data = kmalloc_obj(*data);
if (!data)
return -ENOMEM;
/*
* Check if the metadata is already enabled, or if the device always
* returns metadata.
*/
ret = uvc_query_ctrl(dev, UVC_GET_CUR, entity->id, dev->intfnum,
UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
if (ret)
return 0;
if (*data) {
dev->quirks |= UVC_QUIRK_MSXU_META;
return 0;
}
/*
* Set the value of UVC_MSXU_CONTROL_METADATA to the value reported by
* GET_MAX to enable production of MSXU metadata. The GET_MAX request
* reports the maximum size of the metadata, if its value is 0 then MSXU
* metadata is not supported. For more information, see
* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#2229-metadata-control
*/
ret = uvc_query_ctrl(dev, UVC_GET_MAX, entity->id, dev->intfnum,
UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
if (ret || !*data)
return 0;
/*
* If we can set UVC_MSXU_CONTROL_METADATA, the device will report
* metadata.
*/
ret = uvc_query_ctrl(dev, UVC_SET_CUR, entity->id, dev->intfnum,
UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
if (!ret)
dev->quirks |= UVC_QUIRK_MSXU_META;
return 0;
}
int uvc_meta_register(struct uvc_streaming *stream)
{
struct uvc_device *dev = stream->dev;
struct uvc_video_queue *queue = &stream->meta.queue;
stream->meta.format = V4L2_META_FMT_UVC;
stream->meta.buffersize = UVC_METADATA_BUF_MIN_SIZE;
return uvc_register_video_device(dev, stream, queue,
V4L2_BUF_TYPE_META_CAPTURE,
&uvc_meta_fops, &uvc_meta_ioctl_ops);
}
int uvc_meta_init(struct uvc_device *dev)
{
unsigned int i = 0;
int ret;
ret = uvc_meta_detect_msxu(dev);
if (ret)
return ret;
dev->meta_formats[i++] = V4L2_META_FMT_UVC;
if (dev->info->meta_format &&
!WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC))
dev->meta_formats[i++] = dev->info->meta_format;
if (dev->quirks & UVC_QUIRK_MSXU_META &&
!WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC_MSXU_1_5))
dev->meta_formats[i++] = V4L2_META_FMT_UVC_MSXU_1_5;
/* IMPORTANT: for new meta-formats update UVC_MAX_META_DATA_FORMATS. */
dev->nmeta_formats = i;
return 0;
}
@@ -0,0 +1,398 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_queue.c -- USB Video Class driver - Buffers management
*
* Copyright (C) 2005-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
#include "uvcvideo.h"
/* ------------------------------------------------------------------------
* Video buffers queue management.
*
* Video queues is initialized by uvc_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
* Video buffers are managed by videobuf2. The driver uses a mutex to protect
* the videobuf2 queue operations by serializing calls to videobuf2 and a
* spinlock to protect the IRQ queue that holds the buffers to be processed by
* the driver.
*/
static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf)
{
return container_of(buf, struct uvc_buffer, buf);
}
/*
* Return all queued buffers to videobuf2 in the requested state.
*
* This function must be called with the queue spinlock held.
*/
static void __uvc_queue_return_buffers(struct uvc_video_queue *queue,
enum uvc_buffer_state state)
{
enum vb2_buffer_state vb2_state = state == UVC_BUF_STATE_ERROR
? VB2_BUF_STATE_ERROR
: VB2_BUF_STATE_QUEUED;
lockdep_assert_held(&queue->irqlock);
while (!list_empty(&queue->irqqueue)) {
struct uvc_buffer *buf = list_first_entry(&queue->irqqueue,
struct uvc_buffer,
queue);
list_del(&buf->queue);
buf->state = state;
vb2_buffer_done(&buf->buf.vb2_buf, vb2_state);
}
}
static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
enum uvc_buffer_state state)
{
spin_lock_irq(&queue->irqlock);
__uvc_queue_return_buffers(queue, state);
spin_unlock_irq(&queue->irqlock);
}
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
static int uvc_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = queue->stream;
unsigned int size;
switch (vq->type) {
case V4L2_BUF_TYPE_META_CAPTURE:
size = stream->meta.buffersize;
break;
default:
size = stream->ctrl.dwMaxVideoFrameSize;
break;
}
/*
* When called with plane sizes, validate them. The driver supports
* single planar formats only, and requires buffers to be large enough
* to store a complete frame.
*/
if (*nplanes)
return *nplanes != 1 || sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
return 0;
}
static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf);
if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
uvc_dbg(queue->stream->dev, CAPTURE,
"[E] Bytes used out of bounds\n");
return -EINVAL;
}
if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
return -ENODEV;
buf->state = UVC_BUF_STATE_QUEUED;
buf->error = 0;
buf->mem = vb2_plane_vaddr(vb, 0);
buf->length = vb2_plane_size(vb, 0);
if (vb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
buf->bytesused = 0;
else
buf->bytesused = vb2_get_plane_payload(vb, 0);
return 0;
}
static void uvc_buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf);
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
kref_init(&buf->ref);
list_add_tail(&buf->queue, &queue->irqqueue);
} else {
/*
* If the device is disconnected return the buffer to userspace
* directly. The next QBUF call will fail with -ENODEV.
*/
buf->state = UVC_BUF_STATE_ERROR;
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&queue->irqlock, flags);
}
static void uvc_buffer_finish(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf);
if (vb->state == VB2_BUF_STATE_DONE)
uvc_video_clock_update(queue->stream, vbuf, buf);
}
static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = queue->stream;
int ret;
lockdep_assert_irqs_enabled();
ret = uvc_pm_get(stream->dev);
if (ret)
goto err_buffers;
queue->buf_used = 0;
ret = uvc_video_start_streaming(stream);
if (ret)
goto err_pm;
return 0;
err_pm:
uvc_pm_put(stream->dev);
err_buffers:
uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
return ret;
}
static void uvc_stop_streaming_video(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = queue->stream;
lockdep_assert_irqs_enabled();
uvc_video_stop_streaming(stream);
uvc_pm_put(stream->dev);
uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
}
static void uvc_stop_streaming_meta(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
lockdep_assert_irqs_enabled();
uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
}
static const struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
.buf_finish = uvc_buffer_finish,
.start_streaming = uvc_start_streaming_video,
.stop_streaming = uvc_stop_streaming_video,
};
static const struct vb2_ops uvc_meta_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
/*
* .start_streaming is not provided here. Metadata relies on video
* streaming being active. If video isn't streaming, then no metadata
* will arrive either.
*/
.stop_streaming = uvc_stop_streaming_meta,
};
int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue,
enum v4l2_buf_type type)
{
int ret;
queue->stream = stream;
queue->queue.type = type;
queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.mem_ops = &vb2_vmalloc_memops;
queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
| V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
queue->queue.lock = &queue->mutex;
switch (type) {
case V4L2_BUF_TYPE_META_CAPTURE:
queue->queue.ops = &uvc_meta_queue_qops;
break;
default:
queue->queue.ops = &uvc_queue_qops;
break;
}
ret = vb2_queue_init(&queue->queue);
if (ret)
return ret;
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->irqqueue);
return 0;
}
/* -----------------------------------------------------------------------------
*
*/
/*
* Cancel the video buffers queue.
*
* Cancelling the queue marks all buffers on the irq queue as erroneous,
* wakes them up and removes them from the queue.
*
* If the disconnect parameter is set, further calls to uvc_queue_buffer will
* fail with -ENODEV.
*
* This function acquires the irq spinlock and can be called from interrupt
* context.
*/
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
{
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
__uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
/*
* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_buffer_queue and the disconnection event that
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
* blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED
* state outside the queue code.
*/
if (disconnect)
queue->flags |= UVC_QUEUE_DISCONNECTED;
spin_unlock_irqrestore(&queue->irqlock, flags);
}
/*
* uvc_queue_get_current_buffer: Obtain the current working output buffer
*
* Buffers may span multiple packets, and even URBs, therefore the active buffer
* remains on the queue until the EOF marker.
*/
static struct uvc_buffer *
__uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
{
if (list_empty(&queue->irqqueue))
return NULL;
return list_first_entry(&queue->irqqueue, struct uvc_buffer, queue);
}
struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
{
struct uvc_buffer *nextbuf;
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
nextbuf = __uvc_queue_get_current_buffer(queue);
spin_unlock_irqrestore(&queue->irqlock, flags);
return nextbuf;
}
/*
* uvc_queue_buffer_requeue: Requeue a buffer on our internal irqqueue
*
* Reuse a buffer through our internal queue without the need to 'prepare'.
* The buffer will be returned to userspace through the uvc_buffer_queue call if
* the device has been disconnected.
*/
static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
buf->bytesused = 0;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
uvc_buffer_queue(&buf->buf.vb2_buf);
}
static void uvc_queue_buffer_complete(struct kref *ref)
{
struct uvc_buffer *buf = container_of(ref, struct uvc_buffer, ref);
struct vb2_buffer *vb = &buf->buf.vb2_buf;
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
if (buf->error && !uvc_no_drop_param) {
uvc_queue_buffer_requeue(queue, buf);
return;
}
buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, buf->error ? VB2_BUF_STATE_ERROR :
VB2_BUF_STATE_DONE);
}
/*
* Release a reference on the buffer. Complete the buffer when the last
* reference is released.
*/
void uvc_queue_buffer_release(struct uvc_buffer *buf)
{
kref_put(&buf->ref, uvc_queue_buffer_complete);
}
/*
* Remove this buffer from the queue. Lifetime will persist while async actions
* are still running (if any), and uvc_queue_buffer_release will give the buffer
* back to VB2 when all users have completed.
*/
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
nextbuf = __uvc_queue_get_current_buffer(queue);
spin_unlock_irqrestore(&queue->irqlock, flags);
uvc_queue_buffer_release(buf);
return nextbuf;
}
@@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* uvc_status.c -- USB Video Class driver - Status endpoint
*
* Copyright (C) 2005-2009
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <asm/barrier.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include "uvcvideo.h"
/* --------------------------------------------------------------------------
* Input device
*/
#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
static bool uvc_input_has_button(struct uvc_device *dev)
{
struct uvc_streaming *stream;
/*
* The device has button events if both bTriggerSupport and
* bTriggerUsage are one. Otherwise the camera button does not
* exist or is handled automatically by the camera without host
* driver or client application intervention.
*/
list_for_each_entry(stream, &dev->streams, list) {
if (stream->header.bTriggerSupport == 1 &&
stream->header.bTriggerUsage == 1)
return true;
}
return false;
}
static int uvc_input_init(struct uvc_device *dev)
{
struct input_dev *input;
int ret;
if (!uvc_input_has_button(dev))
return 0;
input = input_allocate_device();
if (input == NULL)
return -ENOMEM;
usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));
strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
input->name = dev->name;
input->phys = dev->input_phys;
usb_to_input_id(dev->udev, &input->id);
input->dev.parent = &dev->intf->dev;
__set_bit(EV_KEY, input->evbit);
__set_bit(KEY_CAMERA, input->keybit);
ret = input_register_device(input);
if (ret < 0)
goto error;
dev->input = input;
return 0;
error:
input_free_device(input);
return ret;
}
static void uvc_input_unregister(struct uvc_device *dev)
{
if (dev->input)
input_unregister_device(dev->input);
}
static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
int value)
{
if (dev->input) {
input_report_key(dev->input, code, value);
input_sync(dev->input);
}
}
#else
#define uvc_input_init(dev)
#define uvc_input_unregister(dev)
#define uvc_input_report_key(dev, code, value)
#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
/* --------------------------------------------------------------------------
* Status interrupt endpoint
*/
static void uvc_event_streaming(struct uvc_device *dev,
struct uvc_status *status, int len)
{
if (len <= offsetof(struct uvc_status, bEvent)) {
uvc_dbg(dev, STATUS,
"Invalid streaming status event received\n");
return;
}
if (status->bEvent == 0) {
if (len <= offsetof(struct uvc_status, streaming))
return;
uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n",
status->bOriginator,
status->streaming.button ? "pressed" : "released", len);
uvc_input_report_key(dev, KEY_CAMERA, status->streaming.button);
} else {
uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n",
status->bOriginator, status->bEvent, len);
}
}
#define UVC_CTRL_VALUE_CHANGE 0
#define UVC_CTRL_INFO_CHANGE 1
#define UVC_CTRL_FAILURE_CHANGE 2
#define UVC_CTRL_MIN_CHANGE 3
#define UVC_CTRL_MAX_CHANGE 4
static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity,
u8 selector)
{
struct uvc_control *ctrl;
unsigned int i;
for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++)
if (ctrl->info.selector == selector)
return ctrl;
return NULL;
}
static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
const struct uvc_status *status,
struct uvc_video_chain **chain)
{
list_for_each_entry((*chain), &dev->chains, list) {
struct uvc_entity *entity;
struct uvc_control *ctrl;
list_for_each_entry(entity, &(*chain)->entities, chain) {
if (entity->id != status->bOriginator)
continue;
ctrl = uvc_event_entity_find_ctrl(entity,
status->control.bSelector);
if (ctrl)
return ctrl;
}
}
return NULL;
}
static bool uvc_event_control(struct urb *urb,
const struct uvc_status *status, int len)
{
static const char *attrs[] = { "value", "info", "failure", "min", "max" };
struct uvc_device *dev = urb->context;
struct uvc_video_chain *chain;
struct uvc_control *ctrl;
if (len < 6 || status->bEvent != 0 ||
status->control.bAttribute >= ARRAY_SIZE(attrs)) {
uvc_dbg(dev, STATUS, "Invalid control status event received\n");
return false;
}
uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n",
status->bOriginator, status->control.bSelector,
attrs[status->control.bAttribute], len);
/* Find the control. */
ctrl = uvc_event_find_ctrl(dev, status, &chain);
if (!ctrl)
return false;
switch (status->control.bAttribute) {
case UVC_CTRL_VALUE_CHANGE:
return uvc_ctrl_status_event_async(urb, chain, ctrl,
status->control.bValue);
case UVC_CTRL_INFO_CHANGE:
case UVC_CTRL_FAILURE_CHANGE:
case UVC_CTRL_MIN_CHANGE:
case UVC_CTRL_MAX_CHANGE:
break;
}
return false;
}
static void uvc_status_complete(struct urb *urb)
{
struct uvc_device *dev = urb->context;
int len, ret;
switch (urb->status) {
case 0:
break;
case -ENOENT: /* usb_kill_urb() called. */
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
case -EPROTO: /* Device is disconnected (reported by some host controllers). */
return;
default:
dev_warn(&dev->intf->dev,
"Non-zero status (%d) in status completion handler.\n",
urb->status);
return;
}
len = urb->actual_length;
if (len > 0) {
switch (dev->status->bStatusType & 0x0f) {
case UVC_STATUS_TYPE_CONTROL: {
if (uvc_event_control(urb, dev->status, len))
/* The URB will be resubmitted in work context. */
return;
break;
}
case UVC_STATUS_TYPE_STREAMING: {
uvc_event_streaming(dev, dev->status, len);
break;
}
default:
uvc_dbg(dev, STATUS, "Unknown status event type %u\n",
dev->status->bStatusType);
break;
}
}
/* Resubmit the URB. */
urb->interval = dev->int_ep->desc.bInterval;
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0)
dev_err(&dev->intf->dev,
"Failed to resubmit status URB (%d).\n", ret);
}
int uvc_status_init(struct uvc_device *dev)
{
struct usb_host_endpoint *ep = dev->int_ep;
unsigned int pipe;
int interval;
mutex_init(&dev->status_lock);
if (ep == NULL)
return 0;
dev->status = kzalloc_obj(*dev->status);
if (!dev->status)
return -ENOMEM;
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->int_urb) {
kfree(dev->status);
dev->status = NULL;
return -ENOMEM;
}
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
/*
* For high-speed interrupt endpoints, the bInterval value is used as
* an exponent of two. Some developers forgot about it.
*/
interval = ep->desc.bInterval;
if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&
(dev->quirks & UVC_QUIRK_STATUS_INTERVAL))
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
dev->status, sizeof(*dev->status), uvc_status_complete,
dev, interval);
uvc_input_init(dev);
return 0;
}
void uvc_status_unregister(struct uvc_device *dev)
{
if (!dev->status)
return;
uvc_status_suspend(dev);
uvc_input_unregister(dev);
}
void uvc_status_cleanup(struct uvc_device *dev)
{
usb_free_urb(dev->int_urb);
kfree(dev->status);
}
static int uvc_status_start(struct uvc_device *dev, gfp_t flags)
{
lockdep_assert_held(&dev->status_lock);
if (!dev->int_urb)
return 0;
/*
* If the previous uvc_status_stop() call was from the async work,
* the work may still be running. Wait for it to finish before we submit
* the urb.
*/
flush_work(&dev->async_ctrl.work);
/* Clear the flush status if we were previously stopped. */
smp_store_release(&dev->flush_status, false);
return usb_submit_urb(dev->int_urb, flags);
}
static void uvc_status_stop(struct uvc_device *dev)
{
struct uvc_ctrl_work *w = &dev->async_ctrl;
lockdep_assert_held(&dev->status_lock);
if (!dev->int_urb)
return;
/*
* Prevent the asynchronous control handler from requeing the URB. The
* barrier is needed so the flush_status change is visible to other
* CPUs running the asynchronous handler before usb_kill_urb() is
* called below.
*/
smp_store_release(&dev->flush_status, true);
/*
* If we are called from the event work function, the URB is guaranteed
* to not be in flight as it has completed and has not been resubmitted.
* There's no need to cancel the work (which would deadlock), or to kill
* the URB.
*/
if (current_work() == &w->work)
return;
/*
* Cancel any pending asynchronous work. If any status event was queued,
* process it synchronously.
*/
if (cancel_work_sync(&w->work))
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
/* Kill the urb. */
usb_kill_urb(dev->int_urb);
/*
* The URB completion handler may have queued asynchronous work. This
* won't resubmit the URB as flush_status is set, but it needs to be
* cancelled before returning or it could then race with a future
* uvc_status_start() call.
*/
if (cancel_work_sync(&w->work))
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
}
int uvc_status_resume(struct uvc_device *dev)
{
guard(mutex)(&dev->status_lock);
if (dev->status_users)
return uvc_status_start(dev, GFP_NOIO);
return 0;
}
void uvc_status_suspend(struct uvc_device *dev)
{
guard(mutex)(&dev->status_lock);
if (dev->status_users)
uvc_status_stop(dev);
}
int uvc_status_get(struct uvc_device *dev)
{
int ret;
guard(mutex)(&dev->status_lock);
if (!dev->status_users) {
ret = uvc_status_start(dev, GFP_KERNEL);
if (ret)
return ret;
}
dev->status_users++;
return 0;
}
void uvc_status_put(struct uvc_device *dev)
{
guard(mutex)(&dev->status_lock);
if (dev->status_users == 1)
uvc_status_stop(dev);
WARN_ON(!dev->status_users);
if (dev->status_users)
dev->status_users--;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,815 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _USB_VIDEO_H_
#define _USB_VIDEO_H_
#ifndef __KERNEL__
#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
#endif /* __KERNEL__ */
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/usb/video.h>
#include <linux/uvcvideo.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.h>
#include <media/videobuf2-v4l2.h>
/* --------------------------------------------------------------------------
* UVC constants
*/
#define UVC_TERM_INPUT 0x0000
#define UVC_TERM_OUTPUT 0x8000
#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000)
#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
#define UVC_ENTITY_IS_ITERM(entity) \
(UVC_ENTITY_IS_TERM(entity) && \
((entity)->type & 0x8000) == UVC_TERM_INPUT)
#define UVC_ENTITY_IS_OTERM(entity) \
(UVC_ENTITY_IS_TERM(entity) && \
((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
#define UVC_EXT_GPIO_UNIT 0x7ffe
#define UVC_EXT_GPIO_UNIT_ID 0x100
#define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff)
#define UVC_INVALID_ENTITY_ID 0xffff
/* ------------------------------------------------------------------------
* Driver specific constants.
*/
#define DRIVER_VERSION "1.1.1"
/* Number of isochronous URBs. */
#define UVC_URBS 5
/* Maximum number of packets per URB. */
#define UVC_MAX_PACKETS 32
#define UVC_CTRL_CONTROL_TIMEOUT 5000
#define UVC_CTRL_STREAMING_TIMEOUT 5000
/* Maximum allowed number of control mappings per device */
#define UVC_MAX_CONTROL_MAPPINGS 1024
#define UVC_MAX_CONTROL_MENU_ENTRIES 32
/* Devices quirks */
#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
#define UVC_QUIRK_PROBE_MINMAX 0x00000002
#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004
#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
#define UVC_QUIRK_STREAM_NO_FID 0x00000010
#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080
#define UVC_QUIRK_PROBE_DEF 0x00000100
#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400
#define UVC_QUIRK_FORCE_Y8 0x00000800
#define UVC_QUIRK_FORCE_BPP 0x00001000
#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000
#define UVC_QUIRK_NO_RESET_RESUME 0x00004000
#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000
#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000
#define UVC_QUIRK_MSXU_META 0x00040000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
#define UVC_FMT_FLAG_STREAM 0x00000002
/* ------------------------------------------------------------------------
* Structures.
*/
struct gpio_desc;
struct sg_table;
struct uvc_control;
struct uvc_device;
struct uvc_video_chain;
/*
* TODO: Put the most frequently accessed fields at the beginning of
* structures to maximize cache efficiency.
*/
struct uvc_control_info {
struct list_head mappings;
u8 entity[16];
u8 index; /* Bit index in bmControls */
u8 selector;
u16 size;
u32 flags;
};
struct uvc_control_mapping {
struct list_head list;
struct list_head ev_subs;
u32 id;
char *name;
u8 entity[16];
u8 selector;
/*
* Size of the control data in the payload of the UVC control GET and
* SET requests, expressed in bits.
*/
u8 size;
u8 offset;
enum v4l2_ctrl_type v4l2_type;
u32 data_type;
const u32 *menu_mapping;
const char (*menu_names)[UVC_MENU_NAME_LEN];
unsigned long menu_mask;
u32 master_id;
s32 master_manual;
u32 slave_ids[2];
bool disabled;
const struct uvc_control_mapping *(*filter_mapping)
(struct uvc_video_chain *chain,
struct uvc_control *ctrl);
int (*get)(struct uvc_control_mapping *mapping, u8 query,
const void *uvc_in, size_t v4l2_size, void *v4l2_out);
int (*set)(struct uvc_control_mapping *mapping, size_t v4l2_size,
const void *v4l2_in, void *uvc_out);
};
struct uvc_control {
struct uvc_entity *entity;
struct uvc_control_info info;
u8 index; /* Used to match the uvc_control entry with a uvc_control_info. */
u8 dirty:1,
loaded:1,
modified:1,
cached:1,
initialized:1;
u8 *uvc_data;
struct uvc_fh *handle; /* File handle that last changed the control. */
};
/*
* The term 'entity' refers to both UVC units and UVC terminals.
*
* The type field is either the terminal type (wTerminalType in the terminal
* descriptor), or the unit type (bDescriptorSubtype in the unit descriptor).
* As the bDescriptorSubtype field is one byte long, the type value will
* always have a null MSB for units. All terminal types defined by the UVC
* specification have a non-null MSB, so it is safe to use the MSB to
* differentiate between units and terminals as long as the descriptor parsing
* code makes sure terminal types have a non-null MSB.
*
* For terminals, the type's most significant bit stores the terminal
* direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should
* always be accessed with the UVC_ENTITY_* macros and never directly.
*/
#define UVC_ENTITY_FLAG_DEFAULT (1 << 0)
struct uvc_entity {
struct list_head list; /* Entity as part of a UVC device. */
struct list_head chain; /* Entity as part of a video device chain. */
unsigned int flags;
/*
* Entities exposed by the UVC device use IDs 0-255, extra entities
* implemented by the driver (such as the GPIO entity) use IDs 256 and
* up.
*/
u16 id;
u16 type;
char name[64];
u8 guid[16];
/* Media controller-related fields. */
struct video_device *vdev;
struct v4l2_subdev subdev;
unsigned int num_pads;
unsigned int num_links;
struct media_pad *pads;
union {
struct {
u16 wObjectiveFocalLengthMin;
u16 wObjectiveFocalLengthMax;
u16 wOcularFocalLength;
u8 bControlSize;
u8 *bmControls;
} camera;
struct {
u8 bControlSize;
u8 *bmControls;
u8 bTransportModeSize;
u8 *bmTransportModes;
} media;
struct {
} output;
struct {
u16 wMaxMultiplier;
u8 bControlSize;
u8 *bmControls;
u8 bmVideoStandards;
} processing;
struct {
} selector;
struct {
u8 bNumControls;
u8 bControlSize;
u8 *bmControls;
u8 *bmControlsType;
} extension;
struct {
u8 bControlSize;
u8 *bmControls;
struct gpio_desc *gpio_privacy;
int irq;
bool initialized;
} gpio;
};
u8 bNrInPins;
u8 *baSourceID;
int (*get_info)(struct uvc_device *dev, struct uvc_entity *entity,
u8 cs, u8 *caps);
int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
u8 cs, void *data, u16 size);
unsigned int ncontrols;
struct uvc_control *controls;
};
struct uvc_frame {
u8 bFrameIndex;
u8 bmCapabilities;
u16 wWidth;
u16 wHeight;
u32 dwMinBitRate;
u32 dwMaxBitRate;
u32 dwMaxVideoFrameBufferSize;
u8 bFrameIntervalType;
u32 dwDefaultFrameInterval;
const u32 *dwFrameInterval;
};
struct uvc_format {
u8 type;
u8 index;
u8 bpp;
enum v4l2_colorspace colorspace;
enum v4l2_xfer_func xfer_func;
enum v4l2_ycbcr_encoding ycbcr_enc;
u32 fcc;
u32 flags;
unsigned int nframes;
const struct uvc_frame *frames;
};
struct uvc_streaming_header {
u8 bNumFormats;
u8 bEndpointAddress;
u8 bTerminalLink;
u8 bControlSize;
u8 *bmaControls;
/* The following fields are used by input headers only. */
u8 bmInfo;
u8 bStillCaptureMethod;
u8 bTriggerSupport;
u8 bTriggerUsage;
};
enum uvc_buffer_state {
UVC_BUF_STATE_IDLE = 0,
UVC_BUF_STATE_QUEUED = 1,
UVC_BUF_STATE_ACTIVE = 2,
UVC_BUF_STATE_READY = 3,
UVC_BUF_STATE_DONE = 4,
UVC_BUF_STATE_ERROR = 5,
};
struct uvc_buffer {
struct vb2_v4l2_buffer buf;
struct list_head queue;
enum uvc_buffer_state state;
unsigned int error;
void *mem;
unsigned int length;
unsigned int bytesused;
u32 pts;
/* Asynchronous buffer handling. */
struct kref ref;
};
#define UVC_QUEUE_DISCONNECTED (1 << 0)
struct uvc_video_queue {
struct uvc_streaming *stream;
struct video_device vdev;
struct vb2_queue queue;
struct mutex mutex; /*
* Serializes vb2_queue and
* fops
*/
unsigned int flags;
unsigned int buf_used;
spinlock_t irqlock; /* Protects irqqueue */
struct list_head irqqueue;
};
struct uvc_video_chain {
struct uvc_device *dev;
struct list_head list;
struct list_head entities; /* All entities */
struct uvc_entity *processing; /* Processing unit */
struct uvc_entity *selector; /* Selector unit */
struct mutex ctrl_mutex; /*
* Protects ctrl.info,
* ctrl.handle and
* uvc_fh.pending_async_ctrls
*/
struct v4l2_prio_state prio; /* V4L2 priority state */
u32 caps; /* V4L2 chain-wide caps */
u8 ctrl_class_bitmap; /* Bitmap of valid classes */
};
struct uvc_stats_frame {
unsigned int size; /* Number of bytes captured */
unsigned int first_data; /* Index of the first non-empty packet */
unsigned int nb_packets; /* Number of packets */
unsigned int nb_empty; /* Number of empty packets */
unsigned int nb_invalid; /* Number of packets with an invalid header */
unsigned int nb_errors; /* Number of packets with the error bit set */
unsigned int nb_pts; /* Number of packets with a PTS timestamp */
unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */
unsigned int last_pts_diff; /* Index of the last PTS difference */
bool has_initial_pts; /* Whether the first non-empty packet has a PTS */
bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */
u32 pts; /* PTS of the last packet */
unsigned int nb_scr; /* Number of packets with a SCR timestamp */
unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */
u16 scr_sof; /* SCR.SOF of the last packet */
u32 scr_stc; /* SCR.STC of the last packet */
};
struct uvc_stats_stream {
ktime_t start_ts; /* Stream start timestamp */
ktime_t stop_ts; /* Stream stop timestamp */
unsigned int nb_frames; /* Number of frames */
unsigned int nb_packets; /* Number of packets */
unsigned int nb_empty; /* Number of empty packets */
unsigned int nb_invalid; /* Number of packets with an invalid header */
unsigned int nb_errors; /* Number of packets with the error bit set */
unsigned int nb_pts_constant; /* Number of frames with constant PTS */
unsigned int nb_pts_early; /* Number of frames with early PTS */
unsigned int nb_pts_initial; /* Number of frames with initial PTS */
unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */
unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */
unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */
unsigned int scr_sof; /* STC.SOF of the last packet */
unsigned int min_sof; /* Minimum STC.SOF value */
unsigned int max_sof; /* Maximum STC.SOF value */
};
#define UVC_METADATA_BUF_MIN_SIZE 10240
/**
* struct uvc_copy_op: Context structure to schedule asynchronous memcpy
*
* @buf: active buf object for this operation
* @dst: copy destination address
* @src: copy source address
* @len: copy length
*/
struct uvc_copy_op {
struct uvc_buffer *buf;
void *dst;
const __u8 *src;
size_t len;
};
/**
* struct uvc_urb - URB context management structure
*
* @urb: the URB described by this context structure
* @stream: UVC streaming context
* @buffer: memory storage for the URB
* @dma: Allocated DMA handle
* @sgt: sgt_table with the urb locations in memory
* @async_operations: counter to indicate the number of copy operations
* @copy_operations: work descriptors for asynchronous copy operations
* @work: work queue entry for asynchronous decode
*/
struct uvc_urb {
struct urb *urb;
struct uvc_streaming *stream;
char *buffer;
dma_addr_t dma;
struct sg_table *sgt;
unsigned int async_operations;
struct uvc_copy_op copy_operations[UVC_MAX_PACKETS];
struct work_struct work;
};
struct uvc_streaming {
struct list_head list;
struct uvc_device *dev;
struct uvc_video_chain *chain;
atomic_t active;
struct usb_interface *intf;
int intfnum;
u32 maxpsize;
struct uvc_streaming_header header;
enum v4l2_buf_type type;
unsigned int nformats;
const struct uvc_format *formats;
struct uvc_streaming_control ctrl;
const struct uvc_format *def_format;
const struct uvc_format *cur_format;
const struct uvc_frame *cur_frame;
/* Buffers queue. */
unsigned int frozen : 1;
struct uvc_video_queue queue;
struct workqueue_struct *async_wq;
void (*decode)(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
struct uvc_buffer *meta_buf);
struct {
struct uvc_video_queue queue;
u32 format;
u32 buffersize;
} meta;
/* Context data used by the bulk completion handler. */
struct {
u8 header[256];
unsigned int header_size;
int skip_payload;
u32 payload_size;
u32 max_payload_size;
} bulk;
struct uvc_urb uvc_urb[UVC_URBS];
unsigned int urb_size;
u32 sequence;
u8 last_fid;
/* debugfs */
struct dentry *debugfs_dir;
struct {
struct uvc_stats_frame frame;
struct uvc_stats_stream stream;
} stats;
/* Timestamps support. */
struct uvc_clock {
struct uvc_clock_sample {
u32 dev_stc;
u16 dev_sof;
u16 host_sof;
ktime_t host_time;
} *samples;
unsigned int head;
unsigned int count;
unsigned int size;
unsigned int last_sof_overflow;
u16 last_sof_processed;
u16 last_sof_raw;
u16 sof_offset;
u8 last_scr[6];
spinlock_t lock;
} clock;
};
#define for_each_uvc_urb(uvc_urb, uvc_streaming) \
for ((uvc_urb) = &(uvc_streaming)->uvc_urb[0]; \
(uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
++(uvc_urb))
static inline u32 uvc_urb_index(const struct uvc_urb *uvc_urb)
{
return uvc_urb - &uvc_urb->stream->uvc_urb[0];
}
struct uvc_device_info {
u32 quirks;
u32 meta_format;
u16 uvc_version;
};
struct uvc_rect {
u16 top;
u16 left;
u16 bottom;
u16 right;
} __packed;
struct uvc_status_streaming {
u8 button;
} __packed;
struct uvc_status_control {
u8 bSelector;
u8 bAttribute;
u8 bValue[11];
} __packed;
struct uvc_status {
u8 bStatusType;
u8 bOriginator;
u8 bEvent;
union {
struct uvc_status_control control;
struct uvc_status_streaming streaming;
};
} __packed;
#define UVC_MAX_META_DATA_FORMATS 3
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
unsigned long warnings;
u32 quirks;
int intfnum;
char name[32];
const struct uvc_device_info *info;
u32 meta_formats[UVC_MAX_META_DATA_FORMATS];
unsigned int nmeta_formats;
atomic_t nmappings;
/* Video control interface */
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device mdev;
#endif
struct v4l2_device vdev;
u16 uvc_version;
u32 clock_frequency;
struct list_head entities;
struct list_head chains;
/* Video Streaming interfaces */
struct list_head streams;
struct kref ref;
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
struct urb *int_urb;
struct uvc_status *status;
struct mutex status_lock; /* Protects status_users */
unsigned int status_users;
bool flush_status;
struct input_dev *input;
char input_phys[64];
struct uvc_ctrl_work {
struct work_struct work;
struct urb *urb;
struct uvc_video_chain *chain;
struct uvc_control *ctrl;
const void *data;
} async_ctrl;
struct uvc_entity *gpio_unit;
};
struct uvc_fh {
struct v4l2_fh vfh;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
unsigned int pending_async_ctrls;
};
static inline struct uvc_fh *to_uvc_fh(struct file *filp)
{
return container_of(file_to_v4l2_fh(filp), struct uvc_fh, vfh);
}
/* ------------------------------------------------------------------------
* Debugging, printing and logging
*/
#define UVC_DBG_PROBE (1 << 0)
#define UVC_DBG_DESCR (1 << 1)
#define UVC_DBG_CONTROL (1 << 2)
#define UVC_DBG_FORMAT (1 << 3)
#define UVC_DBG_CAPTURE (1 << 4)
#define UVC_DBG_CALLS (1 << 5)
#define UVC_DBG_FRAME (1 << 7)
#define UVC_DBG_SUSPEND (1 << 8)
#define UVC_DBG_STATUS (1 << 9)
#define UVC_DBG_VIDEO (1 << 10)
#define UVC_DBG_STATS (1 << 11)
#define UVC_DBG_CLOCK (1 << 12)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
#define UVC_WARN_XU_GET_RES 2
extern unsigned int uvc_clock_param;
extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_dbg_param;
extern unsigned int uvc_timeout_param;
extern unsigned int uvc_hw_timestamps_param;
extern bool uvc_allow_privacy_override_param;
#define uvc_dbg(_dev, flag, fmt, ...) \
do { \
if (uvc_dbg_param & UVC_DBG_##flag) \
dev_printk(KERN_DEBUG, &(_dev)->intf->dev, fmt, \
##__VA_ARGS__); \
} while (0)
#define uvc_dbg_cont(flag, fmt, ...) \
do { \
if (uvc_dbg_param & UVC_DBG_##flag) \
pr_cont(fmt, ##__VA_ARGS__); \
} while (0)
#define uvc_warn_once(_dev, warn, fmt, ...) \
do { \
if (!test_and_set_bit(warn, &(_dev)->warnings)) \
dev_info(&(_dev)->intf->dev, fmt, ##__VA_ARGS__); \
} while (0)
/* --------------------------------------------------------------------------
* Internal functions.
*/
struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue,
enum v4l2_buf_type type);
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
void uvc_queue_buffer_release(struct uvc_buffer *buf);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
}
/* V4L2 interface */
extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
extern const struct v4l2_file_operations uvc_fops;
/* Media controller */
int uvc_mc_register_entities(struct uvc_video_chain *chain);
void uvc_mc_cleanup_entity(struct uvc_entity *entity);
/* Video */
int uvc_video_init(struct uvc_streaming *stream);
int uvc_video_suspend(struct uvc_streaming *stream);
int uvc_video_resume(struct uvc_streaming *stream, int reset);
int uvc_video_start_streaming(struct uvc_streaming *stream);
void uvc_video_stop_streaming(struct uvc_streaming *stream);
int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe);
int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
u8 intfnum, u8 cs, void *data, u16 size);
void uvc_video_clock_update(struct uvc_streaming *stream,
struct vb2_v4l2_buffer *vbuf,
struct uvc_buffer *buf);
int uvc_meta_init(struct uvc_device *dev);
int uvc_meta_register(struct uvc_streaming *stream);
int uvc_register_video_device(struct uvc_device *dev,
struct uvc_streaming *stream,
struct uvc_video_queue *queue,
enum v4l2_buf_type type,
const struct v4l2_file_operations *fops,
const struct v4l2_ioctl_ops *ioctl_ops);
/* Status */
int uvc_status_init(struct uvc_device *dev);
void uvc_status_unregister(struct uvc_device *dev);
void uvc_status_cleanup(struct uvc_device *dev);
int uvc_status_resume(struct uvc_device *dev);
void uvc_status_suspend(struct uvc_device *dev);
int uvc_status_get(struct uvc_device *dev);
void uvc_status_put(struct uvc_device *dev);
/* PM */
int uvc_pm_get(struct uvc_device *dev);
void uvc_pm_put(struct uvc_device *dev);
/* Controls */
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_query_ext_ctrl *v4l2_ctrl);
int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
struct v4l2_querymenu *query_menu);
int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
const struct uvc_control_mapping *mapping);
int uvc_ctrl_init_device(struct uvc_device *dev);
void uvc_ctrl_cleanup_device(struct uvc_device *dev);
int uvc_ctrl_restore_values(struct uvc_device *dev);
bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain,
struct uvc_control *ctrl, const u8 *data);
void uvc_ctrl_status_event(struct uvc_video_chain *chain,
struct uvc_control *ctrl, const u8 *data);
int uvc_ctrl_begin(struct uvc_video_chain *chain);
int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
struct v4l2_ext_controls *ctrls);
static inline int uvc_ctrl_commit(struct uvc_fh *handle,
struct v4l2_ext_controls *ctrls)
{
return __uvc_ctrl_commit(handle, 0, ctrls);
}
static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
{
return __uvc_ctrl_commit(handle, 1, NULL);
}
int uvc_ctrl_get(struct uvc_video_chain *chain, u32 which,
struct v4l2_ext_control *xctrl);
int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
const struct v4l2_ext_controls *ctrls, u32 which,
unsigned long ioctl);
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
/* Utility functions */
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
u8 epaddr);
/* Quirks support */
void uvc_video_decode_isight(struct uvc_urb *uvc_urb,
struct uvc_buffer *buf,
struct uvc_buffer *meta_buf);
/* debugfs and statistics */
void uvc_debugfs_init(void);
void uvc_debugfs_cleanup(void);
void uvc_debugfs_init_stream(struct uvc_streaming *stream);
void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
size_t size);
#endif