From b9878dcfec74417c805c6dbd21a6cdd13f2deb2a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 16 Aug 2010 11:57:58 -0400 Subject: [PATCH] Add ir-core streamzap driver, drop lirc_streamzap Signed-off-by: Jarod Wilson --- config-generic | 4 +- kernel.spec | 7 +- linux-2.6-v4l-dvb-ir-core-streamzap.patch | 944 ++++++++++++++++++++++ lirc-staging-2.6.36.patch | 845 +------------------ 4 files changed, 958 insertions(+), 842 deletions(-) create mode 100644 linux-2.6-v4l-dvb-ir-core-streamzap.patch diff --git a/config-generic b/config-generic index a6719bf98..d61368e37 100644 --- a/config-generic +++ b/config-generic @@ -2587,9 +2587,11 @@ CONFIG_IR_RC5_DECODER=m CONFIG_IR_RC6_DECODER=m CONFIG_IR_JVC_DECODER=m CONFIG_IR_SONY_DECODER=m +CONFIG_IR_RC5_SZ_DECODER=m CONFIG_IR_LIRC_CODEC=m CONFIG_IR_IMON=m CONFIG_IR_MCEUSB=m +CONFIG_IR_STREAMZAP=m CONFIG_V4L_MEM2MEM_DRIVERS=y # CONFIG_VIDEO_MEM2MEM_TESTDEV is not set @@ -4015,7 +4017,7 @@ CONFIG_LIRC_SERIAL=m CONFIG_LIRC_SERIAL_TRANSMITTER=y CONFIG_LIRC_SASEM=m CONFIG_LIRC_SIR=m -CONFIG_LIRC_STREAMZAP=m +# CONFIG_LIRC_STREAMZAP is not set CONFIG_LIRC_TTUSBIR=m # CONFIG_SAMPLES is not set diff --git a/kernel.spec b/kernel.spec index 3b299f93a..20eefe23c 100644 --- a/kernel.spec +++ b/kernel.spec @@ -48,7 +48,7 @@ Summary: The Linux kernel # reset this by hand to 1 (or to 0 and then use rpmdev-bumpspec). # scripts/rebase.sh should be made to do that for you, actually. # -%global baserelease 8 +%global baserelease 9 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -679,6 +679,7 @@ Patch2910: linux-2.6-v4l-dvb-add-lgdt3304-support.patch Patch2911: linux-2.6-v4l-dvb-add-kworld-a340-support.patch Patch2912: linux-2.6-v4l-dvb-ir-core-update.patch Patch2913: linux-2.6-v4l-dvb-ir-core-memleak-fixes.patch +Patch2914: linux-2.6-v4l-dvb-ir-core-streamzap.patch Patch2915: lirc-staging-2.6.36.patch #Patch2916: lirc-staging-2.6.36-fixes.patch @@ -1276,6 +1277,7 @@ ApplyPatch linux-2.6-v4l-dvb-uvcvideo-update.patch ApplyPatch linux-2.6-v4l-dvb-ir-core-update.patch ApplyPatch linux-2.6-v4l-dvb-ir-core-memleak-fixes.patch +ApplyPatch linux-2.6-v4l-dvb-ir-core-streamzap.patch ApplyPatch linux-2.6-v4l-dvb-add-lgdt3304-support.patch ApplyPatch linux-2.6-v4l-dvb-add-kworld-a340-support.patch @@ -1884,6 +1886,9 @@ fi # and build. %changelog +* Mon Aug 16 2010 Jarod Wilson 2.6.35.2-9 +- Add ir-core streamzap driver, nuke lirc_streamzap + * Sun Aug 15 2010 Chuck Ebbert 2.6.35.2-8 - Linux 2.6.35.2 diff --git a/linux-2.6-v4l-dvb-ir-core-streamzap.patch b/linux-2.6-v4l-dvb-ir-core-streamzap.patch new file mode 100644 index 000000000..e1caed7b9 --- /dev/null +++ b/linux-2.6-v4l-dvb-ir-core-streamzap.patch @@ -0,0 +1,944 @@ +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/ir-core-priv.h linux-2.6.35.x86_64/drivers/media/IR/ir-core-priv.h +--- linux-2.6.35.x86_64.orig/drivers/media/IR/ir-core-priv.h 2010-08-15 17:50:34.572382442 -0400 ++++ linux-2.6.35.x86_64/drivers/media/IR/ir-core-priv.h 2010-08-16 00:11:42.756124321 -0400 +@@ -73,6 +73,12 @@ struct ir_raw_event_ctrl { + bool first; + bool toggle; + } jvc; ++ struct rc5_sz_dec { ++ int state; ++ u32 bits; ++ unsigned count; ++ unsigned wanted_bits; ++ } rc5_sz; + struct lirc_codec { + struct ir_input_dev *ir_dev; + struct lirc_driver *drv; +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/ir-rc5-sz-decoder.c linux-2.6.35.x86_64/drivers/media/IR/ir-rc5-sz-decoder.c +--- linux-2.6.35.x86_64.orig/drivers/media/IR/ir-rc5-sz-decoder.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6.35.x86_64/drivers/media/IR/ir-rc5-sz-decoder.c 2010-08-16 00:07:19.962608685 -0400 +@@ -0,0 +1,153 @@ ++/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol ++ * ++ * Copyright (C) 2010 by Mauro Carvalho Chehab ++ * Copyright (C) 2010 by Jarod Wilson ++ * ++ * 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 version 2 of the License. ++ * ++ * 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. ++ */ ++ ++/* ++ * This code handles the 15 bit RC5-ish protocol used by the Streamzap ++ * PC Remote. ++ * It considers a carrier of 36 kHz, with a total of 15 bits, where ++ * the first two bits are start bits, and a third one is a filing bit ++ */ ++ ++#include "ir-core-priv.h" ++ ++#define RC5_SZ_NBITS 15 ++#define RC5_UNIT 888888 /* ns */ ++#define RC5_BIT_START (1 * RC5_UNIT) ++#define RC5_BIT_END (1 * RC5_UNIT) ++ ++enum rc5_sz_state { ++ STATE_INACTIVE, ++ STATE_BIT_START, ++ STATE_BIT_END, ++ STATE_FINISHED, ++}; ++ ++/** ++ * ir_rc5_sz_decode() - Decode one RC-5 Streamzap pulse or space ++ * @input_dev: the struct input_dev descriptor of the device ++ * @ev: the struct ir_raw_event descriptor of the pulse/space ++ * ++ * This function returns -EINVAL if the pulse violates the state machine ++ */ ++static int ir_rc5_sz_decode(struct input_dev *input_dev, struct ir_raw_event ev) ++{ ++ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); ++ struct rc5_sz_dec *data = &ir_dev->raw->rc5_sz; ++ u8 toggle, command, system; ++ u32 scancode; ++ ++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5_SZ)) ++ return 0; ++ ++ if (IS_RESET(ev)) { ++ data->state = STATE_INACTIVE; ++ return 0; ++ } ++ ++ if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) ++ goto out; ++ ++again: ++ IR_dprintk(2, "RC5-sz decode started at state %i (%uus %s)\n", ++ data->state, TO_US(ev.duration), TO_STR(ev.pulse)); ++ ++ if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) ++ return 0; ++ ++ switch (data->state) { ++ ++ case STATE_INACTIVE: ++ if (!ev.pulse) ++ break; ++ ++ data->state = STATE_BIT_START; ++ data->count = 1; ++ data->wanted_bits = RC5_SZ_NBITS; ++ decrease_duration(&ev, RC5_BIT_START); ++ goto again; ++ ++ case STATE_BIT_START: ++ if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) ++ break; ++ ++ data->bits <<= 1; ++ if (!ev.pulse) ++ data->bits |= 1; ++ data->count++; ++ data->state = STATE_BIT_END; ++ return 0; ++ ++ case STATE_BIT_END: ++ if (!is_transition(&ev, &ir_dev->raw->prev_ev)) ++ break; ++ ++ if (data->count == data->wanted_bits) ++ data->state = STATE_FINISHED; ++ else ++ data->state = STATE_BIT_START; ++ ++ decrease_duration(&ev, RC5_BIT_END); ++ goto again; ++ ++ case STATE_FINISHED: ++ if (ev.pulse) ++ break; ++ ++ /* RC5-sz */ ++ command = (data->bits & 0x0003F) >> 0; ++ system = (data->bits & 0x02FC0) >> 6; ++ toggle = (data->bits & 0x01000) ? 1 : 0; ++ scancode = system << 6 | command; ++ ++ IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n", ++ scancode, toggle); ++ ++ ir_keydown(input_dev, scancode, toggle); ++ data->state = STATE_INACTIVE; ++ return 0; ++ } ++ ++out: ++ IR_dprintk(1, "RC5-sz decode failed at state %i (%uus %s)\n", ++ data->state, TO_US(ev.duration), TO_STR(ev.pulse)); ++ data->state = STATE_INACTIVE; ++ return -EINVAL; ++} ++ ++static struct ir_raw_handler rc5_sz_handler = { ++ .protocols = IR_TYPE_RC5_SZ, ++ .decode = ir_rc5_sz_decode, ++}; ++ ++static int __init ir_rc5_sz_decode_init(void) ++{ ++ ir_raw_handler_register(&rc5_sz_handler); ++ ++ printk(KERN_INFO "IR RC5 (streamzap) protocol handler initialized\n"); ++ return 0; ++} ++ ++static void __exit ir_rc5_sz_decode_exit(void) ++{ ++ ir_raw_handler_unregister(&rc5_sz_handler); ++} ++ ++module_init(ir_rc5_sz_decode_init); ++module_exit(ir_rc5_sz_decode_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jarod Wilson "); ++MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); ++MODULE_DESCRIPTION("RC5 (streamzap) IR protocol decoder"); +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/ir-sysfs.c linux-2.6.35.x86_64/drivers/media/IR/ir-sysfs.c +--- linux-2.6.35.x86_64.orig/drivers/media/IR/ir-sysfs.c 2010-08-15 17:50:34.574380413 -0400 ++++ linux-2.6.35.x86_64/drivers/media/IR/ir-sysfs.c 2010-08-16 00:23:24.893168856 -0400 +@@ -93,6 +93,11 @@ static ssize_t show_protocols(struct dev + else if (allowed & IR_TYPE_SONY) + tmp += sprintf(tmp, "sony "); + ++ if (allowed & enabled & IR_TYPE_RC5_SZ) ++ tmp += sprintf(tmp, "[rc5sz] "); ++ else if (allowed & IR_TYPE_RC5_SZ) ++ tmp += sprintf(tmp, "rc5sz "); ++ + if (allowed & enabled & IR_TYPE_LIRC) + tmp += sprintf(tmp, "[lirc] "); + else if (allowed & IR_TYPE_LIRC) +@@ -165,6 +170,9 @@ static ssize_t store_protocols(struct de + } else if (!strncasecmp(tmp, "sony", 4)) { + tmp += 4; + mask = IR_TYPE_SONY; ++ } else if (!strncasecmp(tmp, "rc5sz", 5)) { ++ tmp += 5; ++ mask = IR_TYPE_RC5_SZ; + } else if (!strncasecmp(tmp, "lirc", 4)) { + tmp += 4; + mask = IR_TYPE_LIRC; +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/Kconfig linux-2.6.35.x86_64/drivers/media/IR/Kconfig +--- linux-2.6.35.x86_64.orig/drivers/media/IR/Kconfig 2010-08-15 17:50:34.571382513 -0400 ++++ linux-2.6.35.x86_64/drivers/media/IR/Kconfig 2010-08-16 00:10:09.292873588 -0400 +@@ -69,6 +69,16 @@ config IR_SONY_DECODER + Enable this option if you have an infrared remote control which + uses the Sony protocol, and you need software decoding support. + ++config IR_RC5_SZ_DECODER ++ tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol" ++ depends on IR_CORE ++ select BITREVERSE ++ default y ++ ++ ---help--- ++ Enable this option if you have IR with RC-5 (streamzap) protocol, ++ and if the IR is decoded in software. ++ + config IR_LIRC_CODEC + tristate "Enable IR to LIRC bridge" + depends on IR_CORE +@@ -102,3 +112,15 @@ config IR_MCEUSB + + To compile this driver as a module, choose M here: the + module will be called mceusb. ++ ++config IR_STREAMZAP ++ tristate "Streamzap PC Remote IR Receiver" ++ depends on USB_ARCH_HAS_HCD ++ depends on IR_CORE ++ select USB ++ ---help--- ++ Say Y here if you want to use a Streamzap PC Remote ++ Infrared Receiver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called streamzap. +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/keymaps/Makefile linux-2.6.35.x86_64/drivers/media/IR/keymaps/Makefile +--- linux-2.6.35.x86_64.orig/drivers/media/IR/keymaps/Makefile 2010-08-15 17:50:34.575383346 -0400 ++++ linux-2.6.35.x86_64/drivers/media/IR/keymaps/Makefile 2010-08-16 00:05:47.400370419 -0400 +@@ -60,6 +60,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t + rc-rc5-tv.o \ + rc-rc6-mce.o \ + rc-real-audio-220-32-keys.o \ ++ rc-streamzap.o \ + rc-tbs-nec.o \ + rc-terratec-cinergy-xs.o \ + rc-tevii-nec.o \ +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/keymaps/rc-streamzap.c linux-2.6.35.x86_64/drivers/media/IR/keymaps/rc-streamzap.c +--- linux-2.6.35.x86_64.orig/drivers/media/IR/keymaps/rc-streamzap.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6.35.x86_64/drivers/media/IR/keymaps/rc-streamzap.c 2010-08-16 00:05:47.402370467 -0400 +@@ -0,0 +1,82 @@ ++/* rc-streamzap.c - Keytable for Streamzap PC Remote, for use ++ * with the Streamzap PC Remote IR Receiver. ++ * ++ * Copyright (c) 2010 by Jarod Wilson ++ * ++ * 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. ++ */ ++ ++#include ++ ++static struct ir_scancode streamzap[] = { ++/* ++ * The Streamzap remote is almost, but not quite, RC-5, as it has an extra ++ * bit in it, which throws the in-kernel RC-5 decoder for a loop. Currently, ++ * an additional RC-5-sz decoder is being deployed to support it, but it ++ * may be possible to merge it back with the standard RC-5 decoder. ++ */ ++ { 0x28c0, KEY_NUMERIC_0 }, ++ { 0x28c1, KEY_NUMERIC_1 }, ++ { 0x28c2, KEY_NUMERIC_2 }, ++ { 0x28c3, KEY_NUMERIC_3 }, ++ { 0x28c4, KEY_NUMERIC_4 }, ++ { 0x28c5, KEY_NUMERIC_5 }, ++ { 0x28c6, KEY_NUMERIC_6 }, ++ { 0x28c7, KEY_NUMERIC_7 }, ++ { 0x28c8, KEY_NUMERIC_8 }, ++ { 0x28c9, KEY_NUMERIC_9 }, ++ { 0x28ca, KEY_POWER }, ++ { 0x28cb, KEY_MUTE }, ++ { 0x28cc, KEY_CHANNELUP }, ++ { 0x28cd, KEY_VOLUMEUP }, ++ { 0x28ce, KEY_CHANNELDOWN }, ++ { 0x28cf, KEY_VOLUMEDOWN }, ++ { 0x28d0, KEY_UP }, ++ { 0x28d1, KEY_LEFT }, ++ { 0x28d2, KEY_OK }, ++ { 0x28d3, KEY_RIGHT }, ++ { 0x28d4, KEY_DOWN }, ++ { 0x28d5, KEY_MENU }, ++ { 0x28d6, KEY_EXIT }, ++ { 0x28d7, KEY_PLAY }, ++ { 0x28d8, KEY_PAUSE }, ++ { 0x28d9, KEY_STOP }, ++ { 0x28da, KEY_BACK }, ++ { 0x28db, KEY_FORWARD }, ++ { 0x28dc, KEY_RECORD }, ++ { 0x28dd, KEY_REWIND }, ++ { 0x28de, KEY_FASTFORWARD }, ++ { 0x28e0, KEY_RED }, ++ { 0x28e1, KEY_GREEN }, ++ { 0x28e2, KEY_YELLOW }, ++ { 0x28e3, KEY_BLUE }, ++ ++}; ++ ++static struct rc_keymap streamzap_map = { ++ .map = { ++ .scan = streamzap, ++ .size = ARRAY_SIZE(streamzap), ++ .ir_type = IR_TYPE_RC5, ++ .name = RC_MAP_STREAMZAP, ++ } ++}; ++ ++static int __init init_rc_map_streamzap(void) ++{ ++ return ir_register_map(&streamzap_map); ++} ++ ++static void __exit exit_rc_map_streamzap(void) ++{ ++ ir_unregister_map(&streamzap_map); ++} ++ ++module_init(init_rc_map_streamzap) ++module_exit(exit_rc_map_streamzap) ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jarod Wilson "); +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/Makefile linux-2.6.35.x86_64/drivers/media/IR/Makefile +--- linux-2.6.35.x86_64.orig/drivers/media/IR/Makefile 2010-08-15 17:50:34.571382513 -0400 ++++ linux-2.6.35.x86_64/drivers/media/IR/Makefile 2010-08-16 00:10:29.478144601 -0400 +@@ -11,8 +11,10 @@ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-d + obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o + obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o + obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o ++obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o + obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o + + # stand-alone IR receivers/transmitters + obj-$(CONFIG_IR_IMON) += imon.o + obj-$(CONFIG_IR_MCEUSB) += mceusb.o ++obj-$(CONFIG_IR_STREAMZAP) += streamzap.o +diff -Naurp linux-2.6.35.x86_64.orig/drivers/media/IR/streamzap.c linux-2.6.35.x86_64/drivers/media/IR/streamzap.c +--- linux-2.6.35.x86_64.orig/drivers/media/IR/streamzap.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6.35.x86_64/drivers/media/IR/streamzap.c 2010-08-16 00:12:43.223932881 -0400 +@@ -0,0 +1,569 @@ ++/* ++ * Streamzap Remote Control driver ++ * ++ * Copyright (c) 2005 Christoph Bartelmus ++ * Copyright (c) 2010 Jarod Wilson ++ * ++ * 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. ++ * ++ * Ported to in-kernel ir-core interface by Jarod Wilson ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "1.61" ++#define DRIVER_NAME "streamzap" ++#define DRIVER_DESC "Streamzap Remote Control driver" ++ ++#ifdef CONFIG_USB_DEBUG ++static int debug = 1; ++#else ++static int debug; ++#endif ++ ++#define USB_STREAMZAP_VENDOR_ID 0x0e9c ++#define USB_STREAMZAP_PRODUCT_ID 0x0000 ++ ++/* 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 SZ_BUF_LEN 128 ++ ++/* from ir-rc5-sz-decoder.c */ ++#ifdef CONFIG_IR_RC5_SZ_DECODER_MODULE ++#define load_rc5_sz_decode() request_module("ir-rc5-sz-decoder") ++#else ++#define load_rc5_sz_decode() 0 ++#endif ++ ++enum StreamzapDecoderState { ++ PulseSpace, ++ FullPulse, ++ FullSpace, ++ IgnorePulse ++}; ++ ++/* structure to hold our device specific stuff */ ++struct streamzap_ir { ++ ++ /* ir-core */ ++ struct ir_dev_props *props; ++ ++ /* core device info */ ++ struct device *dev; ++ struct input_dev *idev; ++ ++ /* usb */ ++ struct usb_device *usbdev; ++ struct usb_interface *interface; ++ struct usb_endpoint_descriptor *endpoint; ++ struct urb *urb_in; ++ ++ /* buffer & dma */ ++ unsigned char *buf_in; ++ dma_addr_t dma_in; ++ unsigned int buf_in_len; ++ ++ /* track what state we're in */ ++ enum StreamzapDecoderState decoder_state; ++ /* tracks whether we are currently receiving some signal */ ++ bool 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; ++ bool timeout_enabled; ++ ++ char name[128]; ++ char phys[64]; ++}; ++ ++ ++/* 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 streamzap_callback(struct urb *urb); ++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 sz_push(struct streamzap_ir *sz, struct ir_raw_event rawir) ++{ ++ ir_raw_event_store(sz->idev, &rawir); ++} ++ ++static void sz_push_full_pulse(struct streamzap_ir *sz, ++ unsigned char value) ++{ ++ struct ir_raw_event rawir; ++ ++ if (sz->idle) { ++ long deltv; ++ ++ sz->signal_last = sz->signal_start; ++ do_gettimeofday(&sz->signal_start); ++ ++ deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec; ++ rawir.pulse = false; ++ if (deltv > 15) { ++ /* really long time */ ++ rawir.duration = IR_MAX_DURATION; ++ } else { ++ rawir.duration = (int)(deltv * 1000000 + ++ sz->signal_start.tv_usec - ++ sz->signal_last.tv_usec); ++ rawir.duration -= sz->sum; ++ rawir.duration *= 1000; ++ rawir.duration &= IR_MAX_DURATION; ++ } ++ dev_dbg(sz->dev, "ls %u\n", rawir.duration); ++ sz_push(sz, rawir); ++ ++ sz->idle = false; ++ sz->sum = 0; ++ } ++ ++ rawir.pulse = true; ++ rawir.duration = ((int) value) * STREAMZAP_RESOLUTION; ++ rawir.duration += STREAMZAP_RESOLUTION / 2; ++ sz->sum += rawir.duration; ++ rawir.duration *= 1000; ++ rawir.duration &= IR_MAX_DURATION; ++ dev_dbg(sz->dev, "p %u\n", rawir.duration); ++ sz_push(sz, rawir); ++} ++ ++static void sz_push_half_pulse(struct streamzap_ir *sz, ++ unsigned char value) ++{ ++ sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4); ++} ++ ++static void sz_push_full_space(struct streamzap_ir *sz, ++ unsigned char value) ++{ ++ struct ir_raw_event rawir; ++ ++ rawir.pulse = false; ++ rawir.duration = ((int) value) * STREAMZAP_RESOLUTION; ++ rawir.duration += STREAMZAP_RESOLUTION / 2; ++ sz->sum += rawir.duration; ++ rawir.duration *= 1000; ++ dev_dbg(sz->dev, "s %u\n", rawir.duration); ++ sz_push(sz, rawir); ++} ++ ++static void sz_push_half_space(struct streamzap_ir *sz, ++ unsigned long value) ++{ ++ sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK); ++} ++ ++/** ++ * streamzap_callback - usb IRQ handler callback ++ * ++ * This procedure is invoked on reception of data from ++ * the usb remote. ++ */ ++static void streamzap_callback(struct urb *urb) ++{ ++ struct streamzap_ir *sz; ++ unsigned int i; ++ int len; ++ static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) & ++ IR_MAX_DURATION) | 0x03000000); ++ ++ 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 ++ */ ++ dev_err(sz->dev, "urb terminated, status: %d\n", urb->status); ++ return; ++ default: ++ break; ++ } ++ ++ dev_info(sz->dev, "%s: received urb, len %d\n", __func__, len); ++ for (i = 0; i < len; i++) { ++ dev_info(sz->dev, "sz idx %d: %x\n", ++ 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) { ++ sz_push_half_pulse(sz, sz->buf_in[i]); ++ sz->decoder_state = FullSpace; ++ continue; ++ } else { ++ sz_push_half_pulse(sz, sz->buf_in[i]); ++ sz_push_half_space(sz, sz->buf_in[i]); ++ } ++ break; ++ case FullPulse: ++ sz_push_full_pulse(sz, sz->buf_in[i]); ++ sz->decoder_state = IgnorePulse; ++ break; ++ case FullSpace: ++ if (sz->buf_in[i] == STREAMZAP_TIMEOUT) { ++ struct ir_raw_event rawir; ++ ++ rawir.pulse = false; ++ rawir.duration = timeout * 1000; ++ sz->idle = true; ++ if (sz->timeout_enabled) ++ sz_push(sz, rawir); ++ ir_raw_event_handle(sz->idev); ++ } else { ++ sz_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; ++ } ++ sz_push_half_space(sz, sz->buf_in[i]); ++ sz->decoder_state = PulseSpace; ++ break; ++ } ++ } ++ ++ usb_submit_urb(urb, GFP_ATOMIC); ++ ++ return; ++} ++ ++static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz) ++{ ++ struct input_dev *idev; ++ struct ir_dev_props *props; ++ struct device *dev = sz->dev; ++ int ret; ++ ++ idev = input_allocate_device(); ++ if (!idev) { ++ dev_err(dev, "remote input dev allocation failed\n"); ++ goto idev_alloc_failed; ++ } ++ ++ props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); ++ if (!props) { ++ dev_err(dev, "remote ir dev props allocation failed\n"); ++ goto props_alloc_failed; ++ } ++ ++ snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared " ++ "Receiver (%04x:%04x)", ++ le16_to_cpu(sz->usbdev->descriptor.idVendor), ++ le16_to_cpu(sz->usbdev->descriptor.idProduct)); ++ ++ idev->name = sz->name; ++ usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys)); ++ strlcat(sz->phys, "/input0", sizeof(sz->phys)); ++ idev->phys = sz->phys; ++ ++ props->priv = sz; ++ props->driver_type = RC_DRIVER_IR_RAW; ++ props->allowed_protos = IR_TYPE_ALL; ++ ++ sz->props = props; ++ ++ ret = ir_input_register(idev, RC_MAP_STREAMZAP, props, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "remote input device register failed\n"); ++ goto irdev_failed; ++ } ++ ++ return idev; ++ ++irdev_failed: ++ kfree(props); ++props_alloc_failed: ++ input_free_device(idev); ++idev_alloc_failed: ++ return NULL; ++} ++ ++/** ++ * 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 __devinit streamzap_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *usbdev = interface_to_usbdev(intf); ++ struct usb_host_interface *iface_host; ++ struct streamzap_ir *sz = NULL; ++ char buf[63], name[128] = ""; ++ int retval = -ENOMEM; ++ int pipe, maxp; ++ ++ /* Allocate space for device driver specific data */ ++ sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL); ++ if (!sz) ++ return -ENOMEM; ++ ++ sz->usbdev = usbdev; ++ sz->interface = intf; ++ ++ /* Check to ensure endpoint information matches requirements */ ++ iface_host = intf->cur_altsetting; ++ ++ if (iface_host->desc.bNumEndpoints != 1) { ++ dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n", ++ __func__, iface_host->desc.bNumEndpoints); ++ retval = -ENODEV; ++ goto free_sz; ++ } ++ ++ sz->endpoint = &(iface_host->endpoint[0].desc); ++ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ != USB_DIR_IN) { ++ dev_err(&intf->dev, "%s: endpoint doesn't match input device " ++ "02%02x\n", __func__, sz->endpoint->bEndpointAddress); ++ retval = -ENODEV; ++ goto free_sz; ++ } ++ ++ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ != USB_ENDPOINT_XFER_INT) { ++ dev_err(&intf->dev, "%s: endpoint attributes don't match xfer " ++ "02%02x\n", __func__, sz->endpoint->bmAttributes); ++ retval = -ENODEV; ++ goto free_sz; ++ } ++ ++ pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress); ++ maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe)); ++ ++ if (maxp == 0) { ++ dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n", ++ __func__); ++ retval = -ENODEV; ++ goto free_sz; ++ } ++ ++ /* Allocate the USB buffer and IRQ URB */ ++ sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in); ++ if (!sz->buf_in) ++ goto free_sz; ++ ++ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL); ++ if (!sz->urb_in) ++ goto free_buf_in; ++ ++ sz->dev = &intf->dev; ++ sz->buf_in_len = maxp; ++ ++ if (usbdev->descriptor.iManufacturer ++ && usb_string(usbdev, usbdev->descriptor.iManufacturer, ++ buf, sizeof(buf)) > 0) ++ strlcpy(name, buf, sizeof(name)); ++ ++ if (usbdev->descriptor.iProduct ++ && usb_string(usbdev, usbdev->descriptor.iProduct, ++ buf, sizeof(buf)) > 0) ++ snprintf(name + strlen(name), sizeof(name) - strlen(name), ++ " %s", buf); ++ ++ sz->idev = streamzap_init_input_dev(sz); ++ if (!sz->idev) ++ goto input_dev_fail; ++ ++ sz->idle = true; ++ sz->decoder_state = PulseSpace; ++ /* FIXME: don't yet have a way to set this */ ++ sz->timeout_enabled = true; ++ #if 0 ++ /* not yet supported, depends on patches from maxim */ ++ /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */ ++ sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000; ++ sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000; ++ #endif ++ ++ do_gettimeofday(&sz->signal_start); ++ ++ /* Complete final initialisations */ ++ usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in, ++ maxp, (usb_complete_t)streamzap_callback, ++ sz, sz->endpoint->bInterval); ++ sz->urb_in->transfer_dma = sz->dma_in; ++ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ usb_set_intfdata(intf, sz); ++ ++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) ++ dev_err(sz->dev, "urb submit failed\n"); ++ ++ dev_info(sz->dev, "Registered %s on usb%d:%d\n", name, ++ usbdev->bus->busnum, usbdev->devnum); ++ ++ /* Load the streamzap not-quite-rc5 decoder too */ ++ load_rc5_sz_decode(); ++ ++ return 0; ++ ++input_dev_fail: ++ usb_free_urb(sz->urb_in); ++free_buf_in: ++ usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in); ++free_sz: ++ kfree(sz); ++ ++ return retval; ++} ++ ++/** ++ * 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->usbdev. 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 streamzap_ir *sz = usb_get_intfdata(interface); ++ struct usb_device *usbdev = interface_to_usbdev(interface); ++ ++ usb_set_intfdata(interface, NULL); ++ ++ if (!sz) ++ return; ++ ++ sz->usbdev = NULL; ++ ir_input_unregister(sz->idev); ++ usb_kill_urb(sz->urb_in); ++ usb_free_urb(sz->urb_in); ++ usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in); ++ ++ kfree(sz); ++} ++ ++static int streamzap_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct streamzap_ir *sz = usb_get_intfdata(intf); ++ ++ usb_kill_urb(sz->urb_in); ++ ++ return 0; ++} ++ ++static int streamzap_resume(struct usb_interface *intf) ++{ ++ struct streamzap_ir *sz = usb_get_intfdata(intf); ++ ++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { ++ dev_err(sz->dev, "Error sumbiting urb\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/** ++ * streamzap_init ++ */ ++static int __init streamzap_init(void) ++{ ++ int ret; ++ ++ /* register this driver with the USB subsystem */ ++ ret = usb_register(&streamzap_driver); ++ if (ret < 0) ++ printk(KERN_ERR DRIVER_NAME ": usb register failed, " ++ "result = %d\n", ret); ++ ++ return ret; ++} ++ ++/** ++ * streamzap_exit ++ */ ++static void __exit streamzap_exit(void) ++{ ++ usb_deregister(&streamzap_driver); ++} ++ ++ ++module_init(streamzap_init); ++module_exit(streamzap_exit); ++ ++MODULE_AUTHOR("Jarod Wilson "); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Enable debugging messages"); +diff -Naurp linux-2.6.35.x86_64.orig/include/media/rc-map.h linux-2.6.35.x86_64/include/media/rc-map.h +--- linux-2.6.35.x86_64.orig/include/media/rc-map.h 2010-08-15 17:50:34.584382568 -0400 ++++ linux-2.6.35.x86_64/include/media/rc-map.h 2010-08-16 00:18:02.455993732 -0400 +@@ -17,12 +17,13 @@ + #define IR_TYPE_RC6 (1 << 2) /* Philips RC6 protocol */ + #define IR_TYPE_JVC (1 << 3) /* JVC protocol */ + #define IR_TYPE_SONY (1 << 4) /* Sony12/15/20 protocol */ ++#define IR_TYPE_RC5_SZ (1 << 5) /* RC5 variant used by Streamzap */ + #define IR_TYPE_LIRC (1 << 30) /* Pass raw IR to lirc userspace */ + #define IR_TYPE_OTHER (1u << 31) + + #define IR_TYPE_ALL (IR_TYPE_RC5 | IR_TYPE_NEC | IR_TYPE_RC6 | \ + IR_TYPE_JVC | IR_TYPE_SONY | IR_TYPE_LIRC | \ +- IR_TYPE_OTHER) ++ IR_TYPE_RC5_SZ | IR_TYPE_OTHER) + + struct ir_scancode { + u32 scancode; +@@ -115,6 +116,7 @@ void rc_map_init(void); + #define RC_MAP_RC5_TV "rc-rc5-tv" + #define RC_MAP_RC6_MCE "rc-rc6-mce" + #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" ++#define RC_MAP_STREAMZAP "rc-streamzap" + #define RC_MAP_TBS_NEC "rc-tbs-nec" + #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs" + #define RC_MAP_TEVII_NEC "rc-tevii-nec" diff --git a/lirc-staging-2.6.36.patch b/lirc-staging-2.6.36.patch index 23c80bc0e..8f7aa1236 100644 --- a/lirc-staging-2.6.36.patch +++ b/lirc-staging-2.6.36.patch @@ -1,7 +1,7 @@ drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + - drivers/staging/lirc/Kconfig | 111 +++ - drivers/staging/lirc/Makefile | 19 + + drivers/staging/lirc/Kconfig | 106 +++ + drivers/staging/lirc/Makefile | 18 + drivers/staging/lirc/TODO | 8 + drivers/staging/lirc/TODO.lirc_i2c | 3 + drivers/staging/lirc/lirc_bt829.c | 383 +++++++++ @@ -18,10 +18,9 @@ drivers/staging/lirc/lirc_sasem.c | 933 +++++++++++++++++++++ drivers/staging/lirc/lirc_serial.c | 1313 +++++++++++++++++++++++++++++ drivers/staging/lirc/lirc_sir.c | 1282 ++++++++++++++++++++++++++++ - drivers/staging/lirc/lirc_streamzap.c | 821 ++++++++++++++++++ drivers/staging/lirc/lirc_ttusbir.c | 396 +++++++++ drivers/staging/lirc/lirc_zilog.c | 1387 +++++++++++++++++++++++++++++++ - 23 files changed, 12034 insertions(+), 0 deletions(-) + 22 files changed, 11206 insertions(+), 0 deletions(-) diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 984a754..9296517 100644 @@ -52,7 +51,7 @@ new file mode 100644 index 0000000..ab30a09 --- /dev/null +++ b/drivers/staging/lirc/Kconfig -@@ -0,0 +1,111 @@ +@@ -0,0 +1,105 @@ +# +# LIRC driver(s) configuration +# @@ -145,12 +144,6 @@ index 0000000..ab30a09 + help + Driver for the SIR IrDA port + -+config LIRC_STREAMZAP -+ tristate "Streamzap PC Receiver" -+ depends on LIRC_STAGING && USB -+ help -+ Driver for the Streamzap PC Receiver -+ +config LIRC_TTUSBIR + tristate "Technotrend USB IR Receiver" + depends on LIRC_STAGING && USB @@ -169,7 +162,7 @@ new file mode 100644 index 0000000..a019182 --- /dev/null +++ b/drivers/staging/lirc/Makefile -@@ -0,0 +1,19 @@ +@@ -0,0 +1,18 @@ +# Makefile for the lirc drivers. +# + @@ -186,7 +179,6 @@ index 0000000..a019182 +obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o +obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o +obj-$(CONFIG_LIRC_SIR) += lirc_sir.o -+obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o +obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o +obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o diff --git a/drivers/staging/lirc/TODO b/drivers/staging/lirc/TODO @@ -9582,833 +9574,6 @@ index 0000000..eb08fa7 + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); -diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c -new file mode 100644 -index 0000000..be09c10 ---- /dev/null -+++ b/drivers/staging/lirc/lirc_streamzap.c -@@ -0,0 +1,821 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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_TIMEOUT 0xff -+#define STREAMZAP_RESOLUTION 256 -+ -+/* number of samples buffered */ -+#define STREAMZAP_BUF_LEN 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_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; -+ -+ /* 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 */ -+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 long streamzap_ioctl(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; -+ 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; -+ int data; -+ -+ spin_lock_irqsave(&sz->timer_lock, flags); -+ -+ if (!lirc_buffer_empty(sz->delay_buf) && -+ !lirc_buffer_full(sz->driver->rbuf)) { -+ lirc_buffer_read(sz->delay_buf, (unsigned char *) &data); -+ lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data); -+ } -+ if (!lirc_buffer_empty(sz->delay_buf)) { -+ while (lirc_buffer_available(sz->delay_buf) < -+ STREAMZAP_BUF_LEN / 2 && -+ !lirc_buffer_full(sz->driver->rbuf)) { -+ lirc_buffer_read(sz->delay_buf, -+ (unsigned char *) &data); -+ lirc_buffer_write(sz->driver->rbuf, -+ (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->driver->rbuf)) -+ wake_up(&sz->driver->rbuf->wait_poll); -+ -+ spin_unlock_irqrestore(&sz->timer_lock, flags); -+} -+ -+static 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(sz->delay_buf, (unsigned char *) &data); -+ if (!lirc_buffer_full(sz->driver->rbuf)) { -+ lirc_buffer_write(sz->driver->rbuf, -+ (unsigned char *) &data); -+ } else { -+ dprintk("buffer overflow", sz->driver->minor); -+ } -+ } -+ if (!empty) -+ wake_up(&sz->driver->rbuf->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)) { -+ int read_data; -+ -+ lirc_buffer_read(sz->delay_buf, -+ (unsigned char *) &read_data); -+ if (!lirc_buffer_full(sz->driver->rbuf)) { -+ lirc_buffer_write(sz->driver->rbuf, -+ (unsigned char *) &read_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) -+{ -+ 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) { -+ /* really long time */ -+ tmp = LIRC_SPACE(LIRC_VALUE_MASK); -+ } else { -+ tmp = (int) (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 = ((int) 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) -+{ -+ int space; -+ -+ space = ((int) 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. -+ */ -+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->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) { -+ int 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; -+ } -+ } -+ } -+ -+ usb_submit_urb(urb, GFP_ATOMIC); -+ -+ return; -+} -+ -+static const struct file_operations streamzap_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = streamzap_ioctl, -+ .read = lirc_dev_fop_read, -+ .write = lirc_dev_fop_write, -+ .poll = lirc_dev_fop_poll, -+ .open = lirc_dev_fop_open, -+ .release = lirc_dev_fop_close, -+}; -+ -+ -+/** -+ * 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; -+ struct usb_streamzap *sz; -+ struct lirc_driver *driver; -+ struct lirc_buffer *lirc_buf; -+ struct lirc_buffer *delay_buf; -+ char buf[63], name[128] = ""; -+ int retval = -ENOMEM; -+ int minor = 0; -+ -+ /* Allocate space for device driver specific data */ -+ sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL); -+ if (sz == NULL) -+ return -ENOMEM; -+ -+ 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 free_sz; -+ } -+ -+ 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 free_sz; -+ } -+ -+ 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 free_sz; -+ } -+ -+ if (sz->endpoint->wMaxPacketSize == 0) { -+ err("%s: endpoint message size==0? ", __func__); -+ retval = -ENODEV; -+ goto free_sz; -+ } -+ -+ /* Allocate the USB buffer and IRQ URB */ -+ -+ sz->buf_in_len = sz->endpoint->wMaxPacketSize; -+ sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len, -+ GFP_ATOMIC, &sz->dma_in); -+ if (sz->buf_in == NULL) -+ goto free_sz; -+ -+ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL); -+ if (sz->urb_in == NULL) -+ goto free_sz; -+ -+ /* Connect this device to the LIRC sub-system */ -+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); -+ if (!driver) -+ goto free_sz; -+ -+ lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); -+ if (!lirc_buf) -+ goto free_driver; -+ if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN)) -+ goto kfree_lirc_buf; -+ -+ delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); -+ if (!delay_buf) -+ goto free_lirc_buf; -+ if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN)) -+ goto kfree_delay_buf; -+ -+ sz->driver = driver; -+ strcpy(sz->driver->name, DRIVER_NAME); -+ sz->driver->minor = -1; -+ sz->driver->sample_rate = 0; -+ sz->driver->code_length = sizeof(int) * 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 = lirc_buf; -+ sz->delay_buf = delay_buf; -+ sz->driver->set_use_inc = &streamzap_use_inc; -+ sz->driver->set_use_dec = &streamzap_use_dec; -+ sz->driver->fops = &streamzap_fops; -+ sz->driver->dev = &interface->dev; -+ 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); -+ sz->urb_in->transfer_dma = sz->dma_in; -+ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ -+ 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); -+ -+ minor = lirc_register_driver(driver); -+ -+ if (minor < 0) -+ goto free_delay_buf; -+ -+ sz->driver->minor = minor; -+ -+ usb_set_intfdata(interface, sz); -+ -+ printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n", -+ sz->driver->minor, name, -+ udev->bus->busnum, sz->udev->devnum); -+ -+ return 0; -+ -+free_delay_buf: -+ lirc_buffer_free(sz->delay_buf); -+kfree_delay_buf: -+ kfree(delay_buf); -+free_lirc_buf: -+ lirc_buffer_free(sz->driver->rbuf); -+kfree_lirc_buf: -+ kfree(lirc_buf); -+free_driver: -+ kfree(driver); -+free_sz: -+ if (retval == -ENOMEM) -+ err("Out of memory"); -+ -+ if (sz) { -+ usb_free_urb(sz->urb_in); -+ usb_free_coherent(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->driver->minor); -+ -+ lirc_buffer_clear(sz->driver->rbuf); -+ lirc_buffer_clear(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->driver->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->driver->minor); -+ -+ if (sz->flush) { -+ sz->flush = 0; -+ del_timer_sync(&sz->flush_timer); -+ } -+ -+ usb_kill_urb(sz->urb_in); -+ -+ stop_timer(sz); -+ -+ sz->in_use--; -+} -+ -+static long streamzap_ioctl(struct file *filep, unsigned int cmd, -+ unsigned long arg) -+{ -+ int result = 0; -+ int val; -+ 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, (int *)arg); -+ if (result == 0) { -+ if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) -+ sz->timeout_enabled = 1; -+ else if (val == 0) -+ sz->timeout_enabled = 0; -+ else -+ result = -EINVAL; -+ } -+ break; -+ default: -+ return lirc_dev_fop_ioctl(filep, cmd, arg); -+ } -+ 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. -+ */ -+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_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->driver->rbuf); -+ -+ /* unregister from the USB sub-system */ -+ -+ usb_free_urb(sz->urb_in); -+ -+ usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in); -+ -+ minor = sz->driver->minor; -+ kfree(sz->driver->rbuf); -+ kfree(sz->driver); -+ kfree(sz->delay_buf); -+ 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(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->driver->rbuf); -+ 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; -+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { -+ dprintk("open result = -EIO error submitting urb", -+ sz->driver->minor); -+ return -EIO; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * 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"); diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/lirc/lirc_ttusbir.c new file mode 100644 index 0000000..e345ab9