/* $Id: lirc_streamzap.c,v 1.55 2010/08/16 20:20:48 jarodwilson Exp $ */ /* * Streamzap Remote Control driver * * Copyright (c) 2005 Christoph Bartelmus * * 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@kroah.com) * * 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 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) #error "*******************************************************" #error "Sorry, this driver needs kernel version 2.4.0 or higher" #error "*******************************************************" #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) #include #endif #include #include #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) #include #else #include #endif #include #include "drivers/lirc.h" #include "drivers/kcompat.h" #include "drivers/lirc_dev/lirc_dev.h" #define DRIVER_VERSION "$Revision: 1.55 $" #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_TIMEOUT 0xff #define STREAMZAP_RESOLUTION 256 /* number of samples buffered */ #define STREAMZAP_BUFFER_SIZE 256 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_driver/lirc_unregister_driver * 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_driver 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_driver driver; 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; int timeout_enabled; }; /* local function prototypes */ #ifdef KERNEL_2_5 static int streamzap_probe(struct usb_interface *interface, const struct usb_device_id *id); static void streamzap_disconnect(struct usb_interface *interface); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static void usb_streamzap_irq(struct urb *urb, struct pt_regs *regs); #else static void usb_streamzap_irq(struct urb *urb); #endif #else static void *streamzap_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id); static void streamzap_disconnect(struct usb_device *dev, void *ptr); static void usb_streamzap_irq(struct urb *urb); #endif static int streamzap_use_inc(void *data); static void streamzap_use_dec(void *data); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) static int streamzap_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg); #else static long streamzap_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) static int streamzap_suspend(struct usb_interface *intf, pm_message_t message); static int streamzap_resume(struct usb_interface *intf); #endif /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver streamzap_driver = { LIRC_THIS_MODULE(.owner = THIS_MODULE) .name = DRIVER_NAME, .probe = streamzap_probe, .disconnect = streamzap_disconnect, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) .suspend = streamzap_suspend, .resume = streamzap_resume, #endif .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; spin_unlock_irqrestore(&sz->timer_lock, flags); del_timer_sync(&sz->delay_timer); } else { 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; lirc_t data; spin_lock_irqsave(&sz->timer_lock, flags); if (!lirc_buffer_empty(&sz->delay_buf) && !lirc_buffer_full(&sz->lirc_buf)) { lirc_buffer_read(&sz->delay_buf, (unsigned char *) &data); lirc_buffer_write(&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(&sz->delay_buf, (unsigned char *) &data); lirc_buffer_write(&sz->lirc_buf, (unsigned char *) &data); } if (sz->timer_running) { sz->delay_timer.expires = jiffies + 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 void flush_delay_buffer(struct usb_streamzap *sz) { lirc_t data; int empty = 1; while (!lirc_buffer_empty(&sz->delay_buf)) { empty = 0; lirc_buffer_read(&sz->delay_buf, (unsigned char *) &data); if (!lirc_buffer_full(&sz->lirc_buf)) { lirc_buffer_write(&sz->lirc_buf, (unsigned char *) &data); } else { dprintk("buffer overflow", sz->driver.minor); } } if (!empty) wake_up(&sz->lirc_buf.wait_poll); } static 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)) { lirc_t data; lirc_buffer_read(&sz->delay_buf, (unsigned char *) &data); if (!lirc_buffer_full(&sz->lirc_buf)) { lirc_buffer_write(&sz->lirc_buf, (unsigned char *) &data); } else { dprintk("buffer overflow", sz->driver.minor); } } lirc_buffer_write(&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 void push_full_pulse(struct usb_streamzap *sz, unsigned char value) { lirc_t pulse; if (sz->idle) { long deltv; lirc_t 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) { /* really long time */ tmp = LIRC_SPACE(LIRC_VALUE_MASK); } else { tmp = (lirc_t) (deltv*1000000+ sz->signal_start.tv_usec - sz->signal_last.tv_usec); tmp -= sz->sum; tmp = LIRC_SPACE(tmp); } dprintk("ls %u", sz->driver.minor, tmp); push(sz, (char *)&tmp); sz->idle = 0; sz->sum = 0; } pulse = ((lirc_t) value)*STREAMZAP_RESOLUTION; pulse += STREAMZAP_RESOLUTION/2; sz->sum += pulse; pulse = LIRC_PULSE(pulse); dprintk("p %u", sz->driver.minor, pulse&PULSE_MASK); push(sz, (char *)&pulse); } static void push_half_pulse(struct usb_streamzap *sz, unsigned char value) { push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4); } static void push_full_space(struct usb_streamzap *sz, unsigned char value) { lirc_t space; space = ((lirc_t) value)*STREAMZAP_RESOLUTION; space += STREAMZAP_RESOLUTION/2; sz->sum += space; space = LIRC_SPACE(space); dprintk("s %u", sz->driver.minor, space); push(sz, (char *)&space); } static 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. */ #if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static void usb_streamzap_irq(struct urb *urb, struct pt_regs *regs) #else static void usb_streamzap_irq(struct urb *urb) #endif { 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->driver.minor, urb->actual_length); if (!sz->flush) { for (i = 0; i < urb->actual_length; i++) { dprintk("%d: %x", sz->driver.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] == STREAMZAP_TIMEOUT) { sz->idle = 1; stop_timer(sz); if (sz->timeout_enabled) { lirc_t timeout = LIRC_TIMEOUT (STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION); push(sz, (char *)&timeout); } 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; } } } #ifdef KERNEL_2_5 usb_submit_urb(urb, GFP_ATOMIC); #endif return; } static struct file_operations streamzap_fops = { .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) .ioctl = streamzap_ioctl, #else .unlocked_ioctl = streamzap_ioctl, #endif }; /** * 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 */ #ifdef KERNEL_2_5 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; #else static void *streamzap_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) { struct usb_interface *interface = &udev->actconfig->interface[ifnum]; struct usb_interface_descriptor *iface_host; #endif int retval = -ENOMEM; struct usb_streamzap *sz = NULL; char buf[63], name[128] = ""; /* Allocate space for device driver specific data */ sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL); if (sz == NULL) goto error; sz->udev = udev; sz->interface = interface; /* Check to ensure endpoint information matches requirements */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5) iface_host = &interface->altsetting[interface->act_altsetting]; #else iface_host = interface->cur_altsetting; #endif #ifdef KERNEL_2_5 if (iface_host->desc.bNumEndpoints != 1) { #else if (iface_host->bNumEndpoints != 1) { #endif #ifdef KERNEL_2_5 err("%s: Unexpected desc.bNumEndpoints (%d)", __func__, iface_host->desc.bNumEndpoints); #else err("%s: Unexpected desc.bNumEndpoints (%d)", __func__, iface_host->bNumEndpoints); #endif retval = -ENODEV; goto error; } #ifdef KERNEL_2_5 sz->endpoint = &(iface_host->endpoint[0].desc); #else sz->endpoint = &(iface_host->endpoint[0]); #endif 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; #ifdef KERNEL_2_5 sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len, GFP_ATOMIC, &sz->dma_in); #else sz->buf_in = kmalloc(sz->buf_in_len, GFP_KERNEL); #endif if (sz->buf_in == NULL) goto error; #ifdef KERNEL_2_5 sz->urb_in = usb_alloc_urb(0, GFP_KERNEL); #else sz->urb_in = usb_alloc_urb(0); #endif if (sz->urb_in == NULL) goto error; /* Connect this device to the LIRC sub-system */ if (lirc_buffer_init(&sz->lirc_buf, sizeof(lirc_t), STREAMZAP_BUFFER_SIZE)) goto error; if (lirc_buffer_init(&sz->delay_buf, sizeof(lirc_t), STREAMZAP_BUFFER_SIZE)) { lirc_buffer_free(&sz->lirc_buf); goto error; } strcpy(sz->driver.name, DRIVER_NAME); sz->driver.minor = -1; sz->driver.sample_rate = 0; sz->driver.code_length = sizeof(lirc_t) * 8; sz->driver.features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION | LIRC_CAN_SET_REC_TIMEOUT; sz->driver.data = sz; sz->driver.min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION; sz->driver.max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION; sz->driver.rbuf = &sz->lirc_buf; sz->driver.set_use_inc = &streamzap_use_inc; sz->driver.set_use_dec = &streamzap_use_dec; sz->driver.fops = &streamzap_fops; #ifdef LIRC_HAVE_SYSFS sz->driver.dev = &interface->dev; #endif sz->driver.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); #ifdef KERNEL_2_5 sz->urb_in->transfer_dma = sz->dma_in; sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; #endif if (udev->descriptor.iManufacturer && usb_string(udev, udev->descriptor.iManufacturer, buf, sizeof(buf)) > 0) strlcpy(name, buf, sizeof(name)); if (udev->descriptor.iProduct && usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0) snprintf(name + strlen(name), sizeof(name) - strlen(name), " %s", buf); printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n", sz->driver.minor, name, udev->bus->busnum, sz->udev->devnum); #ifdef KERNEL_2_5 usb_set_intfdata(interface, sz); #endif if (lirc_register_driver(&sz->driver) < 0) { lirc_buffer_free(&sz->delay_buf); lirc_buffer_free(&sz->lirc_buf); goto error; } #ifdef KERNEL_2_5 return 0; #else return sz; #endif 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); #ifdef KERNEL_2_5 usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in); #else if (sz->buf_in) { kfree(sz->buf_in); } #endif kfree(sz); } #ifdef KERNEL_2_5 return retval; #else return NULL; #endif } 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->driver.minor); MOD_INC_USE_COUNT; lirc_buffer_clear(&sz->lirc_buf); lirc_buffer_clear(&sz->delay_buf); sz->flush_timer.expires = jiffies + HZ; sz->flush = 1; add_timer(&sz->flush_timer); sz->timeout_enabled = 0; sz->urb_in->dev = sz->udev; #ifdef KERNEL_2_5 if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { #else if (usb_submit_urb(sz->urb_in)) { #endif dprintk("open result = -EIO error submitting urb", sz->driver.minor); MOD_DEC_USE_COUNT; 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->driver.minor); if (sz->flush) { sz->flush = 0; del_timer_sync(&sz->flush_timer); } usb_kill_urb(sz->urb_in); stop_timer(sz); MOD_DEC_USE_COUNT; sz->in_use--; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) static int streamzap_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg) #else static long streamzap_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) #endif { int result = 0; lirc_t val; unsigned int flag = 0; struct usb_streamzap *sz = lirc_get_pdata(filep); switch (cmd) { case LIRC_GET_REC_RESOLUTION: result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg); break; case LIRC_SET_REC_TIMEOUT: result = get_user(val, (lirc_t *)arg); if (result == 0) { if (val != STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) result = -EINVAL; } break; case LIRC_SET_REC_TIMEOUT_REPORTS: result = get_user(flag, (unsigned int *)arg); if (result == 0) { if (flag != 0) sz->timeout_enabled = 1; else sz->timeout_enabled = 0; } break; default: return -ENOIOCTLCMD; } return result; } /** * 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. */ #ifdef KERNEL_2_5 static void streamzap_disconnect(struct usb_interface *interface) #else static void streamzap_disconnect(struct usb_device *dev, void *ptr) #endif { struct usb_streamzap *sz; int errnum; int minor; #ifdef KERNEL_2_5 sz = usb_get_intfdata(interface); #else sz = ptr; #endif /* unregister from the LIRC sub-system */ errnum = lirc_unregister_driver(sz->driver.minor); if (errnum != 0) dprintk("error in lirc_unregister: (returned %d)", sz->driver.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); #ifdef KERNEL_2_5 usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in); #else kfree(sz->buf_in); #endif minor = sz->driver.minor; kfree(sz); printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) static int streamzap_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_streamzap *sz = usb_get_intfdata(intf); printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver.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); lirc_buffer_clear(&sz->lirc_buf); lirc_buffer_clear(&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; #ifdef KERNEL_2_5 if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { #else if (usb_submit_urb(sz->urb_in)) { #endif dprintk("open result = -EIO error submitting urb", sz->driver.minor); MOD_DEC_USE_COUNT; return -EIO; } } return 0; } #endif #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) { 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, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging messages"); EXPORT_NO_SYMBOLS; #endif /* MODULE */