Skip to content

Commit

Permalink
uvcvideo: backported from kernel 3.4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Siarhei Volkau committed Aug 31, 2015
1 parent dcfcc2e commit f8493a8
Show file tree
Hide file tree
Showing 11 changed files with 1,347 additions and 813 deletions.
1 change: 1 addition & 0 deletions drivers/media/video/uvc/Kconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
config USB_VIDEO_CLASS
tristate "USB Video Class (UVC)"
select VIDEOBUF2_VMALLOC
---help---
Support for the USB Video Class (UVC). Currently only video
input devices, such as webcams, are supported.
Expand Down
2 changes: 1 addition & 1 deletion drivers/media/video/uvc/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
uvc_status.o uvc_isight.o
uvc_status.o uvc_isight.o uvc_debugfs.o
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
uvcvideo-objs += uvc_entity.o
endif
Expand Down
31 changes: 23 additions & 8 deletions drivers/media/video/uvc/uvc_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <linux/atomic.h>

#include "uvcvideo.h"

Expand Down Expand Up @@ -878,8 +878,21 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
chain->dev->intfnum, ctrl->info.selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
ctrl->info.size);
if (ret < 0)
return ret;
if (ret < 0) {
if (UVC_ENTITY_TYPE(ctrl->entity) !=
UVC_VC_EXTENSION_UNIT)
return ret;

/* GET_RES is mandatory for XU controls, but some
* cameras still choke on it. Ignore errors and set the
* resolution value to zero.
*/
uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES,
"UVC non compliance - GET_RES failed on "
"an XU control. Enabling workaround.\n");
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
ctrl->info.size);
}
}

ctrl->cached = 1;
Expand Down Expand Up @@ -1016,7 +1029,8 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,

menu_info = &mapping->menu_info[query_menu->index];

if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
(ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
s32 bitmap;

if (!ctrl->cached) {
Expand Down Expand Up @@ -1225,7 +1239,8 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
/* Valid menu indices are reported by the GET_RES request for
* UVC controls that support it.
*/
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
(ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
Expand Down Expand Up @@ -1664,8 +1679,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
return -EINVAL;
}

/* Search for the matching (GUID/CS) control in the given device */
list_for_each_entry(entity, &dev->entities, list) {
/* Search for the matching (GUID/CS) control on the current chain */
list_for_each_entry(entity, &chain->entities, chain) {
unsigned int i;

if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
Expand Down Expand Up @@ -1859,7 +1874,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
if (ncontrols == 0)
continue;

entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
GFP_KERNEL);
if (entity->controls == NULL)
return -ENOMEM;
Expand Down
136 changes: 136 additions & 0 deletions drivers/media/video/uvc/uvc_debugfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* uvc_debugfs.c -- USB Video Class driver - Debugging support
*
* Copyright (C) 2011
* Laurent Pinchart ([email protected])
*
* 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/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(sizeof(*buf), GFP_KERNEL);
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,
.llseek = no_llseek,
.read = uvc_debugfs_stats_read,
.release = uvc_debugfs_stats_release,
};

/* -----------------------------------------------------------------------------
* Global and stream initialization/cleanup
*/

static struct dentry *uvc_debugfs_root_dir;

int uvc_debugfs_init_stream(struct uvc_streaming *stream)
{
struct usb_device *udev = stream->dev->udev;
struct dentry *dent;
char dir_name[32];

if (uvc_debugfs_root_dir == NULL)
return -ENODEV;

sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum);

dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
if (IS_ERR_OR_NULL(dent)) {
uvc_printk(KERN_INFO, "Unable to create debugfs %s "
"directory.\n", dir_name);
return -ENODEV;
}

stream->debugfs_dir = dent;

dent = debugfs_create_file("stats", 0444, stream->debugfs_dir,
stream, &uvc_debugfs_stats_fops);
if (IS_ERR_OR_NULL(dent)) {
uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n");
uvc_debugfs_cleanup_stream(stream);
return -ENODEV;
}

return 0;
}

void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream)
{
if (stream->debugfs_dir == NULL)
return;

debugfs_remove_recursive(stream->debugfs_dir);
stream->debugfs_dir = NULL;
}

int uvc_debugfs_init(void)
{
struct dentry *dir;

dir = debugfs_create_dir("uvcvideo", usb_debug_root);
if (IS_ERR_OR_NULL(dir)) {
uvc_printk(KERN_INFO, "Unable to create debugfs directory\n");
return -ENODATA;
}

uvc_debugfs_root_dir = dir;
return 0;
}

void uvc_debugfs_cleanup(void)
{
if (uvc_debugfs_root_dir != NULL)
debugfs_remove_recursive(uvc_debugfs_root_dir);
}
66 changes: 59 additions & 7 deletions drivers/media/video/uvc/uvc_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* codec can't handle MJPEG data.
*/

#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
Expand All @@ -31,7 +32,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <linux/version.h>
#include <asm/unaligned.h>

#include <media/v4l2-common.h>
Expand Down Expand Up @@ -113,6 +114,11 @@ static struct uvc_format_desc uvc_fmts[] = {
.guid = UVC_GUID_FORMAT_RGBP,
.fcc = V4L2_PIX_FMT_RGB565,
},
{
.name = "H.264",
.guid = UVC_GUID_FORMAT_H264,
.fcc = V4L2_PIX_FMT_H264,
},
};

/* ------------------------------------------------------------------------
Expand Down Expand Up @@ -1669,6 +1675,8 @@ static void uvc_unregister_video(struct uvc_device *dev)

video_unregister_device(stream->vdev);
stream->vdev = NULL;

uvc_debugfs_cleanup_stream(stream);
}

/* Decrement the stream count and call uvc_delete explicitly if there
Expand All @@ -1694,6 +1702,8 @@ static int uvc_register_video(struct uvc_device *dev,
return ret;
}

uvc_debugfs_init_stream(stream);

/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL) {
Expand Down Expand Up @@ -1857,7 +1867,7 @@ static int uvc_probe(struct usb_interface *intf,
sizeof(dev->mdev.serial));
strcpy(dev->mdev.bus_info, udev->devpath);
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
dev->mdev.driver_version = DRIVER_VERSION_NUMBER;
dev->mdev.driver_version = LINUX_VERSION_CODE;
if (media_device_register(&dev->mdev) < 0)
goto error;

Expand Down Expand Up @@ -2027,6 +2037,15 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
* though they are compliant.
*/
static struct usb_device_id uvc_ids[] = {
/* LogiLink Wireless Webcam */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0416,
.idProduct = 0xa91a,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Genius eFace 2025 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
Expand Down Expand Up @@ -2120,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Dell XPS m1530 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05a9,
.idProduct = 0x2640,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_DEF },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
Expand All @@ -2130,6 +2158,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_BUILTIN_ISIGHT },
/* Foxlink ("HP Webcam" on HP Mini 5103) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05c8,
.idProduct = 0x0403,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* Genesys Logic USB 2.0 PC Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
Expand Down Expand Up @@ -2321,6 +2358,14 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_DEF },
/* The Imaging Source USB CCD cameras */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x199e,
.idProduct = 0x8102,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Bodelin ProScopeHR */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_DEV_HI
Expand Down Expand Up @@ -2373,17 +2418,24 @@ struct uvc_driver uvc_driver = {

static int __init uvc_init(void)
{
int result;
int ret;

uvc_debugfs_init();

ret = usb_register(&uvc_driver.driver);
if (ret < 0) {
uvc_debugfs_cleanup();
return ret;
}

result = usb_register(&uvc_driver.driver);
if (result == 0)
printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
return result;
printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
return 0;
}

static void __exit uvc_cleanup(void)
{
usb_deregister(&uvc_driver.driver);
uvc_debugfs_cleanup();
}

module_init(uvc_init);
Expand Down
10 changes: 5 additions & 5 deletions drivers/media/video/uvc/uvc_isight.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
* 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->buf.bytesused != 0) {
if (is_header && buf->bytesused != 0) {
buf->state = UVC_BUF_STATE_DONE;
return -EAGAIN;
}
Expand All @@ -83,13 +83,13 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
* contain no data.
*/
if (!is_header) {
maxlen = buf->buf.length - buf->buf.bytesused;
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
maxlen = buf->length - buf->bytesused;
mem = buf->mem + buf->bytesused;
nbytes = min(len, maxlen);
memcpy(mem, data, nbytes);
buf->buf.bytesused += nbytes;
buf->bytesused += nbytes;

if (len > maxlen || buf->buf.bytesused == buf->buf.length) {
if (len > maxlen || buf->bytesused == buf->length) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete "
"(overflow).\n");
buf->state = UVC_BUF_STATE_DONE;
Expand Down
Loading

0 comments on commit f8493a8

Please sign in to comment.