Forked from HiassofT/linux-052-XBOX_remote_support.patch
Last active
May 18, 2017 23:31
-
-
Save CvH/49683cc6d4472389950e04c71d796625 to your computer and use it in GitHub Desktop.
lirc xbox patch for kernel 4.12
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig | |
index 3e350a9..935dcc2 100644 | |
--- a/drivers/staging/media/lirc/Kconfig | |
+++ b/drivers/staging/media/lirc/Kconfig | |
@@ -19,3 +19,10 @@ | |
Driver for the Zilog/Hauppauge IR Transmitter, found on | |
PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards | |
+ | |
+config LIRC_XBOX | |
+ tristate "XBOX USB IR Remote" | |
+ depends on LIRC && USB | |
+ help | |
+ Driver for the Microsoft XBOX USB IR Remote | |
+ | |
endif | |
diff --git a/drivers/staging/media/lirc/lirc_xbox.c b/drivers/staging/media/lirc/lirc_xbox.c | |
new file mode 100644 | |
index 0000000..c269d5d | |
--- /dev/null | |
+++ b/drivers/staging/media/lirc/lirc_xbox.c | |
@@ -0,0 +1,995 @@ | |
+/* | |
+ * lirc_xbox - USB remote support for LIRC | |
+ * (supports Microsoft XBOX DVD Dongle) | |
+ * | |
+ * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net> | |
+ * | |
+ * This driver was derived from: | |
+ * Vladimir Dergachev <volodya@minspring.com>'s 2002 | |
+ * "USB ATI Remote support" (input device) | |
+ * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002 | |
+ * "USB StreamZap remote driver" (LIRC) | |
+ * Artur Lipowski <alipowski@kki.net.pl>'s 2002 | |
+ * "lirc_dev" and "lirc_gpio" LIRC modules | |
+ * Michael Wojciechowski | |
+ * initial xbox support | |
+ * Vassilis Virvilis <vasvir@iit.demokritos.gr> 2006 | |
+ * reworked the patch for lirc submission | |
+ * Paul Miller's <pmiller9@users.sourceforge.net> 2004 | |
+ * lirc_atiusb - removed all ati remote support | |
+ * $Id: lirc_xbox.c,v 1.88 2011/06/03 11:11:11 jmartin Exp $ | |
+ */ | |
+ | |
+/* | |
+ * 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> | |
+ | |
+//#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) | |
+//#include <linux/autoconf.h> | |
+//#endif | |
+ | |
+#include <linux/kernel.h> | |
+#include <linux/errno.h> | |
+#include <linux/init.h> | |
+#include <linux/slab.h> | |
+#include <linux/module.h> | |
+#include <linux/kmod.h> | |
+#include <linux/completion.h> | |
+#include <linux/uaccess.h> | |
+#include <linux/usb.h> | |
+#include <linux/poll.h> | |
+#include <linux/wait.h> | |
+#include <linux/list.h> | |
+ | |
+//#include "drivers/kcompat.h" | |
+//#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35) | |
+#include <media/lirc.h> | |
+#include <media/lirc_dev.h> | |
+//#else | |
+//#include "drivers/lirc.h" | |
+//#include "drivers/lirc_dev/lirc_dev.h" | |
+//#endif | |
+ | |
+#define DRIVER_VERSION "$Revision: 0.01 $" | |
+#define DRIVER_AUTHOR "Jason Martin <austinspartan@users.sourceforge.net>" | |
+#define DRIVER_DESC "XBOX DVD Dongle USB remote driver for LIRC" | |
+#define DRIVER_NAME "lirc_xbox" | |
+ | |
+#define CODE_LENGTH 6 | |
+#define CODE_MIN_LENGTH 6 | |
+#define DECODE_LENGTH 1 | |
+ | |
+#ifndef URB_ASYNC_UNLINK | |
+#define URB_ASYNC_UNLINK 0 | |
+#endif | |
+ | |
+/* module parameters */ | |
+#ifdef CONFIG_USB_DEBUG | |
+static int debug = 1; | |
+#else | |
+static int debug; | |
+#endif | |
+ | |
+#define dprintk(fmt, args...) \ | |
+ do { \ | |
+ if (debug) \ | |
+ printk(KERN_DEBUG fmt, ## args); \ | |
+ } while (0) | |
+ | |
+/* | |
+ * USB_BUFF_LEN must be the maximum value of the code_length array. | |
+ * It is used for static arrays. | |
+ */ | |
+#define USB_BUFF_LEN 6 | |
+ | |
+static int mask = 0xFFFF; /* channel acceptance bit mask */ | |
+static int unique; /* enable channel-specific codes */ | |
+static int repeat = 10; /* repeat time in 1/100 sec */ | |
+static unsigned long repeat_jiffies; /* repeat timeout */ | |
+ | |
+/* get hi and low bytes of a 16-bits int */ | |
+#define HI(a) ((unsigned char)((a) >> 8)) | |
+#define LO(a) ((unsigned char)((a) & 0xff)) | |
+ | |
+/* general constants */ | |
+#define SEND_FLAG_IN_PROGRESS 1 | |
+#define SEND_FLAG_COMPLETE 2 | |
+#define FREE_ALL 0xFF | |
+ | |
+/* endpoints */ | |
+#define EP_KEYS 0 | |
+#define EP_MOUSE 1 | |
+#define EP_MOUSE_ADDR 0x81 | |
+#define EP_KEYS_ADDR 0x82 | |
+ | |
+/* USB vendor ids for XBOX DVD Dongles */ | |
+#define VENDOR_MS1 0x040b | |
+#define VENDOR_MS2 0x045e | |
+#define VENDOR_MS3 0xFFFF | |
+ | |
+static struct usb_device_id usb_remote_table[] = { | |
+ /* Gamester Xbox DVD Movie Playback Kit IR */ | |
+ { USB_DEVICE(VENDOR_MS1, 0x6521) }, | |
+ | |
+ /* Microsoft Xbox DVD Movie Playback Kit IR */ | |
+ { USB_DEVICE(VENDOR_MS2, 0x0284) }, | |
+ | |
+ /* | |
+ * Some Chinese manufacturer -- conflicts with the joystick from the | |
+ * same manufacturer | |
+ */ | |
+ { USB_DEVICE(VENDOR_MS3, 0xFFFF) }, | |
+ | |
+ /* Terminating entry */ | |
+ { } | |
+}; | |
+ | |
+/* init strings */ | |
+#define USB_OUTLEN 7 | |
+ | |
+static char init1[] = {0x01, 0x00, 0x20, 0x14}; | |
+static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20}; | |
+ | |
+struct in_endpt { | |
+ /* inner link in list of endpoints for the remote specified by ir */ | |
+ struct list_head iep_list_link; | |
+ struct xbox_dev *ir; | |
+ struct urb *urb; | |
+ struct usb_endpoint_descriptor *ep; | |
+ | |
+ /* buffers and dma */ | |
+ unsigned char *buf; | |
+ unsigned int len; | |
+ dma_addr_t dma; | |
+ | |
+ /* handle repeats */ | |
+ unsigned char old[USB_BUFF_LEN]; | |
+ unsigned long old_jiffies; | |
+}; | |
+ | |
+struct out_endpt { | |
+ struct xbox_dev *ir; | |
+ struct urb *urb; | |
+ struct usb_endpoint_descriptor *ep; | |
+ | |
+ /* buffers and dma */ | |
+ unsigned char *buf; | |
+ dma_addr_t dma; | |
+ | |
+ /* handle sending (init strings) */ | |
+ int send_flags; | |
+ wait_queue_head_t wait; | |
+}; | |
+ | |
+ | |
+/* data structure for each usb remote */ | |
+struct xbox_dev { | |
+ /* inner link in list of all remotes managed by this module */ | |
+ struct list_head remote_list_link; | |
+ /* Number of usb interfaces associated with this device */ | |
+ int dev_refcount; | |
+ | |
+ /* usb */ | |
+ struct usb_device *usbdev; | |
+ /* Head link to list of all inbound endpoints in this remote */ | |
+ struct list_head iep_listhead; | |
+ struct out_endpt *out_init; | |
+ int devnum; | |
+ | |
+ /* lirc */ | |
+ struct lirc_driver *d; | |
+ int connected; | |
+ | |
+ /* locking */ | |
+ struct mutex lock; | |
+}; | |
+ | |
+/* list of all registered devices via the remote_list_link in xbox_dev */ | |
+static struct list_head remote_list; | |
+ | |
+/* | |
+ * Convenience macros to retrieve a pointer to the surrounding struct from | |
+ * the given list_head reference within, pointed at by link. | |
+ */ | |
+#define get_iep_from_link(link) \ | |
+ list_entry((link), struct in_endpt, iep_list_link); | |
+#define get_irctl_from_link(link) \ | |
+ list_entry((link), struct xbox_dev, remote_list_link); | |
+ | |
+/* send packet - used to initialize remote */ | |
+static void send_packet(struct out_endpt *oep, u16 cmd, unsigned char *data) | |
+{ | |
+ struct xbox_dev *ir = oep->ir; | |
+ DECLARE_WAITQUEUE(wait, current); | |
+ int timeout = HZ; /* 1 second */ | |
+ unsigned char buf[USB_OUTLEN]; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd); | |
+ | |
+ mutex_lock(&ir->lock); | |
+ oep->urb->transfer_buffer_length = LO(cmd) + 1; | |
+ oep->urb->dev = oep->ir->usbdev; | |
+ oep->send_flags = SEND_FLAG_IN_PROGRESS; | |
+ | |
+ memcpy(buf+1, data, LO(cmd)); | |
+ buf[0] = HI(cmd); | |
+ memcpy(oep->buf, buf, LO(cmd)+1); | |
+ | |
+ set_current_state(TASK_INTERRUPTIBLE); | |
+ add_wait_queue(&oep->wait, &wait); | |
+ | |
+ if (usb_submit_urb(oep->urb, GFP_ATOMIC)) { | |
+ set_current_state(TASK_RUNNING); | |
+ remove_wait_queue(&oep->wait, &wait); | |
+ mutex_unlock(&ir->lock); | |
+ return; | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+ | |
+ while (timeout && (oep->urb->status == -EINPROGRESS) | |
+ && !(oep->send_flags & SEND_FLAG_COMPLETE)) { | |
+ timeout = schedule_timeout(timeout); | |
+ rmb(); | |
+ } | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: send complete (%#x)\n", ir->devnum, cmd); | |
+ | |
+ set_current_state(TASK_RUNNING); | |
+ remove_wait_queue(&oep->wait, &wait); | |
+ oep->urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(oep->urb); | |
+} | |
+ | |
+static int unregister_from_lirc(struct xbox_dev *ir) | |
+{ | |
+ struct lirc_driver *d = ir->d; | |
+ int devnum; | |
+ | |
+ devnum = ir->devnum; | |
+ dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum); | |
+ | |
+ lirc_unregister_driver(d->minor); | |
+ | |
+ printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum); | |
+ return 0; | |
+} | |
+ | |
+static int set_use_inc(void *data) | |
+{ | |
+ struct xbox_dev *ir = data; | |
+ struct list_head *pos, *n; | |
+ struct in_endpt *iep; | |
+ int rtn; | |
+ | |
+ if (!ir) { | |
+ printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); | |
+ return -EIO; | |
+ } | |
+ dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); | |
+ | |
+ mutex_lock(&ir->lock); | |
+ if (!ir->connected) { | |
+ if (!ir->usbdev) { | |
+ mutex_unlock(&ir->lock); | |
+ dprintk(DRIVER_NAME "[%d]: !ir->usbdev\n", ir->devnum); | |
+ return -ENOENT; | |
+ } | |
+ | |
+ /* Iterate through the inbound endpoints */ | |
+ list_for_each_safe(pos, n, &ir->iep_listhead) { | |
+ /* extract the current in_endpt */ | |
+ iep = get_iep_from_link(pos); | |
+ iep->urb->dev = ir->usbdev; | |
+ dprintk(DRIVER_NAME "[%d]: linking iep 0x%02x (%p)\n", | |
+ ir->devnum, iep->ep->bEndpointAddress, iep); | |
+ rtn = usb_submit_urb(iep->urb, GFP_ATOMIC); | |
+ if (rtn) { | |
+ printk(DRIVER_NAME "[%d]: open result = %d " | |
+ "error submitting urb\n", | |
+ ir->devnum, rtn); | |
+ mutex_unlock(&ir->lock); | |
+ return -EIO; | |
+ } | |
+ } | |
+ ir->connected = 1; | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void set_use_dec(void *data) | |
+{ | |
+ struct xbox_dev *ir = data; | |
+ struct list_head *pos, *n; | |
+ struct in_endpt *iep; | |
+ | |
+ if (!ir) { | |
+ printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); | |
+ return; | |
+ } | |
+ dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); | |
+ | |
+ mutex_lock(&ir->lock); | |
+ if (ir->connected) { | |
+ /* Free inbound usb urbs */ | |
+ list_for_each_safe(pos, n, &ir->iep_listhead) { | |
+ iep = get_iep_from_link(pos); | |
+ dprintk(DRIVER_NAME "[%d]: unlinking iep 0x%02x (%p)\n", | |
+ ir->devnum, iep->ep->bEndpointAddress, iep); | |
+ usb_kill_urb(iep->urb); | |
+ } | |
+ ir->connected = 0; | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+} | |
+ | |
+static void print_data(struct in_endpt *iep, char *buf, int len) | |
+{ | |
+ const int clen = CODE_LENGTH; | |
+ char codes[clen * 3 + 1]; | |
+ int i; | |
+ | |
+ if (len <= 0) | |
+ return; | |
+ | |
+ for (i = 0; i < len && i < clen; i++) | |
+ snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF); | |
+ printk(DRIVER_NAME "[%d]: data received %s (ep=0x%x length=%d)\n", | |
+ iep->ir->devnum, codes, iep->ep->bEndpointAddress, len); | |
+} | |
+ | |
+static int code_check_xbox(struct in_endpt *iep, int len) | |
+{ | |
+ // struct xbox_dev *ir = iep->ir; | |
+ const int clen = CODE_LENGTH; | |
+ | |
+ if (len != clen) { | |
+ dprintk(DRIVER_NAME ": We got %d instead of %d bytes from xbox " | |
+ "ir.. ?\n", len, clen); | |
+ return -1; | |
+ } | |
+ | |
+ /* check for repeats */ | |
+ if (memcmp(iep->old, iep->buf, len) == 0) { | |
+ if (iep->old_jiffies + repeat_jiffies > jiffies) | |
+ return -1; | |
+ } else { | |
+ /* | |
+ * the third byte of xbox ir packet seems to contain key info | |
+ * the last two bytes are.. some kind of clock? | |
+ */ | |
+ iep->buf[0] = iep->buf[2]; | |
+ memset(iep->buf + 1, 0, len - 1); | |
+ memcpy(iep->old, iep->buf, len); | |
+ } | |
+ iep->old_jiffies = jiffies; | |
+ | |
+ return 0; | |
+} | |
+ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs) | |
+#else | |
+static void usb_remote_recv(struct urb *urb) | |
+#endif | |
+{ | |
+ struct in_endpt *iep; | |
+ int len, result = -1; | |
+ | |
+ if (!urb) | |
+ return; | |
+ iep = urb->context; | |
+ if (!iep) { | |
+ urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(urb); | |
+ return; | |
+ } | |
+ if (!iep->ir->usbdev) | |
+ return; | |
+ | |
+ len = urb->actual_length; | |
+ if (debug) | |
+ print_data(iep, urb->transfer_buffer, len); | |
+ | |
+ switch (urb->status) { | |
+ | |
+ case 0: | |
+ result = code_check_xbox(iep, len); | |
+ | |
+ if (result < 0) | |
+ break; | |
+ | |
+ lirc_buffer_write(iep->ir->d->rbuf, iep->buf); | |
+ wake_up(&iep->ir->d->rbuf->wait_poll); | |
+ break; | |
+ | |
+ case -ECONNRESET: | |
+ case -ENOENT: | |
+ case -ESHUTDOWN: | |
+ urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(urb); | |
+ return; | |
+ | |
+ case -EPIPE: | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ usb_submit_urb(urb, GFP_ATOMIC); | |
+} | |
+ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
+static void usb_remote_send(struct urb *urb, struct pt_regs *regs) | |
+#else | |
+static void usb_remote_send(struct urb *urb) | |
+#endif | |
+{ | |
+ struct out_endpt *oep; | |
+ | |
+ if (!urb) | |
+ return; | |
+ oep = urb->context; | |
+ if (!oep) { | |
+ urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(urb); | |
+ return; | |
+ } | |
+ if (!oep->ir->usbdev) | |
+ return; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: usb out called\n", oep->ir->devnum); | |
+ | |
+ if (urb->status) | |
+ return; | |
+ | |
+ oep->send_flags |= SEND_FLAG_COMPLETE; | |
+ wmb(); | |
+ if (waitqueue_active(&oep->wait)) | |
+ wake_up(&oep->wait); | |
+} | |
+ | |
+ | |
+/* | |
+ * Initialization and removal | |
+ */ | |
+ | |
+/* | |
+ * Free iep according to mem_failure which specifies a checkpoint into the | |
+ * initialization sequence for rollback recovery. | |
+ */ | |
+static void free_in_endpt(struct in_endpt *iep, int mem_failure) | |
+{ | |
+ struct xbox_dev *ir; | |
+ dprintk(DRIVER_NAME ": free_in_endpt(%p, %d)\n", iep, mem_failure); | |
+ if (!iep) | |
+ return; | |
+ | |
+ ir = iep->ir; | |
+ if (!ir) { | |
+ dprintk(DRIVER_NAME ": free_in_endpt: WARNING! null ir\n"); | |
+ return; | |
+ } | |
+ mutex_lock(&ir->lock); | |
+ switch (mem_failure) { | |
+ case FREE_ALL: | |
+ case 5: | |
+ list_del(&iep->iep_list_link); | |
+ dprintk(DRIVER_NAME "[%d]: free_in_endpt removing ep=0x%0x " | |
+ "from list\n", ir->devnum, iep->ep->bEndpointAddress); | |
+ case 4: | |
+ if (iep->urb) { | |
+ iep->urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(iep->urb); | |
+ usb_free_urb(iep->urb); | |
+ iep->urb = 0; | |
+ } else | |
+ dprintk(DRIVER_NAME "[%d]: free_in_endpt null urb!\n", | |
+ ir->devnum); | |
+ case 3: | |
+ usb_free_coherent(iep->ir->usbdev, iep->len, iep->buf, iep->dma); | |
+ iep->buf = 0; | |
+ case 2: | |
+ kfree(iep); | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+} | |
+ | |
+/* | |
+ * Construct a new inbound endpoint for this remote, and add it to the list of | |
+ * in_epts in ir. | |
+ */ | |
+static struct in_endpt *new_in_endpt(struct xbox_dev *ir, | |
+ struct usb_endpoint_descriptor *ep) | |
+{ | |
+ struct usb_device *dev = ir->usbdev; | |
+ struct in_endpt *iep; | |
+ int pipe, maxp, len, addr; | |
+ int mem_failure; | |
+ | |
+ addr = ep->bEndpointAddress; | |
+ pipe = usb_rcvintpipe(dev, addr); | |
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); | |
+ | |
+/* len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp; | |
+ * len -= (len % CODE_LENGTH); */ | |
+ len = CODE_LENGTH; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: acceptable inbound endpoint (0x%x) found " | |
+ "(maxp=%d len=%d)\n", ir->devnum, addr, maxp, len); | |
+ | |
+ mem_failure = 0; | |
+ iep = kzalloc(sizeof(*iep), GFP_KERNEL); | |
+ if (!iep) { | |
+ mem_failure = 1; | |
+ goto new_in_endpt_failure_check; | |
+ } | |
+ iep->ir = ir; | |
+ iep->ep = ep; | |
+ iep->len = len; | |
+ | |
+ iep->buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &iep->dma); | |
+ if (!iep->buf) { | |
+ mem_failure = 2; | |
+ goto new_in_endpt_failure_check; | |
+ } | |
+ | |
+ iep->urb = usb_alloc_urb(0, GFP_KERNEL); | |
+ if (!iep->urb) | |
+ mem_failure = 3; | |
+ | |
+new_in_endpt_failure_check: | |
+ | |
+ if (mem_failure) { | |
+ free_in_endpt(iep, mem_failure); | |
+ printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n", | |
+ ir->devnum, addr, mem_failure); | |
+ return NULL; | |
+ } | |
+ list_add_tail(&iep->iep_list_link, &ir->iep_listhead); | |
+ dprintk(DRIVER_NAME "[%d]: adding ep=0x%0x to list\n", | |
+ ir->devnum, iep->ep->bEndpointAddress); | |
+ return iep; | |
+} | |
+ | |
+static void free_out_endpt(struct out_endpt *oep, int mem_failure) | |
+{ | |
+ struct xbox_dev *ir; | |
+ dprintk(DRIVER_NAME ": free_out_endpt(%p, %d)\n", oep, mem_failure); | |
+ if (!oep) | |
+ return; | |
+ | |
+ wake_up_all(&oep->wait); | |
+ | |
+ ir = oep->ir; | |
+ if (!ir) { | |
+ dprintk(DRIVER_NAME ": free_out_endpt: WARNING! null ir\n"); | |
+ return; | |
+ } | |
+ mutex_lock(&ir->lock); | |
+ switch (mem_failure) { | |
+ case FREE_ALL: | |
+ case 4: | |
+ if (oep->urb) { | |
+ oep->urb->transfer_flags |= URB_ASYNC_UNLINK; | |
+ usb_unlink_urb(oep->urb); | |
+ usb_free_urb(oep->urb); | |
+ oep->urb = 0; | |
+ } else { | |
+ dprintk(DRIVER_NAME "[%d]: free_out_endpt: null urb!\n", | |
+ ir->devnum); | |
+ } | |
+ case 3: | |
+ usb_free_coherent(oep->ir->usbdev, USB_OUTLEN, | |
+ oep->buf, oep->dma); | |
+ oep->buf = 0; | |
+ case 2: | |
+ kfree(oep); | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+} | |
+ | |
+static struct out_endpt *new_out_endpt(struct xbox_dev *ir, | |
+ struct usb_endpoint_descriptor *ep) | |
+{ | |
+ struct usb_device *dev = ir->usbdev; | |
+ struct out_endpt *oep; | |
+ int mem_failure; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: acceptable outbound endpoint (0x%x) found\n", | |
+ ir->devnum, ep->bEndpointAddress); | |
+ | |
+ mem_failure = 0; | |
+ oep = kzalloc(sizeof(*oep), GFP_KERNEL); | |
+ if (!oep) | |
+ mem_failure = 1; | |
+ else { | |
+ oep->ir = ir; | |
+ oep->ep = ep; | |
+ init_waitqueue_head(&oep->wait); | |
+ | |
+ oep->buf = usb_alloc_coherent(dev, USB_OUTLEN, | |
+ GFP_ATOMIC, &oep->dma); | |
+ if (!oep->buf) | |
+ mem_failure = 2; | |
+ else { | |
+ oep->urb = usb_alloc_urb(0, GFP_KERNEL); | |
+ if (!oep->urb) | |
+ mem_failure = 3; | |
+ } | |
+ } | |
+ if (mem_failure) { | |
+ free_out_endpt(oep, mem_failure); | |
+ printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n", | |
+ ir->devnum, ep->bEndpointAddress, mem_failure); | |
+ return NULL; | |
+ } | |
+ return oep; | |
+} | |
+ | |
+static void free_irctl(struct xbox_dev *ir, int mem_failure) | |
+{ | |
+ struct list_head *pos, *n; | |
+ struct in_endpt *in; | |
+ dprintk(DRIVER_NAME ": free_irctl(%p, %d)\n", ir, mem_failure); | |
+ | |
+ if (!ir) | |
+ return; | |
+ | |
+ list_for_each_safe(pos, n, &ir->iep_listhead) { | |
+ in = get_iep_from_link(pos); | |
+ free_in_endpt(in, FREE_ALL); | |
+ } | |
+ if (ir->out_init) { | |
+ free_out_endpt(ir->out_init, FREE_ALL); | |
+ ir->out_init = NULL; | |
+ } | |
+ | |
+ mutex_lock(&ir->lock); | |
+ switch (mem_failure) { | |
+ case FREE_ALL: | |
+ case 6: | |
+ if (!--ir->dev_refcount) { | |
+ list_del(&ir->remote_list_link); | |
+ dprintk(DRIVER_NAME "[%d]: free_irctl: removing " | |
+ "remote from list\n", ir->devnum); | |
+ } else { | |
+ dprintk(DRIVER_NAME "[%d]: free_irctl: refcount at %d," | |
+ "aborting free_irctl\n", | |
+ ir->devnum, ir->dev_refcount); | |
+ mutex_unlock(&ir->lock); | |
+ return; | |
+ } | |
+ case 5: | |
+ case 4: | |
+ case 3: | |
+ if (ir->d) { | |
+ switch (mem_failure) { | |
+ case 5: | |
+ lirc_buffer_free(ir->d->rbuf); | |
+ case 4: | |
+ kfree(ir->d->rbuf); | |
+ case 3: | |
+ kfree(ir->d); | |
+ } | |
+ } else | |
+ printk(DRIVER_NAME "[%d]: ir->d is a null pointer!\n", | |
+ ir->devnum); | |
+ case 2: | |
+ mutex_unlock(&ir->lock); | |
+ kfree(ir); | |
+ return; | |
+ } | |
+ mutex_unlock(&ir->lock); | |
+} | |
+ | |
+static struct xbox_dev *new_irctl(struct usb_interface *intf) | |
+{ | |
+ struct usb_device *dev = interface_to_usbdev(intf); | |
+ struct xbox_dev *ir; | |
+ struct lirc_driver *driver; | |
+ int devnum, dclen; | |
+ int mem_failure; | |
+ | |
+ devnum = dev->devnum; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: remote type = XBOX DVD Dongle\n", devnum); | |
+ | |
+ mem_failure = 0; | |
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL); | |
+ if (!ir) { | |
+ mem_failure = 1; | |
+ goto new_irctl_failure_check; | |
+ } | |
+ | |
+ dclen = DECODE_LENGTH; | |
+ | |
+ /* | |
+ * add this infrared remote struct to remote_list, keeping track | |
+ * of the number of drivers registered. | |
+ */ | |
+ dprintk(DRIVER_NAME "[%d]: adding remote to list\n", devnum); | |
+ list_add_tail(&ir->remote_list_link, &remote_list); | |
+ ir->dev_refcount = 1; | |
+ | |
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL); | |
+ if (!driver) { | |
+ mem_failure = 2; | |
+ goto new_irctl_failure_check; | |
+ } | |
+ | |
+ ir->d = driver; | |
+ driver->rbuf = kmalloc(sizeof(*(driver->rbuf)), GFP_KERNEL); | |
+ if (!driver->rbuf) { | |
+ mem_failure = 3; | |
+ goto new_irctl_failure_check; | |
+ } | |
+ | |
+ if (lirc_buffer_init(driver->rbuf, dclen, 2)) { | |
+ mem_failure = 4; | |
+ goto new_irctl_failure_check; | |
+ } | |
+ | |
+ strcpy(driver->name, DRIVER_NAME " "); | |
+ driver->minor = -1; | |
+ driver->code_length = dclen * 8; | |
+ driver->features = LIRC_CAN_REC_LIRCCODE; | |
+ driver->data = ir; | |
+ driver->set_use_inc = &set_use_inc; | |
+ driver->set_use_dec = &set_use_dec; | |
+ driver->dev = &intf->dev; | |
+ driver->owner = THIS_MODULE; | |
+ ir->usbdev = dev; | |
+ ir->devnum = devnum; | |
+ | |
+ mutex_init(&ir->lock); | |
+ INIT_LIST_HEAD(&ir->iep_listhead); | |
+ | |
+new_irctl_failure_check: | |
+ | |
+ if (mem_failure) { | |
+ free_irctl(ir, mem_failure); | |
+ printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", | |
+ devnum, mem_failure); | |
+ return NULL; | |
+ } | |
+ return ir; | |
+} | |
+ | |
+/* | |
+ * Scan the global list of remotes to see if the device listed is one of them. | |
+ * If it is, the corresponding xbox_dev is returned, with its dev_refcount | |
+ * incremented. Otherwise, returns null. | |
+ */ | |
+static struct xbox_dev *get_prior_reg_ir(struct usb_device *dev) | |
+{ | |
+ struct list_head *pos; | |
+ struct xbox_dev *ir = NULL; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: scanning remote_list...\n", dev->devnum); | |
+ list_for_each(pos, &remote_list) { | |
+ ir = get_irctl_from_link(pos); | |
+ if (ir->usbdev != dev) { | |
+ dprintk(DRIVER_NAME "[%d]: device %d isn't it...", | |
+ dev->devnum, ir->devnum); | |
+ ir = NULL; | |
+ } else { | |
+ dprintk(DRIVER_NAME "[%d]: prior instance found.\n", | |
+ dev->devnum); | |
+ ir->dev_refcount++; | |
+ break; | |
+ } | |
+ } | |
+ return ir; | |
+} | |
+ | |
+/* | |
+ * If the USB interface has an out endpoint for control. | |
+ */ | |
+static void send_outbound_init(struct xbox_dev *ir) | |
+{ | |
+ if (ir->out_init) { | |
+ struct out_endpt *oep = ir->out_init; | |
+ dprintk(DRIVER_NAME "[%d]: usb_remote_probe: initializing " | |
+ "outbound ep\n", ir->devnum); | |
+ usb_fill_int_urb(oep->urb, ir->usbdev, | |
+ usb_sndintpipe(ir->usbdev, oep->ep->bEndpointAddress), | |
+ oep->buf, USB_OUTLEN, usb_remote_send, | |
+ oep, oep->ep->bInterval); | |
+ oep->urb->transfer_dma = oep->dma; | |
+ oep->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |
+ | |
+ send_packet(oep, 0x8004, init1); | |
+ send_packet(oep, 0x8007, init2); | |
+ } | |
+} | |
+ | |
+/* Log driver and usb info */ | |
+static void log_usb_dev_info(struct usb_device *dev) | |
+{ | |
+ char buf[63], name[128] = ""; | |
+ | |
+ if (dev->descriptor.iManufacturer | |
+ && usb_string(dev, dev->descriptor.iManufacturer, | |
+ buf, sizeof(buf)) > 0) | |
+ strlcpy(name, buf, sizeof(name)); | |
+ if (dev->descriptor.iProduct | |
+ && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) | |
+ snprintf(name + strlen(name), sizeof(name) - strlen(name), | |
+ " %s", buf); | |
+ printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", dev->devnum, name, | |
+ dev->bus->busnum, dev->devnum); | |
+} | |
+ | |
+ | |
+static int usb_remote_probe(struct usb_interface *intf, | |
+ const struct usb_device_id *id) | |
+{ | |
+ struct usb_device *dev = interface_to_usbdev(intf); | |
+ struct usb_host_interface *idesc; | |
+ struct usb_endpoint_descriptor *ep; | |
+ struct in_endpt *iep; | |
+ struct xbox_dev *ir; | |
+ int i; | |
+ | |
+ dprintk(DRIVER_NAME "[%d]: usb_remote_probe: dev:%p, intf:%p, id:%p)\n", | |
+ dev->devnum, dev, intf, id); | |
+ | |
+ idesc = intf->cur_altsetting; | |
+ | |
+ /* Check if a usb remote has already been registered for this device */ | |
+ ir = get_prior_reg_ir(dev); | |
+ | |
+ if (!ir) { | |
+ ir = new_irctl(intf); | |
+ if (!ir) | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ /* | |
+ * step through the endpoints to find first in and first out endpoint | |
+ * of type interrupt transfer | |
+ */ | |
+ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { | |
+ ep = &idesc->endpoint[i].desc; | |
+ dprintk(DRIVER_NAME "[%d]: processing endpoint %d\n", | |
+ dev->devnum, i); | |
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | |
+ USB_DIR_IN) && | |
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | |
+ USB_ENDPOINT_XFER_INT)) { | |
+ | |
+ iep = new_in_endpt(ir, ep); | |
+ if (iep) | |
+ { | |
+ usb_fill_int_urb(iep->urb, dev, | |
+ usb_rcvintpipe(dev, | |
+ iep->ep->bEndpointAddress), | |
+ iep->buf, iep->len, usb_remote_recv, | |
+ iep, iep->ep->bInterval); | |
+ iep->urb->transfer_dma = iep->dma; | |
+ iep->urb->transfer_flags |= | |
+ URB_NO_TRANSFER_DMA_MAP; | |
+ } | |
+ } | |
+ | |
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | |
+ USB_DIR_OUT) && | |
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | |
+ USB_ENDPOINT_XFER_INT) && | |
+ (ir->out_init == NULL)) | |
+ ir->out_init = new_out_endpt(ir, ep); | |
+ } | |
+ if (list_empty(&ir->iep_listhead)) { | |
+ printk(DRIVER_NAME "[%d]: inbound endpoint not found\n", | |
+ ir->devnum); | |
+ free_irctl(ir, FREE_ALL); | |
+ return -ENODEV; | |
+ } | |
+ if (ir->dev_refcount == 1) { | |
+ ir->d->minor = lirc_register_driver(ir->d); | |
+ if (ir->d->minor < 0) { | |
+ free_irctl(ir, FREE_ALL); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ /* Note new driver registration in kernel logs */ | |
+ log_usb_dev_info(dev); | |
+ | |
+ /* outbound data (initialization) */ | |
+ send_outbound_init(ir); | |
+ } | |
+ | |
+ usb_set_intfdata(intf, ir); | |
+ return 0; | |
+} | |
+ | |
+static void usb_remote_disconnect(struct usb_interface *intf) | |
+{ | |
+ /* struct usb_device *dev = interface_to_usbdev(intf); */ | |
+ struct xbox_dev *ir = usb_get_intfdata(intf); | |
+ usb_set_intfdata(intf, NULL); | |
+ | |
+ dprintk(DRIVER_NAME ": disconnecting remote %d:\n", | |
+ (ir ? ir->devnum : -1)); | |
+ if (!ir || !ir->d) | |
+ return; | |
+ | |
+ if (ir->usbdev) { | |
+ /* Only unregister once */ | |
+ ir->usbdev = NULL; | |
+ unregister_from_lirc(ir); | |
+ } | |
+ | |
+ /* This also removes the current remote from remote_list */ | |
+ free_irctl(ir, FREE_ALL); | |
+} | |
+ | |
+static struct usb_driver usb_remote_driver = { | |
+ .name = DRIVER_NAME, | |
+ .probe = usb_remote_probe, | |
+ .disconnect = usb_remote_disconnect, | |
+ .id_table = usb_remote_table | |
+}; | |
+ | |
+static int __init usb_remote_init(void) | |
+{ | |
+ int i; | |
+ | |
+ INIT_LIST_HEAD(&remote_list); | |
+ | |
+ printk(KERN_INFO "\n" DRIVER_NAME ": " DRIVER_DESC " " | |
+ DRIVER_VERSION "\n"); | |
+ printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n"); | |
+ dprintk(DRIVER_NAME ": debug mode enabled: " | |
+ "$Id: lirc_xbox.c,v 1.88 2011/06/05 11:11:11 jmartin Exp $\n"); | |
+ | |
+ repeat_jiffies = repeat*HZ/100; | |
+ | |
+ i = usb_register(&usb_remote_driver); | |
+ if (i) { | |
+ printk(DRIVER_NAME ": usb register failed, result = %d\n", i); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void __exit usb_remote_exit(void) | |
+{ | |
+ usb_deregister(&usb_remote_driver); | |
+} | |
+ | |
+module_init(usb_remote_init); | |
+module_exit(usb_remote_exit); | |
+ | |
+MODULE_DESCRIPTION(DRIVER_DESC); | |
+MODULE_AUTHOR(DRIVER_AUTHOR); | |
+MODULE_LICENSE("GPL"); | |
+MODULE_DEVICE_TABLE(usb, usb_remote_table); | |
+ | |
+module_param(debug, uint, S_IRUGO | S_IWUSR); | |
+MODULE_PARM_DESC(debug, "Debug enabled or not (default: 0)"); | |
+ | |
+module_param(mask, uint, S_IRUGO | S_IWUSR); | |
+MODULE_PARM_DESC(mask, "Set channel acceptance bit mask (default: 0xFFFF)"); | |
+ | |
+module_param(unique, uint, S_IRUGO | S_IWUSR); | |
+MODULE_PARM_DESC(unique, "Enable channel-specific codes (default: 0)"); | |
+ | |
+module_param(repeat, uint, S_IRUGO | S_IWUSR); | |
+MODULE_PARM_DESC(repeat, "Repeat timeout (1/100 sec) (default: 10)"); | |
diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile | |
index 6655624..9e9fc05 100644 | |
--- a/drivers/staging/media/lirc/Makefile | |
+++ b/drivers/staging/media/lirc/Makefile | |
@@ -6 +6,2 @@ | |
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o | |
+obj-$(CONFIG_LIRC_XBOX) += lirc_xbox.o |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment