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
2 changed files with 215 additions and 1 deletions
|
@ -3,6 +3,9 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr)
|
* 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
|
* 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
|
* 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
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
@ -37,6 +40,9 @@
|
||||||
|
|
||||||
#include "gspca.h"
|
#include "gspca.h"
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/usb/input.h>
|
||||||
|
|
||||||
/* global values */
|
/* global values */
|
||||||
#define DEF_NURBS 3 /* default number of URBs */
|
#define DEF_NURBS 3 /* default number of URBs */
|
||||||
#if DEF_NURBS > MAX_NURBS
|
#if DEF_NURBS > MAX_NURBS
|
||||||
|
@ -104,6 +110,182 @@ static const struct vm_operations_struct gspca_vm_ops = {
|
||||||
.close = gspca_vm_close,
|
.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 */
|
/* get the current input frame buffer */
|
||||||
struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
|
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);
|
i, ep->desc.bEndpointAddress);
|
||||||
gspca_dev->alt = i; /* memorize the current alt setting */
|
gspca_dev->alt = i; /* memorize the current alt setting */
|
||||||
if (gspca_dev->nbalt > 1) {
|
if (gspca_dev->nbalt > 1) {
|
||||||
|
gspca_input_destroy_urb(gspca_dev);
|
||||||
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
|
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
err("set alt %d err %d", i, ret);
|
err("set alt %d err %d", i, ret);
|
||||||
return NULL;
|
ep = NULL;
|
||||||
}
|
}
|
||||||
|
gspca_input_create_urb(gspca_dev);
|
||||||
}
|
}
|
||||||
return ep;
|
return ep;
|
||||||
}
|
}
|
||||||
|
@ -698,7 +882,9 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev)
|
||||||
if (gspca_dev->sd_desc->stopN)
|
if (gspca_dev->sd_desc->stopN)
|
||||||
gspca_dev->sd_desc->stopN(gspca_dev);
|
gspca_dev->sd_desc->stopN(gspca_dev);
|
||||||
destroy_urbs(gspca_dev);
|
destroy_urbs(gspca_dev);
|
||||||
|
gspca_input_destroy_urb(gspca_dev);
|
||||||
gspca_set_alt0(gspca_dev);
|
gspca_set_alt0(gspca_dev);
|
||||||
|
gspca_input_create_urb(gspca_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* always call stop0 to free the subdriver's resources */
|
/* 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);
|
usb_set_intfdata(intf, gspca_dev);
|
||||||
PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
|
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;
|
return 0;
|
||||||
out:
|
out:
|
||||||
kfree(gspca_dev->usb_buf);
|
kfree(gspca_dev->usb_buf);
|
||||||
|
@ -2138,6 +2329,7 @@ EXPORT_SYMBOL(gspca_dev_probe);
|
||||||
void gspca_disconnect(struct usb_interface *intf)
|
void gspca_disconnect(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
|
||||||
PDEBUG(D_PROBE, "%s disconnect",
|
PDEBUG(D_PROBE, "%s disconnect",
|
||||||
video_device_node_name(&gspca_dev->vdev));
|
video_device_node_name(&gspca_dev->vdev));
|
||||||
|
@ -2149,6 +2341,13 @@ void gspca_disconnect(struct usb_interface *intf)
|
||||||
wake_up_interruptible(&gspca_dev->wq);
|
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 */
|
/* the device is freed at exit of this function */
|
||||||
gspca_dev->dev = NULL;
|
gspca_dev->dev = NULL;
|
||||||
mutex_unlock(&gspca_dev->usb_lock);
|
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)
|
if (gspca_dev->sd_desc->stopN)
|
||||||
gspca_dev->sd_desc->stopN(gspca_dev);
|
gspca_dev->sd_desc->stopN(gspca_dev);
|
||||||
destroy_urbs(gspca_dev);
|
destroy_urbs(gspca_dev);
|
||||||
|
gspca_input_destroy_urb(gspca_dev);
|
||||||
gspca_set_alt0(gspca_dev);
|
gspca_set_alt0(gspca_dev);
|
||||||
if (gspca_dev->sd_desc->stop0)
|
if (gspca_dev->sd_desc->stop0)
|
||||||
gspca_dev->sd_desc->stop0(gspca_dev);
|
gspca_dev->sd_desc->stop0(gspca_dev);
|
||||||
|
@ -2187,6 +2387,7 @@ int gspca_resume(struct usb_interface *intf)
|
||||||
|
|
||||||
gspca_dev->frozen = 0;
|
gspca_dev->frozen = 0;
|
||||||
gspca_dev->sd_desc->init(gspca_dev);
|
gspca_dev->sd_desc->init(gspca_dev);
|
||||||
|
gspca_input_create_urb(gspca_dev);
|
||||||
if (gspca_dev->streaming)
|
if (gspca_dev->streaming)
|
||||||
return gspca_init_transfer(gspca_dev);
|
return gspca_init_transfer(gspca_dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -91,6 +91,9 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *,
|
||||||
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
|
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
|
||||||
u8 *data,
|
u8 *data,
|
||||||
int len);
|
int len);
|
||||||
|
typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
|
||||||
|
u8 *data,
|
||||||
|
int len);
|
||||||
|
|
||||||
struct ctrl {
|
struct ctrl {
|
||||||
struct v4l2_queryctrl qctrl;
|
struct v4l2_queryctrl qctrl;
|
||||||
|
@ -126,6 +129,9 @@ struct sd_desc {
|
||||||
cam_reg_op get_register;
|
cam_reg_op get_register;
|
||||||
#endif
|
#endif
|
||||||
cam_ident_op get_chip_ident;
|
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 */
|
/* 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 module *module; /* subdriver handling the device */
|
||||||
struct usb_device *dev;
|
struct usb_device *dev;
|
||||||
struct file *capt_file; /* file doing video capture */
|
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 */
|
struct cam cam; /* device information */
|
||||||
const struct sd_desc *sd_desc; /* subdriver description */
|
const struct sd_desc *sd_desc; /* subdriver description */
|
||||||
|
@ -157,6 +167,9 @@ struct gspca_dev {
|
||||||
#define USB_BUF_SZ 64
|
#define USB_BUF_SZ 64
|
||||||
__u8 *usb_buf; /* buffer for USB exchanges */
|
__u8 *usb_buf; /* buffer for USB exchanges */
|
||||||
struct urb *urb[MAX_NURBS];
|
struct urb *urb[MAX_NURBS];
|
||||||
|
#ifdef CONFIG_INPUT
|
||||||
|
struct urb *int_urb;
|
||||||
|
#endif
|
||||||
|
|
||||||
__u8 *frbuf; /* buffer for nframes */
|
__u8 *frbuf; /* buffer for nframes */
|
||||||
struct gspca_frame frame[GSPCA_MAX_FRAMES];
|
struct gspca_frame frame[GSPCA_MAX_FRAMES];
|
||||||
|
|
Loading…
Reference in a new issue