forked from Minki/linux
9b9ea7c2b5
In order to prevent ISOC URBs from being infinitely resubmitted, the driver's USB disconnect handler must kill all the in-flight URBs. While here, change the URB packet status message to a debug level, to avoid spamming the console too much. This commit fixes a lockup caused by an interrupt storm coming from the URB completion handler. Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
1735 lines
44 KiB
C
1735 lines
44 KiB
C
/*
|
|
* Main USB camera driver
|
|
*
|
|
* Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
|
|
*
|
|
* Camera button input handling by Márton Németh
|
|
* Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#define GSPCA_VERSION "2.14.0"
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/string.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/io.h>
|
|
#include <asm/page.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/ktime.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-fh.h>
|
|
#include <media/v4l2-event.h>
|
|
|
|
#include "gspca.h"
|
|
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
#include <linux/input.h>
|
|
#include <linux/usb/input.h>
|
|
#endif
|
|
|
|
/* global values */
|
|
#define DEF_NURBS 3 /* default number of URBs */
|
|
#if DEF_NURBS > MAX_NURBS
|
|
#error "DEF_NURBS too big"
|
|
#endif
|
|
|
|
MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
|
|
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(GSPCA_VERSION);
|
|
|
|
int gspca_debug;
|
|
EXPORT_SYMBOL(gspca_debug);
|
|
|
|
static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt,
|
|
__u32 pixfmt, int w, int h)
|
|
{
|
|
if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
|
|
gspca_dbg(gspca_dev, debug, "%s %c%c%c%c %dx%d\n",
|
|
txt,
|
|
pixfmt & 0xff,
|
|
(pixfmt >> 8) & 0xff,
|
|
(pixfmt >> 16) & 0xff,
|
|
pixfmt >> 24,
|
|
w, h);
|
|
} else {
|
|
gspca_dbg(gspca_dev, debug, "%s 0x%08x %dx%d\n",
|
|
txt,
|
|
pixfmt,
|
|
w, h);
|
|
}
|
|
}
|
|
|
|
/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
|
|
#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
|
|
#define GSPCA_MEMORY_READ 7
|
|
|
|
/*
|
|
* Input and interrupt endpoint handling functions
|
|
*/
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
static void int_irq(struct urb *urb)
|
|
{
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
|
|
int ret;
|
|
|
|
ret = urb->status;
|
|
switch (ret) {
|
|
case 0:
|
|
if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
|
|
urb->transfer_buffer, urb->actual_length) < 0) {
|
|
gspca_err(gspca_dev, "Unknown packet received\n");
|
|
}
|
|
break;
|
|
|
|
case -ENOENT:
|
|
case -ECONNRESET:
|
|
case -ENODEV:
|
|
case -ESHUTDOWN:
|
|
/* Stop is requested either by software or hardware is gone,
|
|
* keep the ret value non-zero and don't resubmit later.
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
gspca_err(gspca_dev, "URB error %i, resubmitting\n",
|
|
urb->status);
|
|
urb->status = 0;
|
|
ret = 0;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
|
if (ret < 0)
|
|
pr_err("Resubmit URB failed with error %i\n", ret);
|
|
}
|
|
}
|
|
|
|
static int gspca_input_connect(struct gspca_dev *dev)
|
|
{
|
|
struct input_dev *input_dev;
|
|
int err = 0;
|
|
|
|
dev->input_dev = NULL;
|
|
if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) {
|
|
input_dev = input_allocate_device();
|
|
if (!input_dev)
|
|
return -ENOMEM;
|
|
|
|
usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
|
|
strlcat(dev->phys, "/input0", sizeof(dev->phys));
|
|
|
|
input_dev->name = dev->sd_desc->name;
|
|
input_dev->phys = dev->phys;
|
|
|
|
usb_to_input_id(dev->dev, &input_dev->id);
|
|
|
|
input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
|
input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
|
|
input_dev->dev.parent = &dev->dev->dev;
|
|
|
|
err = input_register_device(input_dev);
|
|
if (err) {
|
|
pr_err("Input device registration failed with error %i\n",
|
|
err);
|
|
input_dev->dev.parent = NULL;
|
|
input_free_device(input_dev);
|
|
} else {
|
|
dev->input_dev = input_dev;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
|
|
struct usb_endpoint_descriptor *ep)
|
|
{
|
|
unsigned int buffer_len;
|
|
int interval;
|
|
struct urb *urb;
|
|
struct usb_device *dev;
|
|
void *buffer = NULL;
|
|
int ret = -EINVAL;
|
|
|
|
buffer_len = le16_to_cpu(ep->wMaxPacketSize);
|
|
interval = ep->bInterval;
|
|
gspca_dbg(gspca_dev, D_CONF, "found int in endpoint: 0x%x, buffer_len=%u, interval=%u\n",
|
|
ep->bEndpointAddress, buffer_len, interval);
|
|
|
|
dev = gspca_dev->dev;
|
|
|
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!urb) {
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
buffer = usb_alloc_coherent(dev, buffer_len,
|
|
GFP_KERNEL, &urb->transfer_dma);
|
|
if (!buffer) {
|
|
ret = -ENOMEM;
|
|
goto error_buffer;
|
|
}
|
|
usb_fill_int_urb(urb, dev,
|
|
usb_rcvintpipe(dev, ep->bEndpointAddress),
|
|
buffer, buffer_len,
|
|
int_irq, (void *)gspca_dev, interval);
|
|
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
ret = usb_submit_urb(urb, GFP_KERNEL);
|
|
if (ret < 0) {
|
|
gspca_err(gspca_dev, "submit int URB failed with error %i\n",
|
|
ret);
|
|
goto error_submit;
|
|
}
|
|
gspca_dev->int_urb = urb;
|
|
return ret;
|
|
|
|
error_submit:
|
|
usb_free_coherent(dev,
|
|
urb->transfer_buffer_length,
|
|
urb->transfer_buffer,
|
|
urb->transfer_dma);
|
|
error_buffer:
|
|
usb_free_urb(urb);
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct usb_interface *intf;
|
|
struct usb_host_interface *intf_desc;
|
|
struct usb_endpoint_descriptor *ep;
|
|
int i;
|
|
|
|
if (gspca_dev->sd_desc->int_pkt_scan) {
|
|
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
|
|
intf_desc = intf->cur_altsetting;
|
|
for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
|
|
ep = &intf_desc->endpoint[i].desc;
|
|
if (usb_endpoint_dir_in(ep) &&
|
|
usb_endpoint_xfer_int(ep)) {
|
|
|
|
alloc_and_submit_int_urb(gspca_dev, ep);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct urb *urb;
|
|
|
|
urb = gspca_dev->int_urb;
|
|
if (urb) {
|
|
gspca_dev->int_urb = NULL;
|
|
usb_kill_urb(urb);
|
|
usb_free_coherent(gspca_dev->dev,
|
|
urb->transfer_buffer_length,
|
|
urb->transfer_buffer,
|
|
urb->transfer_dma);
|
|
usb_free_urb(urb);
|
|
}
|
|
}
|
|
#else
|
|
static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
|
|
{
|
|
}
|
|
|
|
static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev)
|
|
{
|
|
}
|
|
|
|
static inline int gspca_input_connect(struct gspca_dev *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* fill a video frame from an URB and resubmit
|
|
*/
|
|
static void fill_frame(struct gspca_dev *gspca_dev,
|
|
struct urb *urb)
|
|
{
|
|
u8 *data; /* address of data in the iso message */
|
|
int i, len, st;
|
|
cam_pkt_op pkt_scan;
|
|
|
|
if (urb->status != 0) {
|
|
if (urb->status == -ESHUTDOWN)
|
|
return; /* disconnection */
|
|
#ifdef CONFIG_PM
|
|
if (gspca_dev->frozen)
|
|
return;
|
|
#endif
|
|
gspca_err(gspca_dev, "urb status: %d\n", urb->status);
|
|
urb->status = 0;
|
|
goto resubmit;
|
|
}
|
|
pkt_scan = gspca_dev->sd_desc->pkt_scan;
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
len = urb->iso_frame_desc[i].actual_length;
|
|
|
|
/* check the packet status and length */
|
|
st = urb->iso_frame_desc[i].status;
|
|
if (st) {
|
|
gspca_dbg(gspca_dev, D_PACK, "ISOC data error: [%d] len=%d, status=%d\n",
|
|
i, len, st);
|
|
gspca_dev->last_packet_type = DISCARD_PACKET;
|
|
continue;
|
|
}
|
|
if (len == 0) {
|
|
if (gspca_dev->empty_packet == 0)
|
|
gspca_dev->empty_packet = 1;
|
|
continue;
|
|
}
|
|
|
|
/* let the packet be analyzed by the subdriver */
|
|
gspca_dbg(gspca_dev, D_PACK, "packet [%d] o:%d l:%d\n",
|
|
i, urb->iso_frame_desc[i].offset, len);
|
|
data = (u8 *) urb->transfer_buffer
|
|
+ urb->iso_frame_desc[i].offset;
|
|
pkt_scan(gspca_dev, data, len);
|
|
}
|
|
|
|
resubmit:
|
|
if (!gspca_dev->streaming)
|
|
return;
|
|
/* resubmit the URB */
|
|
st = usb_submit_urb(urb, GFP_ATOMIC);
|
|
if (st < 0)
|
|
pr_err("usb_submit_urb() ret %d\n", st);
|
|
}
|
|
|
|
/*
|
|
* ISOC message interrupt from the USB device
|
|
*
|
|
* Analyse each packet and call the subdriver for copy to the frame buffer.
|
|
*/
|
|
static void isoc_irq(struct urb *urb)
|
|
{
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
|
|
|
|
gspca_dbg(gspca_dev, D_PACK, "isoc irq\n");
|
|
if (!gspca_dev->streaming)
|
|
return;
|
|
fill_frame(gspca_dev, urb);
|
|
}
|
|
|
|
/*
|
|
* bulk message interrupt from the USB device
|
|
*/
|
|
static void bulk_irq(struct urb *urb)
|
|
{
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
|
|
int st;
|
|
|
|
gspca_dbg(gspca_dev, D_PACK, "bulk irq\n");
|
|
if (!gspca_dev->streaming)
|
|
return;
|
|
switch (urb->status) {
|
|
case 0:
|
|
break;
|
|
case -ESHUTDOWN:
|
|
return; /* disconnection */
|
|
default:
|
|
#ifdef CONFIG_PM
|
|
if (gspca_dev->frozen)
|
|
return;
|
|
#endif
|
|
gspca_err(gspca_dev, "urb status: %d\n", urb->status);
|
|
urb->status = 0;
|
|
goto resubmit;
|
|
}
|
|
|
|
gspca_dbg(gspca_dev, D_PACK, "packet l:%d\n", urb->actual_length);
|
|
gspca_dev->sd_desc->pkt_scan(gspca_dev,
|
|
urb->transfer_buffer,
|
|
urb->actual_length);
|
|
|
|
resubmit:
|
|
if (!gspca_dev->streaming)
|
|
return;
|
|
/* resubmit the URB */
|
|
if (gspca_dev->cam.bulk_nurbs != 0) {
|
|
st = usb_submit_urb(urb, GFP_ATOMIC);
|
|
if (st < 0)
|
|
pr_err("usb_submit_urb() ret %d\n", st);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* add data to the current frame
|
|
*
|
|
* This function is called by the subdrivers at interrupt level.
|
|
*
|
|
* To build a frame, these ones must add
|
|
* - one FIRST_PACKET
|
|
* - 0 or many INTER_PACKETs
|
|
* - one LAST_PACKET
|
|
* DISCARD_PACKET invalidates the whole frame.
|
|
*/
|
|
void gspca_frame_add(struct gspca_dev *gspca_dev,
|
|
enum gspca_packet_type packet_type,
|
|
const u8 *data,
|
|
int len)
|
|
{
|
|
struct gspca_buffer *buf;
|
|
unsigned long flags;
|
|
|
|
gspca_dbg(gspca_dev, D_PACK, "add t:%d l:%d\n", packet_type, len);
|
|
|
|
spin_lock_irqsave(&gspca_dev->qlock, flags);
|
|
buf = list_first_entry_or_null(&gspca_dev->buf_list,
|
|
typeof(*buf), list);
|
|
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
|
|
|
|
if (packet_type == FIRST_PACKET) {
|
|
/* if there is no queued buffer, discard the whole frame */
|
|
if (!buf) {
|
|
gspca_dev->last_packet_type = DISCARD_PACKET;
|
|
gspca_dev->sequence++;
|
|
return;
|
|
}
|
|
gspca_dev->image = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
|
|
gspca_dev->image_len = 0;
|
|
} else {
|
|
switch (gspca_dev->last_packet_type) {
|
|
case DISCARD_PACKET:
|
|
if (packet_type == LAST_PACKET) {
|
|
gspca_dev->last_packet_type = packet_type;
|
|
gspca_dev->image = NULL;
|
|
gspca_dev->image_len = 0;
|
|
}
|
|
return;
|
|
case LAST_PACKET:
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* append the packet to the frame buffer */
|
|
if (len > 0) {
|
|
if (gspca_dev->image_len + len > PAGE_ALIGN(gspca_dev->pixfmt.sizeimage)) {
|
|
gspca_err(gspca_dev, "frame overflow %d > %d\n",
|
|
gspca_dev->image_len + len,
|
|
PAGE_ALIGN(gspca_dev->pixfmt.sizeimage));
|
|
packet_type = DISCARD_PACKET;
|
|
} else {
|
|
/* !! image is NULL only when last pkt is LAST or DISCARD
|
|
if (gspca_dev->image == NULL) {
|
|
pr_err("gspca_frame_add() image == NULL\n");
|
|
return;
|
|
}
|
|
*/
|
|
memcpy(gspca_dev->image + gspca_dev->image_len,
|
|
data, len);
|
|
gspca_dev->image_len += len;
|
|
}
|
|
}
|
|
gspca_dev->last_packet_type = packet_type;
|
|
|
|
/* if last packet, invalidate packet concatenation until
|
|
* next first packet, wake up the application and advance
|
|
* in the queue */
|
|
if (packet_type == LAST_PACKET) {
|
|
spin_lock_irqsave(&gspca_dev->qlock, flags);
|
|
list_del(&buf->list);
|
|
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
|
|
buf->vb.vb2_buf.timestamp = ktime_get_ns();
|
|
vb2_set_plane_payload(&buf->vb.vb2_buf, 0,
|
|
gspca_dev->image_len);
|
|
buf->vb.sequence = gspca_dev->sequence++;
|
|
buf->vb.field = V4L2_FIELD_NONE;
|
|
gspca_dbg(gspca_dev, D_FRAM, "frame complete len:%d\n",
|
|
gspca_dev->image_len);
|
|
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
|
gspca_dev->image = NULL;
|
|
gspca_dev->image_len = 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(gspca_frame_add);
|
|
|
|
static void destroy_urbs(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct urb *urb;
|
|
unsigned int i;
|
|
|
|
gspca_dbg(gspca_dev, D_STREAM, "kill transfer\n");
|
|
|
|
/* Killing all URBs guarantee that no URB completion
|
|
* handler is running. Therefore, there shouldn't
|
|
* be anyone trying to access gspca_dev->urb[i]
|
|
*/
|
|
for (i = 0; i < MAX_NURBS; i++)
|
|
usb_kill_urb(gspca_dev->urb[i]);
|
|
|
|
gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n");
|
|
for (i = 0; i < MAX_NURBS; i++) {
|
|
urb = gspca_dev->urb[i];
|
|
if (!urb)
|
|
continue;
|
|
gspca_dev->urb[i] = NULL;
|
|
usb_free_coherent(gspca_dev->dev,
|
|
urb->transfer_buffer_length,
|
|
urb->transfer_buffer,
|
|
urb->transfer_dma);
|
|
usb_free_urb(urb);
|
|
}
|
|
}
|
|
|
|
static int gspca_set_alt0(struct gspca_dev *gspca_dev)
|
|
{
|
|
int ret;
|
|
|
|
if (gspca_dev->alt == 0)
|
|
return 0;
|
|
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
|
|
if (ret < 0)
|
|
pr_err("set alt 0 err %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* look for an input transfer endpoint in an alternate setting.
|
|
*
|
|
* If xfer_ep is invalid, return the first valid ep found, otherwise
|
|
* look for exactly the ep with address equal to xfer_ep.
|
|
*/
|
|
static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
|
|
int xfer, int xfer_ep)
|
|
{
|
|
struct usb_host_endpoint *ep;
|
|
int i, attr;
|
|
|
|
for (i = 0; i < alt->desc.bNumEndpoints; i++) {
|
|
ep = &alt->endpoint[i];
|
|
attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
|
if (attr == xfer
|
|
&& ep->desc.wMaxPacketSize != 0
|
|
&& usb_endpoint_dir_in(&ep->desc)
|
|
&& (xfer_ep < 0 || ep->desc.bEndpointAddress == xfer_ep))
|
|
return ep;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* compute the minimum bandwidth for the current transfer */
|
|
static u32 which_bandwidth(struct gspca_dev *gspca_dev)
|
|
{
|
|
u32 bandwidth;
|
|
|
|
/* get the (max) image size */
|
|
bandwidth = gspca_dev->pixfmt.sizeimage;
|
|
|
|
/* if the image is compressed, estimate its mean size */
|
|
if (!gspca_dev->cam.needs_full_bandwidth &&
|
|
bandwidth < gspca_dev->pixfmt.width *
|
|
gspca_dev->pixfmt.height)
|
|
bandwidth = bandwidth * 3 / 8; /* 0.375 */
|
|
|
|
/* estimate the frame rate */
|
|
if (gspca_dev->sd_desc->get_streamparm) {
|
|
struct v4l2_streamparm parm;
|
|
|
|
gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
|
|
bandwidth *= parm.parm.capture.timeperframe.denominator;
|
|
bandwidth /= parm.parm.capture.timeperframe.numerator;
|
|
} else {
|
|
|
|
/* don't hope more than 15 fps with USB 1.1 and
|
|
* image resolution >= 640x480 */
|
|
if (gspca_dev->pixfmt.width >= 640
|
|
&& gspca_dev->dev->speed == USB_SPEED_FULL)
|
|
bandwidth *= 15; /* 15 fps */
|
|
else
|
|
bandwidth *= 30; /* 30 fps */
|
|
}
|
|
|
|
gspca_dbg(gspca_dev, D_STREAM, "min bandwidth: %d\n", bandwidth);
|
|
return bandwidth;
|
|
}
|
|
|
|
/* endpoint table */
|
|
#define MAX_ALT 16
|
|
struct ep_tb_s {
|
|
u32 alt;
|
|
u32 bandwidth;
|
|
};
|
|
|
|
/*
|
|
* build the table of the endpoints
|
|
* and compute the minimum bandwidth for the image transfer
|
|
*/
|
|
static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
|
|
struct usb_interface *intf,
|
|
struct ep_tb_s *ep_tb)
|
|
{
|
|
struct usb_host_endpoint *ep;
|
|
int i, j, nbalt, psize, found;
|
|
u32 bandwidth, last_bw;
|
|
|
|
nbalt = intf->num_altsetting;
|
|
if (nbalt > MAX_ALT)
|
|
nbalt = MAX_ALT; /* fixme: should warn */
|
|
|
|
/* build the endpoint table */
|
|
i = 0;
|
|
last_bw = 0;
|
|
for (;;) {
|
|
ep_tb->bandwidth = 2000 * 2000 * 120;
|
|
found = 0;
|
|
for (j = 0; j < nbalt; j++) {
|
|
ep = alt_xfer(&intf->altsetting[j],
|
|
USB_ENDPOINT_XFER_ISOC,
|
|
gspca_dev->xfer_ep);
|
|
if (ep == NULL)
|
|
continue;
|
|
if (ep->desc.bInterval == 0) {
|
|
pr_err("alt %d iso endp with 0 interval\n", j);
|
|
continue;
|
|
}
|
|
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
|
|
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
|
|
bandwidth = psize * 1000;
|
|
if (gspca_dev->dev->speed == USB_SPEED_HIGH
|
|
|| gspca_dev->dev->speed >= USB_SPEED_SUPER)
|
|
bandwidth *= 8;
|
|
bandwidth /= 1 << (ep->desc.bInterval - 1);
|
|
if (bandwidth <= last_bw)
|
|
continue;
|
|
if (bandwidth < ep_tb->bandwidth) {
|
|
ep_tb->bandwidth = bandwidth;
|
|
ep_tb->alt = j;
|
|
found = 1;
|
|
}
|
|
}
|
|
if (!found)
|
|
break;
|
|
gspca_dbg(gspca_dev, D_STREAM, "alt %d bandwidth %d\n",
|
|
ep_tb->alt, ep_tb->bandwidth);
|
|
last_bw = ep_tb->bandwidth;
|
|
i++;
|
|
ep_tb++;
|
|
}
|
|
|
|
/*
|
|
* If the camera:
|
|
* has a usb audio class interface (a built in usb mic); and
|
|
* is a usb 1 full speed device; and
|
|
* uses the max full speed iso bandwidth; and
|
|
* and has more than 1 alt setting
|
|
* then skip the highest alt setting to spare bandwidth for the mic
|
|
*/
|
|
if (gspca_dev->audio &&
|
|
gspca_dev->dev->speed == USB_SPEED_FULL &&
|
|
last_bw >= 1000000 &&
|
|
i > 1) {
|
|
gspca_dbg(gspca_dev, D_STREAM, "dev has usb audio, skipping highest alt\n");
|
|
i--;
|
|
ep_tb--;
|
|
}
|
|
|
|
/* get the requested bandwidth and start at the highest atlsetting */
|
|
bandwidth = which_bandwidth(gspca_dev);
|
|
ep_tb--;
|
|
while (i > 1) {
|
|
ep_tb--;
|
|
if (ep_tb->bandwidth < bandwidth)
|
|
break;
|
|
i--;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* create the URBs for image transfer
|
|
*/
|
|
static int create_urbs(struct gspca_dev *gspca_dev,
|
|
struct usb_host_endpoint *ep)
|
|
{
|
|
struct urb *urb;
|
|
int n, nurbs, i, psize, npkt, bsize;
|
|
|
|
/* calculate the packet size and the number of packets */
|
|
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
|
|
|
|
if (!gspca_dev->cam.bulk) { /* isoc */
|
|
|
|
/* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
|
|
if (gspca_dev->pkt_size == 0)
|
|
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
|
|
else
|
|
psize = gspca_dev->pkt_size;
|
|
npkt = gspca_dev->cam.npkt;
|
|
if (npkt == 0)
|
|
npkt = 32; /* default value */
|
|
bsize = psize * npkt;
|
|
gspca_dbg(gspca_dev, D_STREAM,
|
|
"isoc %d pkts size %d = bsize:%d\n",
|
|
npkt, psize, bsize);
|
|
nurbs = DEF_NURBS;
|
|
} else { /* bulk */
|
|
npkt = 0;
|
|
bsize = gspca_dev->cam.bulk_size;
|
|
if (bsize == 0)
|
|
bsize = psize;
|
|
gspca_dbg(gspca_dev, D_STREAM, "bulk bsize:%d\n", bsize);
|
|
if (gspca_dev->cam.bulk_nurbs != 0)
|
|
nurbs = gspca_dev->cam.bulk_nurbs;
|
|
else
|
|
nurbs = 1;
|
|
}
|
|
|
|
for (n = 0; n < nurbs; n++) {
|
|
urb = usb_alloc_urb(npkt, GFP_KERNEL);
|
|
if (!urb)
|
|
return -ENOMEM;
|
|
gspca_dev->urb[n] = urb;
|
|
urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
|
|
bsize,
|
|
GFP_KERNEL,
|
|
&urb->transfer_dma);
|
|
|
|
if (urb->transfer_buffer == NULL) {
|
|
pr_err("usb_alloc_coherent failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
urb->dev = gspca_dev->dev;
|
|
urb->context = gspca_dev;
|
|
urb->transfer_buffer_length = bsize;
|
|
if (npkt != 0) { /* ISOC */
|
|
urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
|
|
ep->desc.bEndpointAddress);
|
|
urb->transfer_flags = URB_ISO_ASAP
|
|
| URB_NO_TRANSFER_DMA_MAP;
|
|
urb->interval = 1 << (ep->desc.bInterval - 1);
|
|
urb->complete = isoc_irq;
|
|
urb->number_of_packets = npkt;
|
|
for (i = 0; i < npkt; i++) {
|
|
urb->iso_frame_desc[i].length = psize;
|
|
urb->iso_frame_desc[i].offset = psize * i;
|
|
}
|
|
} else { /* bulk */
|
|
urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
|
|
ep->desc.bEndpointAddress);
|
|
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
|
urb->complete = bulk_irq;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Note: both the queue and the usb locks should be held when calling this */
|
|
static void gspca_stream_off(struct gspca_dev *gspca_dev)
|
|
{
|
|
gspca_dev->streaming = false;
|
|
gspca_dev->usb_err = 0;
|
|
if (gspca_dev->sd_desc->stopN)
|
|
gspca_dev->sd_desc->stopN(gspca_dev);
|
|
destroy_urbs(gspca_dev);
|
|
gspca_input_destroy_urb(gspca_dev);
|
|
gspca_set_alt0(gspca_dev);
|
|
if (gspca_dev->present)
|
|
gspca_input_create_urb(gspca_dev);
|
|
if (gspca_dev->sd_desc->stop0)
|
|
gspca_dev->sd_desc->stop0(gspca_dev);
|
|
gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n");
|
|
}
|
|
|
|
/*
|
|
* start the USB transfer
|
|
*/
|
|
static int gspca_init_transfer(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct usb_interface *intf;
|
|
struct usb_host_endpoint *ep;
|
|
struct urb *urb;
|
|
struct ep_tb_s ep_tb[MAX_ALT];
|
|
int n, ret, xfer, alt, alt_idx;
|
|
|
|
/* reset the streaming variables */
|
|
gspca_dev->image = NULL;
|
|
gspca_dev->image_len = 0;
|
|
gspca_dev->last_packet_type = DISCARD_PACKET;
|
|
|
|
gspca_dev->usb_err = 0;
|
|
|
|
/* do the specific subdriver stuff before endpoint selection */
|
|
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
|
|
gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0;
|
|
if (gspca_dev->sd_desc->isoc_init) {
|
|
ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
|
|
: USB_ENDPOINT_XFER_ISOC;
|
|
|
|
/* if bulk or the subdriver forced an altsetting, get the endpoint */
|
|
if (gspca_dev->alt != 0) {
|
|
gspca_dev->alt--; /* (previous version compatibility) */
|
|
ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer,
|
|
gspca_dev->xfer_ep);
|
|
if (ep == NULL) {
|
|
pr_err("bad altsetting %d\n", gspca_dev->alt);
|
|
return -EIO;
|
|
}
|
|
ep_tb[0].alt = gspca_dev->alt;
|
|
alt_idx = 1;
|
|
} else {
|
|
/* else, compute the minimum bandwidth
|
|
* and build the endpoint table */
|
|
alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
|
|
if (alt_idx <= 0) {
|
|
pr_err("no transfer endpoint found\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
/* set the highest alternate setting and
|
|
* loop until urb submit succeeds */
|
|
gspca_input_destroy_urb(gspca_dev);
|
|
|
|
gspca_dev->alt = ep_tb[--alt_idx].alt;
|
|
alt = -1;
|
|
for (;;) {
|
|
if (alt != gspca_dev->alt) {
|
|
alt = gspca_dev->alt;
|
|
if (intf->num_altsetting > 1) {
|
|
ret = usb_set_interface(gspca_dev->dev,
|
|
gspca_dev->iface,
|
|
alt);
|
|
if (ret < 0) {
|
|
if (ret == -ENOSPC)
|
|
goto retry; /*fixme: ugly*/
|
|
pr_err("set alt %d err %d\n", alt, ret);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
if (!gspca_dev->cam.no_urb_create) {
|
|
gspca_dbg(gspca_dev, D_STREAM, "init transfer alt %d\n",
|
|
alt);
|
|
ret = create_urbs(gspca_dev,
|
|
alt_xfer(&intf->altsetting[alt], xfer,
|
|
gspca_dev->xfer_ep));
|
|
if (ret < 0) {
|
|
destroy_urbs(gspca_dev);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* clear the bulk endpoint */
|
|
if (gspca_dev->cam.bulk)
|
|
usb_clear_halt(gspca_dev->dev,
|
|
gspca_dev->urb[0]->pipe);
|
|
|
|
/* start the cam */
|
|
ret = gspca_dev->sd_desc->start(gspca_dev);
|
|
if (ret < 0) {
|
|
destroy_urbs(gspca_dev);
|
|
goto out;
|
|
}
|
|
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
|
|
gspca_dev->streaming = true;
|
|
|
|
/* some bulk transfers are started by the subdriver */
|
|
if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
|
|
break;
|
|
|
|
/* submit the URBs */
|
|
for (n = 0; n < MAX_NURBS; n++) {
|
|
urb = gspca_dev->urb[n];
|
|
if (urb == NULL)
|
|
break;
|
|
ret = usb_submit_urb(urb, GFP_KERNEL);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
if (ret >= 0)
|
|
break; /* transfer is started */
|
|
|
|
/* something when wrong
|
|
* stop the webcam and free the transfer resources */
|
|
gspca_stream_off(gspca_dev);
|
|
if (ret != -ENOSPC) {
|
|
pr_err("usb_submit_urb alt %d err %d\n",
|
|
gspca_dev->alt, ret);
|
|
goto out;
|
|
}
|
|
|
|
/* the bandwidth is not wide enough
|
|
* negotiate or try a lower alternate setting */
|
|
retry:
|
|
gspca_err(gspca_dev, "alt %d - bandwidth not wide enough, trying again\n",
|
|
alt);
|
|
msleep(20); /* wait for kill complete */
|
|
if (gspca_dev->sd_desc->isoc_nego) {
|
|
ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
|
|
if (ret < 0)
|
|
goto out;
|
|
} else {
|
|
if (alt_idx <= 0) {
|
|
pr_err("no transfer endpoint found\n");
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
gspca_dev->alt = ep_tb[--alt_idx].alt;
|
|
}
|
|
}
|
|
out:
|
|
gspca_input_create_urb(gspca_dev);
|
|
return ret;
|
|
}
|
|
|
|
static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
|
|
{
|
|
int i;
|
|
|
|
i = gspca_dev->cam.nmodes - 1; /* take the highest mode */
|
|
gspca_dev->curr_mode = i;
|
|
gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i];
|
|
|
|
/* does nothing if ctrl_handler == NULL */
|
|
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
|
|
}
|
|
|
|
static int wxh_to_mode(struct gspca_dev *gspca_dev,
|
|
int width, int height, u32 pixelformat)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < gspca_dev->cam.nmodes; i++) {
|
|
if (width == gspca_dev->cam.cam_mode[i].width
|
|
&& height == gspca_dev->cam.cam_mode[i].height
|
|
&& pixelformat == gspca_dev->cam.cam_mode[i].pixelformat)
|
|
return i;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev,
|
|
int width, int height, u32 pixelformat)
|
|
{
|
|
int i;
|
|
|
|
for (i = gspca_dev->cam.nmodes; --i > 0; ) {
|
|
if (width >= gspca_dev->cam.cam_mode[i].width
|
|
&& height >= gspca_dev->cam.cam_mode[i].height
|
|
&& pixelformat == gspca_dev->cam.cam_mode[i].pixelformat)
|
|
return i;
|
|
}
|
|
for (i = gspca_dev->cam.nmodes; --i > 0; ) {
|
|
if (width >= gspca_dev->cam.cam_mode[i].width
|
|
&& height >= gspca_dev->cam.cam_mode[i].height)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* search a mode with the right pixel format
|
|
*/
|
|
static int gspca_get_mode(struct gspca_dev *gspca_dev,
|
|
int mode,
|
|
int pixfmt)
|
|
{
|
|
int modeU, modeD;
|
|
|
|
modeU = modeD = mode;
|
|
while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) {
|
|
if (--modeD >= 0) {
|
|
if (gspca_dev->cam.cam_mode[modeD].pixelformat
|
|
== pixfmt)
|
|
return modeD;
|
|
}
|
|
if (++modeU < gspca_dev->cam.nmodes) {
|
|
if (gspca_dev->cam.cam_mode[modeU].pixelformat
|
|
== pixfmt)
|
|
return modeU;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
static int vidioc_g_chip_info(struct file *file, void *priv,
|
|
struct v4l2_dbg_chip_info *chip)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
gspca_dev->usb_err = 0;
|
|
if (gspca_dev->sd_desc->get_chip_info)
|
|
return gspca_dev->sd_desc->get_chip_info(gspca_dev, chip);
|
|
return chip->match.addr ? -EINVAL : 0;
|
|
}
|
|
|
|
static int vidioc_g_register(struct file *file, void *priv,
|
|
struct v4l2_dbg_register *reg)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
gspca_dev->usb_err = 0;
|
|
return gspca_dev->sd_desc->get_register(gspca_dev, reg);
|
|
}
|
|
|
|
static int vidioc_s_register(struct file *file, void *priv,
|
|
const struct v4l2_dbg_register *reg)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
gspca_dev->usb_err = 0;
|
|
return gspca_dev->sd_desc->set_register(gspca_dev, reg);
|
|
}
|
|
#endif
|
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *fmtdesc)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
int i, j, index;
|
|
__u32 fmt_tb[8];
|
|
|
|
/* give an index to each format */
|
|
index = 0;
|
|
for (i = gspca_dev->cam.nmodes; --i >= 0; ) {
|
|
fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat;
|
|
j = 0;
|
|
for (;;) {
|
|
if (fmt_tb[j] == fmt_tb[index])
|
|
break;
|
|
j++;
|
|
}
|
|
if (j == index) {
|
|
if (fmtdesc->index == index)
|
|
break; /* new format */
|
|
index++;
|
|
if (index >= ARRAY_SIZE(fmt_tb))
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (i < 0)
|
|
return -EINVAL; /* no more format */
|
|
|
|
fmtdesc->pixelformat = fmt_tb[index];
|
|
if (gspca_dev->cam.cam_mode[i].sizeimage <
|
|
gspca_dev->cam.cam_mode[i].width *
|
|
gspca_dev->cam.cam_mode[i].height)
|
|
fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
|
|
fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
|
|
fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
|
|
fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
|
|
fmtdesc->description[3] = fmtdesc->pixelformat >> 24;
|
|
fmtdesc->description[4] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_format *fmt)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
fmt->fmt.pix = gspca_dev->pixfmt;
|
|
/* some drivers use priv internally, zero it before giving it back to
|
|
the core */
|
|
fmt->fmt.pix.priv = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
|
|
struct v4l2_format *fmt)
|
|
{
|
|
int w, h, mode, mode2;
|
|
|
|
w = fmt->fmt.pix.width;
|
|
h = fmt->fmt.pix.height;
|
|
|
|
PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap",
|
|
fmt->fmt.pix.pixelformat, w, h);
|
|
|
|
/* search the nearest mode for width and height */
|
|
mode = wxh_to_nearest_mode(gspca_dev, w, h, fmt->fmt.pix.pixelformat);
|
|
|
|
/* OK if right palette */
|
|
if (gspca_dev->cam.cam_mode[mode].pixelformat
|
|
!= fmt->fmt.pix.pixelformat) {
|
|
|
|
/* else, search the closest mode with the same pixel format */
|
|
mode2 = gspca_get_mode(gspca_dev, mode,
|
|
fmt->fmt.pix.pixelformat);
|
|
if (mode2 >= 0)
|
|
mode = mode2;
|
|
}
|
|
fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
|
|
if (gspca_dev->sd_desc->try_fmt) {
|
|
/* pass original resolution to subdriver try_fmt */
|
|
fmt->fmt.pix.width = w;
|
|
fmt->fmt.pix.height = h;
|
|
gspca_dev->sd_desc->try_fmt(gspca_dev, fmt);
|
|
}
|
|
/* some drivers use priv internally, zero it before giving it back to
|
|
the core */
|
|
fmt->fmt.pix.priv = 0;
|
|
return mode; /* used when s_fmt */
|
|
}
|
|
|
|
static int vidioc_try_fmt_vid_cap(struct file *file,
|
|
void *priv,
|
|
struct v4l2_format *fmt)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
if (try_fmt_vid_cap(gspca_dev, fmt) < 0)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_format *fmt)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
int mode;
|
|
|
|
if (vb2_is_busy(&gspca_dev->queue))
|
|
return -EBUSY;
|
|
|
|
mode = try_fmt_vid_cap(gspca_dev, fmt);
|
|
if (mode < 0)
|
|
return -EINVAL;
|
|
|
|
gspca_dev->curr_mode = mode;
|
|
if (gspca_dev->sd_desc->try_fmt)
|
|
/* subdriver try_fmt can modify format parameters */
|
|
gspca_dev->pixfmt = fmt->fmt.pix;
|
|
else
|
|
gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode];
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_enum_framesizes(struct file *file, void *priv,
|
|
struct v4l2_frmsizeenum *fsize)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
int i;
|
|
__u32 index = 0;
|
|
|
|
if (gspca_dev->sd_desc->enum_framesizes)
|
|
return gspca_dev->sd_desc->enum_framesizes(gspca_dev, fsize);
|
|
|
|
for (i = 0; i < gspca_dev->cam.nmodes; i++) {
|
|
if (fsize->pixel_format !=
|
|
gspca_dev->cam.cam_mode[i].pixelformat)
|
|
continue;
|
|
|
|
if (fsize->index == index) {
|
|
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
|
|
fsize->discrete.width =
|
|
gspca_dev->cam.cam_mode[i].width;
|
|
fsize->discrete.height =
|
|
gspca_dev->cam.cam_mode[i].height;
|
|
return 0;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int vidioc_enum_frameintervals(struct file *filp, void *priv,
|
|
struct v4l2_frmivalenum *fival)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(filp);
|
|
int mode;
|
|
__u32 i;
|
|
|
|
mode = wxh_to_mode(gspca_dev, fival->width, fival->height,
|
|
fival->pixel_format);
|
|
if (mode < 0)
|
|
return -EINVAL;
|
|
|
|
if (gspca_dev->cam.mode_framerates == NULL ||
|
|
gspca_dev->cam.mode_framerates[mode].nrates == 0)
|
|
return -EINVAL;
|
|
|
|
if (fival->pixel_format !=
|
|
gspca_dev->cam.cam_mode[mode].pixelformat)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
|
|
if (fival->index == i) {
|
|
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
|
|
fival->discrete.numerator = 1;
|
|
fival->discrete.denominator =
|
|
gspca_dev->cam.mode_framerates[mode].rates[i];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void gspca_release(struct v4l2_device *v4l2_device)
|
|
{
|
|
struct gspca_dev *gspca_dev =
|
|
container_of(v4l2_device, struct gspca_dev, v4l2_dev);
|
|
|
|
v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
|
|
v4l2_device_unregister(&gspca_dev->v4l2_dev);
|
|
kfree(gspca_dev->usb_buf);
|
|
kfree(gspca_dev);
|
|
}
|
|
|
|
static int vidioc_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
strscpy((char *)cap->driver, gspca_dev->sd_desc->name,
|
|
sizeof(cap->driver));
|
|
if (gspca_dev->dev->product != NULL) {
|
|
strscpy((char *)cap->card, gspca_dev->dev->product,
|
|
sizeof(cap->card));
|
|
} else {
|
|
snprintf((char *) cap->card, sizeof cap->card,
|
|
"USB Camera (%04x:%04x)",
|
|
le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
|
|
le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
|
|
}
|
|
usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
|
|
sizeof(cap->bus_info));
|
|
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
|
|
| V4L2_CAP_STREAMING
|
|
| V4L2_CAP_READWRITE;
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_enum_input(struct file *file, void *priv,
|
|
struct v4l2_input *input)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
if (input->index != 0)
|
|
return -EINVAL;
|
|
input->type = V4L2_INPUT_TYPE_CAMERA;
|
|
input->status = gspca_dev->cam.input_flags;
|
|
strscpy(input->name, gspca_dev->sd_desc->name,
|
|
sizeof input->name);
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
|
|
{
|
|
*i = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
|
|
{
|
|
if (i > 0)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_g_jpegcomp(struct file *file, void *priv,
|
|
struct v4l2_jpegcompression *jpegcomp)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
gspca_dev->usb_err = 0;
|
|
return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
|
|
}
|
|
|
|
static int vidioc_s_jpegcomp(struct file *file, void *priv,
|
|
const struct v4l2_jpegcompression *jpegcomp)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(file);
|
|
|
|
gspca_dev->usb_err = 0;
|
|
return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
|
|
}
|
|
|
|
static int vidioc_g_parm(struct file *filp, void *priv,
|
|
struct v4l2_streamparm *parm)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(filp);
|
|
|
|
parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed;
|
|
|
|
if (!gspca_dev->sd_desc->get_streamparm)
|
|
return 0;
|
|
|
|
parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
|
|
gspca_dev->usb_err = 0;
|
|
gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
|
|
return gspca_dev->usb_err;
|
|
}
|
|
|
|
static int vidioc_s_parm(struct file *filp, void *priv,
|
|
struct v4l2_streamparm *parm)
|
|
{
|
|
struct gspca_dev *gspca_dev = video_drvdata(filp);
|
|
|
|
parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed;
|
|
|
|
if (!gspca_dev->sd_desc->set_streamparm) {
|
|
parm->parm.capture.capability = 0;
|
|
return 0;
|
|
}
|
|
|
|
parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
|
|
gspca_dev->usb_err = 0;
|
|
gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
|
|
return gspca_dev->usb_err;
|
|
}
|
|
|
|
static int gspca_queue_setup(struct vb2_queue *vq,
|
|
unsigned int *nbuffers, unsigned int *nplanes,
|
|
unsigned int sizes[], struct device *alloc_devs[])
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
|
|
unsigned int size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage);
|
|
|
|
if (*nplanes)
|
|
return sizes[0] < size ? -EINVAL : 0;
|
|
*nplanes = 1;
|
|
sizes[0] = size;
|
|
return 0;
|
|
}
|
|
|
|
static int gspca_buffer_prepare(struct vb2_buffer *vb)
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
unsigned long size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage);
|
|
|
|
if (vb2_plane_size(vb, 0) < size) {
|
|
gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n",
|
|
vb2_plane_size(vb, 0), size);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void gspca_buffer_finish(struct vb2_buffer *vb)
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
|
|
if (!gspca_dev->sd_desc->dq_callback)
|
|
return;
|
|
|
|
gspca_dev->usb_err = 0;
|
|
if (gspca_dev->present)
|
|
gspca_dev->sd_desc->dq_callback(gspca_dev);
|
|
}
|
|
|
|
static void gspca_buffer_queue(struct vb2_buffer *vb)
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct gspca_buffer *buf = to_gspca_buffer(vb);
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gspca_dev->qlock, flags);
|
|
list_add_tail(&buf->list, &gspca_dev->buf_list);
|
|
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
|
|
}
|
|
|
|
static void gspca_return_all_buffers(struct gspca_dev *gspca_dev,
|
|
enum vb2_buffer_state state)
|
|
{
|
|
struct gspca_buffer *buf, *node;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gspca_dev->qlock, flags);
|
|
list_for_each_entry_safe(buf, node, &gspca_dev->buf_list, list) {
|
|
vb2_buffer_done(&buf->vb.vb2_buf, state);
|
|
list_del(&buf->list);
|
|
}
|
|
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
|
|
}
|
|
|
|
static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
|
|
int ret;
|
|
|
|
gspca_dev->sequence = 0;
|
|
|
|
ret = gspca_init_transfer(gspca_dev);
|
|
if (ret)
|
|
gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED);
|
|
return ret;
|
|
}
|
|
|
|
static void gspca_stop_streaming(struct vb2_queue *vq)
|
|
{
|
|
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
|
|
|
|
gspca_stream_off(gspca_dev);
|
|
|
|
/* Release all active buffers */
|
|
gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR);
|
|
}
|
|
|
|
static const struct vb2_ops gspca_qops = {
|
|
.queue_setup = gspca_queue_setup,
|
|
.buf_prepare = gspca_buffer_prepare,
|
|
.buf_finish = gspca_buffer_finish,
|
|
.buf_queue = gspca_buffer_queue,
|
|
.start_streaming = gspca_start_streaming,
|
|
.stop_streaming = gspca_stop_streaming,
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
};
|
|
|
|
static const struct v4l2_file_operations dev_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = v4l2_fh_open,
|
|
.release = vb2_fop_release,
|
|
.unlocked_ioctl = video_ioctl2,
|
|
.read = vb2_fop_read,
|
|
.mmap = vb2_fop_mmap,
|
|
.poll = vb2_fop_poll,
|
|
};
|
|
|
|
static const struct v4l2_ioctl_ops dev_ioctl_ops = {
|
|
.vidioc_querycap = vidioc_querycap,
|
|
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
.vidioc_enum_input = vidioc_enum_input,
|
|
.vidioc_g_input = vidioc_g_input,
|
|
.vidioc_s_input = vidioc_s_input,
|
|
.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
|
|
.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
|
|
.vidioc_g_parm = vidioc_g_parm,
|
|
.vidioc_s_parm = vidioc_s_parm,
|
|
.vidioc_enum_framesizes = vidioc_enum_framesizes,
|
|
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
|
|
|
|
.vidioc_reqbufs = vb2_ioctl_reqbufs,
|
|
.vidioc_create_bufs = vb2_ioctl_create_bufs,
|
|
.vidioc_querybuf = vb2_ioctl_querybuf,
|
|
.vidioc_qbuf = vb2_ioctl_qbuf,
|
|
.vidioc_dqbuf = vb2_ioctl_dqbuf,
|
|
.vidioc_expbuf = vb2_ioctl_expbuf,
|
|
.vidioc_streamon = vb2_ioctl_streamon,
|
|
.vidioc_streamoff = vb2_ioctl_streamoff,
|
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
.vidioc_g_chip_info = vidioc_g_chip_info,
|
|
.vidioc_g_register = vidioc_g_register,
|
|
.vidioc_s_register = vidioc_s_register,
|
|
#endif
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
};
|
|
|
|
static const struct video_device gspca_template = {
|
|
.name = "gspca main driver",
|
|
.fops = &dev_fops,
|
|
.ioctl_ops = &dev_ioctl_ops,
|
|
.release = video_device_release_empty, /* We use v4l2_dev.release */
|
|
};
|
|
|
|
/*
|
|
* probe and create a new gspca device
|
|
*
|
|
* This function must be called by the sub-driver when it is
|
|
* called for probing a new device.
|
|
*/
|
|
int gspca_dev_probe2(struct usb_interface *intf,
|
|
const struct usb_device_id *id,
|
|
const struct sd_desc *sd_desc,
|
|
int dev_size,
|
|
struct module *module)
|
|
{
|
|
struct gspca_dev *gspca_dev;
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
|
struct vb2_queue *q;
|
|
int ret;
|
|
|
|
pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
|
|
sd_desc->name, id->idVendor, id->idProduct);
|
|
|
|
/* create the device */
|
|
if (dev_size < sizeof *gspca_dev)
|
|
dev_size = sizeof *gspca_dev;
|
|
gspca_dev = kzalloc(dev_size, GFP_KERNEL);
|
|
if (!gspca_dev) {
|
|
pr_err("couldn't kzalloc gspca struct\n");
|
|
return -ENOMEM;
|
|
}
|
|
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
|
|
if (!gspca_dev->usb_buf) {
|
|
pr_err("out of memory\n");
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
gspca_dev->dev = dev;
|
|
gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
|
|
gspca_dev->xfer_ep = -1;
|
|
|
|
/* check if any audio device */
|
|
if (dev->actconfig->desc.bNumInterfaces != 1) {
|
|
int i;
|
|
struct usb_interface *intf2;
|
|
|
|
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
|
|
intf2 = dev->actconfig->interface[i];
|
|
if (intf2 != NULL
|
|
&& intf2->altsetting != NULL
|
|
&& intf2->altsetting->desc.bInterfaceClass ==
|
|
USB_CLASS_AUDIO) {
|
|
gspca_dev->audio = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
gspca_dev->v4l2_dev.release = gspca_release;
|
|
ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
|
|
if (ret)
|
|
goto out;
|
|
gspca_dev->present = true;
|
|
gspca_dev->sd_desc = sd_desc;
|
|
gspca_dev->empty_packet = -1; /* don't check the empty packets */
|
|
gspca_dev->vdev = gspca_template;
|
|
gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
|
|
video_set_drvdata(&gspca_dev->vdev, gspca_dev);
|
|
gspca_dev->module = module;
|
|
|
|
mutex_init(&gspca_dev->usb_lock);
|
|
gspca_dev->vdev.lock = &gspca_dev->usb_lock;
|
|
init_waitqueue_head(&gspca_dev->wq);
|
|
|
|
/* Initialize the vb2 queue */
|
|
q = &gspca_dev->queue;
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
|
|
q->drv_priv = gspca_dev;
|
|
q->buf_struct_size = sizeof(struct gspca_buffer);
|
|
q->ops = &gspca_qops;
|
|
q->mem_ops = &vb2_vmalloc_memops;
|
|
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
|
q->min_buffers_needed = 2;
|
|
q->lock = &gspca_dev->usb_lock;
|
|
ret = vb2_queue_init(q);
|
|
if (ret)
|
|
goto out;
|
|
gspca_dev->vdev.queue = q;
|
|
|
|
INIT_LIST_HEAD(&gspca_dev->buf_list);
|
|
spin_lock_init(&gspca_dev->qlock);
|
|
|
|
/* configure the subdriver and initialize the USB device */
|
|
ret = sd_desc->config(gspca_dev, id);
|
|
if (ret < 0)
|
|
goto out;
|
|
ret = sd_desc->init(gspca_dev);
|
|
if (ret < 0)
|
|
goto out;
|
|
if (sd_desc->init_controls)
|
|
ret = sd_desc->init_controls(gspca_dev);
|
|
if (ret < 0)
|
|
goto out;
|
|
gspca_set_default_mode(gspca_dev);
|
|
|
|
ret = gspca_input_connect(gspca_dev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
if (!gspca_dev->sd_desc->get_register)
|
|
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
|
|
if (!gspca_dev->sd_desc->set_register)
|
|
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
|
|
#endif
|
|
if (!gspca_dev->sd_desc->get_jcomp)
|
|
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
|
|
if (!gspca_dev->sd_desc->set_jcomp)
|
|
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
|
|
|
|
/* init video stuff */
|
|
ret = video_register_device(&gspca_dev->vdev,
|
|
VFL_TYPE_GRABBER,
|
|
-1);
|
|
if (ret < 0) {
|
|
pr_err("video_register_device err %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
usb_set_intfdata(intf, gspca_dev);
|
|
gspca_dbg(gspca_dev, D_PROBE, "%s created\n",
|
|
video_device_node_name(&gspca_dev->vdev));
|
|
|
|
gspca_input_create_urb(gspca_dev);
|
|
|
|
return 0;
|
|
out:
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
if (gspca_dev->input_dev)
|
|
input_unregister_device(gspca_dev->input_dev);
|
|
#endif
|
|
v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
|
|
kfree(gspca_dev->usb_buf);
|
|
kfree(gspca_dev);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(gspca_dev_probe2);
|
|
|
|
/* same function as the previous one, but check the interface */
|
|
int gspca_dev_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *id,
|
|
const struct sd_desc *sd_desc,
|
|
int dev_size,
|
|
struct module *module)
|
|
{
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
|
|
|
/* we don't handle multi-config cameras */
|
|
if (dev->descriptor.bNumConfigurations != 1) {
|
|
pr_err("%04x:%04x too many config\n",
|
|
id->idVendor, id->idProduct);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* the USB video interface must be the first one */
|
|
if (dev->actconfig->desc.bNumInterfaces != 1
|
|
&& intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
|
return -ENODEV;
|
|
|
|
return gspca_dev_probe2(intf, id, sd_desc, dev_size, module);
|
|
}
|
|
EXPORT_SYMBOL(gspca_dev_probe);
|
|
|
|
/*
|
|
* USB disconnection
|
|
*
|
|
* This function must be called by the sub-driver
|
|
* when the device disconnects, after the specific resources are freed.
|
|
*/
|
|
void gspca_disconnect(struct usb_interface *intf)
|
|
{
|
|
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
struct input_dev *input_dev;
|
|
#endif
|
|
|
|
gspca_dbg(gspca_dev, D_PROBE, "%s disconnect\n",
|
|
video_device_node_name(&gspca_dev->vdev));
|
|
|
|
mutex_lock(&gspca_dev->usb_lock);
|
|
gspca_dev->present = false;
|
|
destroy_urbs(gspca_dev);
|
|
gspca_input_destroy_urb(gspca_dev);
|
|
|
|
vb2_queue_error(&gspca_dev->queue);
|
|
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
input_dev = gspca_dev->input_dev;
|
|
if (input_dev) {
|
|
gspca_dev->input_dev = NULL;
|
|
input_unregister_device(input_dev);
|
|
}
|
|
#endif
|
|
|
|
v4l2_device_disconnect(&gspca_dev->v4l2_dev);
|
|
video_unregister_device(&gspca_dev->vdev);
|
|
|
|
mutex_unlock(&gspca_dev->usb_lock);
|
|
|
|
/* (this will call gspca_release() immediately or on last close) */
|
|
v4l2_device_put(&gspca_dev->v4l2_dev);
|
|
}
|
|
EXPORT_SYMBOL(gspca_disconnect);
|
|
|
|
#ifdef CONFIG_PM
|
|
int gspca_suspend(struct usb_interface *intf, pm_message_t message)
|
|
{
|
|
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
|
|
|
gspca_input_destroy_urb(gspca_dev);
|
|
|
|
if (!vb2_start_streaming_called(&gspca_dev->queue))
|
|
return 0;
|
|
|
|
mutex_lock(&gspca_dev->usb_lock);
|
|
gspca_dev->frozen = 1; /* avoid urb error messages */
|
|
gspca_dev->usb_err = 0;
|
|
if (gspca_dev->sd_desc->stopN)
|
|
gspca_dev->sd_desc->stopN(gspca_dev);
|
|
destroy_urbs(gspca_dev);
|
|
gspca_set_alt0(gspca_dev);
|
|
if (gspca_dev->sd_desc->stop0)
|
|
gspca_dev->sd_desc->stop0(gspca_dev);
|
|
mutex_unlock(&gspca_dev->usb_lock);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(gspca_suspend);
|
|
|
|
int gspca_resume(struct usb_interface *intf)
|
|
{
|
|
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
|
int streaming, ret = 0;
|
|
|
|
mutex_lock(&gspca_dev->usb_lock);
|
|
gspca_dev->frozen = 0;
|
|
gspca_dev->usb_err = 0;
|
|
gspca_dev->sd_desc->init(gspca_dev);
|
|
/*
|
|
* Most subdrivers send all ctrl values on sd_start and thus
|
|
* only write to the device registers on s_ctrl when streaming ->
|
|
* Clear streaming to avoid setting all ctrls twice.
|
|
*/
|
|
streaming = vb2_start_streaming_called(&gspca_dev->queue);
|
|
if (streaming)
|
|
ret = gspca_init_transfer(gspca_dev);
|
|
else
|
|
gspca_input_create_urb(gspca_dev);
|
|
mutex_unlock(&gspca_dev->usb_lock);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(gspca_resume);
|
|
#endif
|
|
|
|
/* -- module insert / remove -- */
|
|
static int __init gspca_init(void)
|
|
{
|
|
pr_info("v" GSPCA_VERSION " registered\n");
|
|
return 0;
|
|
}
|
|
static void __exit gspca_exit(void)
|
|
{
|
|
}
|
|
|
|
module_init(gspca_init);
|
|
module_exit(gspca_exit);
|
|
|
|
module_param_named(debug, gspca_debug, int, 0644);
|
|
MODULE_PARM_DESC(debug,
|
|
"1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo");
|