linux/drivers/media/usb/gspca/gspca.c
Ezequiel Garcia 9b9ea7c2b5 media: gspca: Kill URBs on USB device disconnect
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>
2019-03-29 07:34:32 -04:00

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");