Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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