V4L/DVB: gspca - main: Add input support for interrupt endpoints.
Signed-off-by: Márton Németh <nm127@freemail.hu> Signed-off-by: Jean-Francois Moine <moinejf@free.fr> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
2abf6dd8e8
commit
0274d42e05
|
@ -3,6 +3,9 @@
|
|||
*
|
||||
* Copyright (C) 2008-2009 Jean-Francois 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
|
||||
|
@ -37,6 +40,9 @@
|
|||
|
||||
#include "gspca.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
/* global values */
|
||||
#define DEF_NURBS 3 /* default number of URBs */
|
||||
#if DEF_NURBS > MAX_NURBS
|
||||
|
@ -104,6 +110,182 @@ static const struct vm_operations_struct gspca_vm_ops = {
|
|||
.close = gspca_vm_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* Input and interrupt endpoint handling functions
|
||||
*/
|
||||
#ifdef 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) {
|
||||
PDEBUG(D_ERR, "Unknown packet received");
|
||||
}
|
||||
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:
|
||||
PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status);
|
||||
urb->status = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
PDEBUG(D_ERR, "Resubmit URB failed with error %i", 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) {
|
||||
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) {
|
||||
PDEBUG(D_ERR, "Input device registration failed "
|
||||
"with error %i", err);
|
||||
input_dev->dev.parent = NULL;
|
||||
input_free_device(input_dev);
|
||||
} else {
|
||||
dev->input_dev = input_dev;
|
||||
}
|
||||
} else
|
||||
err = -EINVAL;
|
||||
|
||||
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 = ep->wMaxPacketSize;
|
||||
interval = ep->bInterval;
|
||||
PDEBUG(D_PROBE, "found int in endpoint: 0x%x, "
|
||||
"buffer_len=%u, interval=%u",
|
||||
ep->bEndpointAddress, buffer_len, interval);
|
||||
|
||||
dev = gspca_dev->dev;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize,
|
||||
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);
|
||||
gspca_dev->int_urb = urb;
|
||||
ret = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
PDEBUG(D_ERR, "submit URB failed with error %i", ret);
|
||||
goto error_submit;
|
||||
}
|
||||
return ret;
|
||||
|
||||
error_submit:
|
||||
usb_buffer_free(dev,
|
||||
urb->transfer_buffer_length,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_dma);
|
||||
error_buffer:
|
||||
usb_free_urb(urb);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gspca_input_create_urb(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
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)) {
|
||||
|
||||
ret = alloc_and_submit_int_urb(gspca_dev, ep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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_buffer_free(gspca_dev->dev,
|
||||
urb->transfer_buffer_length,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define gspca_input_connect(gspca_dev) 0
|
||||
#define gspca_input_create_urb(gspca_dev) 0
|
||||
#define gspca_input_destroy_urb(gspca_dev)
|
||||
#endif
|
||||
|
||||
/* get the current input frame buffer */
|
||||
struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
|
@ -483,11 +665,13 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
|
|||
i, ep->desc.bEndpointAddress);
|
||||
gspca_dev->alt = i; /* memorize the current alt setting */
|
||||
if (gspca_dev->nbalt > 1) {
|
||||
gspca_input_destroy_urb(gspca_dev);
|
||||
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
|
||||
if (ret < 0) {
|
||||
err("set alt %d err %d", i, ret);
|
||||
return NULL;
|
||||
ep = NULL;
|
||||
}
|
||||
gspca_input_create_urb(gspca_dev);
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
@ -698,7 +882,9 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev)
|
|||
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);
|
||||
gspca_input_create_urb(gspca_dev);
|
||||
}
|
||||
|
||||
/* always call stop0 to free the subdriver's resources */
|
||||
|
@ -2121,6 +2307,11 @@ int gspca_dev_probe(struct usb_interface *intf,
|
|||
|
||||
usb_set_intfdata(intf, gspca_dev);
|
||||
PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
|
||||
|
||||
ret = gspca_input_connect(gspca_dev);
|
||||
if (ret == 0)
|
||||
ret = gspca_input_create_urb(gspca_dev);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
kfree(gspca_dev->usb_buf);
|
||||
|
@ -2138,6 +2329,7 @@ EXPORT_SYMBOL(gspca_dev_probe);
|
|||
void gspca_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
||||
struct input_dev *input_dev;
|
||||
|
||||
PDEBUG(D_PROBE, "%s disconnect",
|
||||
video_device_node_name(&gspca_dev->vdev));
|
||||
|
@ -2149,6 +2341,13 @@ void gspca_disconnect(struct usb_interface *intf)
|
|||
wake_up_interruptible(&gspca_dev->wq);
|
||||
}
|
||||
|
||||
gspca_input_destroy_urb(gspca_dev);
|
||||
input_dev = gspca_dev->input_dev;
|
||||
if (input_dev) {
|
||||
gspca_dev->input_dev = NULL;
|
||||
input_unregister_device(input_dev);
|
||||
}
|
||||
|
||||
/* the device is freed at exit of this function */
|
||||
gspca_dev->dev = NULL;
|
||||
mutex_unlock(&gspca_dev->usb_lock);
|
||||
|
@ -2174,6 +2373,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
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->sd_desc->stop0)
|
||||
gspca_dev->sd_desc->stop0(gspca_dev);
|
||||
|
@ -2187,6 +2387,7 @@ int gspca_resume(struct usb_interface *intf)
|
|||
|
||||
gspca_dev->frozen = 0;
|
||||
gspca_dev->sd_desc->init(gspca_dev);
|
||||
gspca_input_create_urb(gspca_dev);
|
||||
if (gspca_dev->streaming)
|
||||
return gspca_init_transfer(gspca_dev);
|
||||
return 0;
|
||||
|
|
|
@ -91,6 +91,9 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *,
|
|||
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
|
||||
u8 *data,
|
||||
int len);
|
||||
typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
|
||||
u8 *data,
|
||||
int len);
|
||||
|
||||
struct ctrl {
|
||||
struct v4l2_queryctrl qctrl;
|
||||
|
@ -126,6 +129,9 @@ struct sd_desc {
|
|||
cam_reg_op get_register;
|
||||
#endif
|
||||
cam_ident_op get_chip_ident;
|
||||
#ifdef CONFIG_INPUT
|
||||
cam_int_pkt_op int_pkt_scan;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* packet types when moving from iso buf to frame buf */
|
||||
|
@ -148,6 +154,10 @@ struct gspca_dev {
|
|||
struct module *module; /* subdriver handling the device */
|
||||
struct usb_device *dev;
|
||||
struct file *capt_file; /* file doing video capture */
|
||||
#ifdef CONFIG_INPUT
|
||||
struct input_dev *input_dev;
|
||||
char phys[64]; /* physical device path */
|
||||
#endif
|
||||
|
||||
struct cam cam; /* device information */
|
||||
const struct sd_desc *sd_desc; /* subdriver description */
|
||||
|
@ -157,6 +167,9 @@ struct gspca_dev {
|
|||
#define USB_BUF_SZ 64
|
||||
__u8 *usb_buf; /* buffer for USB exchanges */
|
||||
struct urb *urb[MAX_NURBS];
|
||||
#ifdef CONFIG_INPUT
|
||||
struct urb *int_urb;
|
||||
#endif
|
||||
|
||||
__u8 *frbuf; /* buffer for nframes */
|
||||
struct gspca_frame frame[GSPCA_MAX_FRAMES];
|
||||
|
|
Loading…
Reference in a new issue