[PATCH 09/18] lirc driver for the Streamzap PC Receiver

From: Jarod Wilson
Date: Tue Sep 09 2008 - 00:10:11 EST


Signed-off-by: Jarod Wilson <jarod@xxxxxxxxxx>
Signed-off-by: Janne Grunau <j@xxxxxxxxxx>
CC: Christoph Bartelmus <lirc@xxxxxxxxxxxx>
---
drivers/input/lirc/Kconfig | 7 +
drivers/input/lirc/Makefile | 1 +
drivers/input/lirc/lirc_streamzap.c | 795 +++++++++++++++++++++++++++++++++++
3 files changed, 803 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/lirc/lirc_streamzap.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 7e37b2d..c105ac6 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -68,4 +68,11 @@ config LIRC_SERIAL
help
Driver for Homebrew Serial Port Receivers

+config LIRC_STREAMZAP
+ tristate "Streamzap PC Receiver"
+ default n
+ depends on LIRC_DEV
+ help
+ Driver for the Streamzap PC Receiver
+
endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index f39fa06..b348110 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb.o
obj-$(CONFIG_LIRC_MCEUSB2) += lirc_mceusb2.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
+obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
diff --git a/drivers/input/lirc/lirc_streamzap.c b/drivers/input/lirc/lirc_streamzap.c
new file mode 100644
index 0000000..69865cb
--- /dev/null
+++ b/drivers/input/lirc/lirc_streamzap.c
@@ -0,0 +1,795 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@xxxxxxxxxxxx>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@xxxxxxxxx)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/version.h>
+
+#include <linux/autoconf.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION "1.28"
+#define DRIVER_NAME "lirc_streamzap"
+#define DRIVER_DESC "Streamzap Remote Control driver"
+
+/* ------------------------------------------------------------------ */
+
+static int debug;
+
+#define USB_STREAMZAP_VENDOR_ID 0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID 0x0000
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
+ fmt "\n", ## args); \
+ } while (0)
+
+/*
+ * table of devices that work with this driver
+ */
+static struct usb_device_id streamzap_table[] = {
+ /* Streamzap Remote Control */
+ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+ /* Terminating entry */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define STREAMZAP_BUFFER_SIZE 128
+
+enum StreamzapDecoderState {
+ PulseSpace,
+ FullPulse,
+ FullSpace,
+ IgnorePulse
+};
+
+/* Structure to hold all of our device specific stuff */
+/* some remarks regarding locking:
+ theoretically this struct can be accessed from three threads:
+
+ - from lirc_dev through set_use_inc/set_use_dec
+
+ - from the USB layer throuh probe/disconnect/irq
+
+ Careful placement of lirc_register_plugin/lirc_unregister_plugin
+ calls will prevent conflicts. lirc_dev makes sure that
+ set_use_inc/set_use_dec are not being executed and will not be
+ called after lirc_unregister_plugin returns.
+
+ - by the timer callback
+
+ The timer is only running when the device is connected and the
+ LIRC device is open. Making sure the timer is deleted by
+ set_use_dec will make conflicts impossible.
+*/
+struct usb_streamzap {
+
+ /* usb */
+ /* save off the usb device pointer */
+ struct usb_device *udev;
+ /* the interface for this device */
+ struct usb_interface *interface;
+
+ /* buffer & dma */
+ unsigned char *buf_in;
+ dma_addr_t dma_in;
+ unsigned int buf_in_len;
+
+ struct usb_endpoint_descriptor *endpoint;
+
+ /* IRQ */
+ struct urb *urb_in;
+
+ /* lirc */
+ struct lirc_plugin plugin;
+ struct lirc_buffer delay_buf;
+ struct lirc_buffer lirc_buf;
+
+ /* timer used to support delay buffering */
+ struct timer_list delay_timer;
+ int timer_running;
+ spinlock_t timer_lock;
+
+ /* tracks whether we are currently receiving some signal */
+ int idle;
+ /* sum of signal lengths received since signal start */
+ unsigned long sum;
+ /* start time of signal; necessary for gap tracking */
+ struct timeval signal_last;
+ struct timeval signal_start;
+ enum StreamzapDecoderState decoder_state;
+ struct timer_list flush_timer;
+ int flush;
+ int in_use;
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void usb_streamzap_irq(struct urb *urb);
+static int streamzap_use_inc(void *data);
+static void streamzap_use_dec(void *data);
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+ unsigned int cmd, unsigned long arg);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+
+static struct usb_driver streamzap_driver = {
+ .name = DRIVER_NAME,
+ .probe = streamzap_probe,
+ .disconnect = streamzap_disconnect,
+ .suspend = streamzap_suspend,
+ .resume = streamzap_resume,
+ .id_table = streamzap_table,
+};
+
+static void stop_timer(struct usb_streamzap *sz)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (sz->timer_running) {
+ sz->timer_running = 0;
+ del_timer_sync(&sz->delay_timer);
+ }
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void flush_timeout(unsigned long arg)
+{
+ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+
+ /* finally start accepting data */
+ sz->flush = 0;
+}
+static void delay_timeout(unsigned long arg)
+{
+ unsigned long flags;
+ /* deliver data every 10 ms */
+ static unsigned long timer_inc =
+ (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
+ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+ int data;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+
+ if (!lirc_buffer_empty(&sz->delay_buf) &&
+ !lirc_buffer_full(&sz->lirc_buf)) {
+ lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+ lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+ }
+ if (!lirc_buffer_empty(&sz->delay_buf)) {
+ while (lirc_buffer_available(&sz->delay_buf) <
+ STREAMZAP_BUFFER_SIZE/2 &&
+ !lirc_buffer_full(&sz->lirc_buf)) {
+ lirc_buffer_read_1(&sz->delay_buf,
+ (unsigned char *) &data);
+ lirc_buffer_write_1(&sz->lirc_buf,
+ (unsigned char *) &data);
+ }
+ if (sz->timer_running) {
+ sz->delay_timer.expires += timer_inc;
+ add_timer(&sz->delay_timer);
+ }
+ } else {
+ sz->timer_running = 0;
+ }
+
+ if (!lirc_buffer_empty(&sz->lirc_buf))
+ wake_up(&sz->lirc_buf.wait_poll);
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void flush_delay_buffer(struct usb_streamzap *sz)
+{
+ int data;
+ int empty = 1;
+
+ while (!lirc_buffer_empty(&sz->delay_buf)) {
+ empty = 0;
+ lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+ if (!lirc_buffer_full(&sz->lirc_buf)) {
+ lirc_buffer_write_1(&sz->lirc_buf,
+ (unsigned char *) &data);
+ } else {
+ dprintk("buffer overflow\n", sz->plugin.minor);
+ }
+ }
+ if (!empty)
+ wake_up(&sz->lirc_buf.wait_poll);
+}
+
+static inline void push(struct usb_streamzap *sz, unsigned char *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (lirc_buffer_full(&sz->delay_buf)) {
+ int data;
+
+ lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+ if (!lirc_buffer_full(&sz->lirc_buf)) {
+ lirc_buffer_write_1(&sz->lirc_buf,
+ (unsigned char *) &data);
+ } else {
+ dprintk("buffer overflow", sz->plugin.minor);
+ }
+ }
+
+ lirc_buffer_write_1(&sz->delay_buf, data);
+
+ if (!sz->timer_running) {
+ sz->delay_timer.expires = jiffies + HZ/10;
+ add_timer(&sz->delay_timer);
+ sz->timer_running = 1;
+ }
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void push_full_pulse(struct usb_streamzap *sz,
+ unsigned char value)
+{
+ int pulse;
+
+ if (sz->idle) {
+ long deltv;
+ int tmp;
+
+ sz->signal_last = sz->signal_start;
+ do_gettimeofday(&sz->signal_start);
+
+ deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
+ if (deltv > 15) {
+ tmp = PULSE_MASK; /* really long time */
+ } else {
+ tmp = (int) (deltv*1000000+
+ sz->signal_start.tv_usec -
+ sz->signal_last.tv_usec);
+ tmp -= sz->sum;
+ }
+ dprintk("ls %u", sz->plugin.minor, tmp);
+ push(sz, (char *)&tmp);
+
+ sz->idle = 0;
+ sz->sum = 0;
+ }
+
+ pulse = ((int) value)*STREAMZAP_RESOLUTION;
+ pulse += STREAMZAP_RESOLUTION/2;
+ sz->sum += pulse;
+ pulse |= PULSE_BIT;
+
+ dprintk("p %u", sz->plugin.minor, pulse&PULSE_MASK);
+ push(sz, (char *)&pulse);
+}
+
+static inline void push_half_pulse(struct usb_streamzap *sz,
+ unsigned char value)
+{
+ push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
+}
+
+static inline void push_full_space(struct usb_streamzap *sz,
+ unsigned char value)
+{
+ int space;
+
+ space = ((int) value)*STREAMZAP_RESOLUTION;
+ space += STREAMZAP_RESOLUTION/2;
+ sz->sum += space;
+ dprintk("s %u", sz->plugin.minor, space);
+ push(sz, (char *)&space);
+}
+
+static inline void push_half_space(struct usb_streamzap *sz,
+ unsigned char value)
+{
+ push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/*
+ * usb_streamzap_irq - IRQ handler
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void usb_streamzap_irq(struct urb *urb)
+{
+ struct usb_streamzap *sz;
+ int len;
+ unsigned int i = 0;
+
+ if (!urb)
+ return;
+
+ sz = urb->context;
+ len = urb->actual_length;
+
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ /* sz might already be invalid at this point */
+ dprintk("urb status: %d", -1, urb->status);
+ return;
+ default:
+ break;
+ }
+
+ dprintk("received %d", sz->plugin.minor, urb->actual_length);
+ if (!sz->flush) {
+ for (i = 0; i < urb->actual_length; i++) {
+ dprintk("%d: %x", sz->plugin.minor,
+ i, (unsigned char) sz->buf_in[i]);
+ switch (sz->decoder_state) {
+ case PulseSpace:
+ if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
+ STREAMZAP_PULSE_MASK) {
+ sz->decoder_state = FullPulse;
+ continue;
+ } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
+ == STREAMZAP_SPACE_MASK) {
+ push_half_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = FullSpace;
+ continue;
+ } else {
+ push_half_pulse(sz, sz->buf_in[i]);
+ push_half_space(sz, sz->buf_in[i]);
+ }
+ break;
+ case FullPulse:
+ push_full_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = IgnorePulse;
+ break;
+ case FullSpace:
+ if (sz->buf_in[i] == 0xff) {
+ sz->idle = 1;
+ stop_timer(sz);
+ flush_delay_buffer(sz);
+ } else
+ push_full_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ case IgnorePulse:
+ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+ STREAMZAP_SPACE_MASK) {
+ sz->decoder_state = FullSpace;
+ continue;
+ }
+ push_half_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ }
+ }
+ }
+
+ /* resubmit only for 2.6 */
+ usb_submit_urb(urb, GFP_ATOMIC);
+
+ return;
+}
+
+/**
+ * streamzap_probe
+ *
+ * Called by usb-core to associated with a candidate device
+ * On any failure the return value is the ERROR
+ * On success return 0
+ */
+static int streamzap_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_host;
+ int retval = -ENOMEM;
+ struct usb_streamzap *sz = NULL;
+ char buf[63], name[128] = "";
+
+ /***************************************************
+ * Allocate space for device driver specific data
+ */
+ sz = kmalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
+ if (sz == NULL)
+ goto error;
+
+ memset(sz, 0, sizeof(*sz));
+ sz->udev = udev;
+ sz->interface = interface;
+
+ /***************************************************
+ * Check to ensure endpoint information matches requirements
+ */
+ iface_host = interface->cur_altsetting;
+
+ if (iface_host->desc.bNumEndpoints != 1) {
+ err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
+ iface_host->desc.bNumEndpoints);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ sz->endpoint = &(iface_host->endpoint[0].desc);
+ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ != USB_DIR_IN) {
+ err("%s: endpoint doesn't match input device 02%02x",
+ __func__, sz->endpoint->bEndpointAddress);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ err("%s: endpoint attributes don't match xfer 02%02x",
+ __func__, sz->endpoint->bmAttributes);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if (sz->endpoint->wMaxPacketSize == 0) {
+ err("%s: endpoint message size==0? ", __func__);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ /***************************************************
+ * Allocate the USB buffer and IRQ URB
+ */
+
+ sz->buf_in_len = sz->endpoint->wMaxPacketSize;
+ sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
+ GFP_ATOMIC, &sz->dma_in);
+ if (sz->buf_in == NULL)
+ goto error;
+
+ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (sz->urb_in == NULL)
+ goto error;
+
+ /***************************************************
+ * Connect this device to the LIRC sub-system
+ */
+
+ if (lirc_buffer_init(&sz->lirc_buf, sizeof(int),
+ STREAMZAP_BUFFER_SIZE))
+ goto error;
+
+ if (lirc_buffer_init(&sz->delay_buf, sizeof(int),
+ STREAMZAP_BUFFER_SIZE)) {
+ lirc_buffer_free(&sz->lirc_buf);
+ goto error;
+ }
+
+ /***************************************************
+ * As required memory is allocated now populate the plugin structure
+ */
+
+ memset(&sz->plugin, 0, sizeof(sz->plugin));
+
+ strcpy(sz->plugin.name, DRIVER_NAME);
+ sz->plugin.minor = -1;
+ sz->plugin.sample_rate = 0;
+ sz->plugin.code_length = sizeof(int) * 8;
+ sz->plugin.features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION;
+ sz->plugin.data = sz;
+ sz->plugin.rbuf = &sz->lirc_buf;
+ sz->plugin.set_use_inc = &streamzap_use_inc;
+ sz->plugin.set_use_dec = &streamzap_use_dec;
+ sz->plugin.ioctl = streamzap_ioctl;
+ sz->plugin.dev = &udev->dev;
+ sz->plugin.owner = THIS_MODULE;
+
+ sz->idle = 1;
+ sz->decoder_state = PulseSpace;
+ init_timer(&sz->delay_timer);
+ sz->delay_timer.function = delay_timeout;
+ sz->delay_timer.data = (unsigned long) sz;
+ sz->timer_running = 0;
+ spin_lock_init(&sz->timer_lock);
+
+ init_timer(&sz->flush_timer);
+ sz->flush_timer.function = flush_timeout;
+ sz->flush_timer.data = (unsigned long) sz;
+ /***************************************************
+ * Complete final initialisations
+ */
+
+ usb_fill_int_urb(sz->urb_in, udev,
+ usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
+ sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
+ sz->endpoint->bInterval);
+
+ if (udev->descriptor.iManufacturer
+ && usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0)
+ strncpy(name, buf, 128);
+
+ if (udev->descriptor.iProduct
+ && usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0)
+ snprintf(name, 128, "%s %s", name, buf);
+
+ printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
+ sz->plugin.minor, name,
+ udev->bus->busnum, sz->udev->devnum);
+
+ usb_set_intfdata(interface, sz);
+
+ if (lirc_register_plugin(&sz->plugin) < 0) {
+ lirc_buffer_free(&sz->delay_buf);
+ lirc_buffer_free(&sz->lirc_buf);
+ goto error;
+ }
+
+ return 0;
+
+error:
+
+ /***************************************************
+ * Premise is that a 'goto error' can be invoked from inside the
+ * probe function and all necessary cleanup actions will be taken
+ * including freeing any necessary memory blocks
+ */
+
+ if (retval == -ENOMEM)
+ err("Out of memory");
+
+ if (sz) {
+ usb_free_urb(sz->urb_in);
+ usb_buffer_free(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+ kfree(sz);
+ }
+
+ return retval;
+}
+
+static int streamzap_use_inc(void *data)
+{
+ struct usb_streamzap *sz = data;
+
+ if (!sz) {
+ dprintk("%s called with no context", -1, __func__);
+ return -EINVAL;
+ }
+ dprintk("set use inc", sz->plugin.minor);
+
+ while (!lirc_buffer_empty(&sz->lirc_buf))
+ lirc_buffer_remove_1(&sz->lirc_buf);
+ while (!lirc_buffer_empty(&sz->delay_buf))
+ lirc_buffer_remove_1(&sz->delay_buf);
+
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = 1;
+ add_timer(&sz->flush_timer);
+
+ sz->urb_in->dev = sz->udev;
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+ dprintk("open result = -EIO error submitting urb",
+ sz->plugin.minor);
+ return -EIO;
+ }
+ sz->in_use++;
+
+ return 0;
+}
+
+static void streamzap_use_dec(void *data)
+{
+ struct usb_streamzap *sz = data;
+
+ if (!sz) {
+ dprintk("%s called with no context", -1, __func__);
+ return;
+ }
+ dprintk("set use dec", sz->plugin.minor);
+
+ if (sz->flush) {
+ sz->flush = 0;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ stop_timer(sz);
+
+ usb_kill_urb(sz->urb_in);
+
+ sz->in_use--;
+}
+
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ int result;
+
+ switch (cmd) {
+ case LIRC_GET_REC_RESOLUTION:
+ result = put_user(STREAMZAP_RESOLUTION, (unsigned long *) arg);
+ if (result)
+ return result;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->udev. It is also supposed to terminate any currently
+ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+ struct usb_streamzap *sz;
+ int errnum;
+ int minor;
+
+ sz = usb_get_intfdata(interface);
+
+ /*
+ * unregister from the LIRC sub-system
+ */
+
+ errnum = lirc_unregister_plugin(sz->plugin.minor);
+ if (errnum != 0)
+ dprintk("error in lirc_unregister: (returned %d)",
+ sz->plugin.minor, errnum);
+
+ lirc_buffer_free(&sz->delay_buf);
+ lirc_buffer_free(&sz->lirc_buf);
+
+ /*
+ * unregister from the USB sub-system
+ */
+
+ usb_free_urb(sz->urb_in);
+
+ usb_buffer_free(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+ minor = sz->plugin.minor;
+ kfree(sz);
+
+ printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+ printk(DRIVER_NAME "[%d]: suspend\n", sz->plugin.minor);
+ if (sz->in_use) {
+ if (sz->flush) {
+ sz->flush = 0;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ stop_timer(sz);
+
+ usb_kill_urb(sz->urb_in);
+ }
+ return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+ struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+ while (!lirc_buffer_empty(&sz->lirc_buf))
+ lirc_buffer_remove_1(&sz->lirc_buf);
+ while (!lirc_buffer_empty(&sz->delay_buf))
+ lirc_buffer_remove_1(&sz->delay_buf);
+
+ if (sz->in_use) {
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = 1;
+ add_timer(&sz->flush_timer);
+
+ sz->urb_in->dev = sz->udev;
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+ dprintk("open result = -EIO error submitting urb",
+ sz->plugin.minor);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+#ifdef MODULE
+
+/**
+ * usb_streamzap_init
+ */
+static int __init usb_streamzap_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+
+ result = usb_register(&streamzap_driver);
+
+ if (result) {
+ err("usb_register failed. Error number %d",
+ result);
+ return result;
+ }
+
+ printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
+ return 0;
+}
+
+/**
+ * usb_streamzap_exit
+ */
+static void __exit usb_streamzap_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&streamzap_driver);
+}
+
+
+module_init(usb_streamzap_init);
+module_exit(usb_streamzap_exit);
+
+MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
--
1.6.0.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/