Hi, I'm trying to reverse-engineer a USB remote control receiver. Fortunately the receiver consists of 1 configuration/1 interface/1 interrupt-input endpoint, and no additional setting is required to read remote control signals from it.
First I used libusb to read input and I succeded. With libusb, I just open the device and usb_bulk_read() to read data. But when I tried to write a kernel driver, I failed. The interrupt routine registered with FILL_INT_URB() has never been called. What is the difference between these? I'm mostly sure that the kernel driver was correct, because I copied code from the working driver (streamzap remocon driver at http://sf.net/projects/szremote). The sources of the libusb test program and kernel driver were attached. Here is the /proc/bus/usb/devices entry: T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 6 Spd=1.5 MxCh= 0 D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=04e8 ProdID=ff30 Rev= 0.14 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA I: If#= 1 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=10 Prot=ff Driver=usbdevfs E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=10ms Regards, -- Changwoo Ryu <[EMAIL PROTECTED]>
#include <stdio.h> #include <stdlib.h> #include <usb.h> struct usb_device *dev; usb_dev_handle *dev_handle; int ep_address; static void dump_input(void) { unsigned char buffer[256]; int len, i; while (1) { len = usb_bulk_read(dev_handle, ep_address, buffer, 8, 10000); if (len <= 0) continue; if (buffer[7] == 255) continue; #if 0 if (buffer[7] == 1) continue; if (buffer[1] == 255 && buffer[2] == 255 && buffer[3] == 255 && buffer[4] == 255) continue; #endif if (buffer[7] == 1) printf("------------------------------\n"); for (i = 0; i < len; i++) { printf("%02X ", buffer[i]); } printf("\n"); } } int main(int argc, char *argv[]) { struct usb_bus *busses, *bus; usb_init(); usb_find_busses(); usb_find_devices(); busses = usb_get_busses(); for (bus = busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.idVendor == 0x04e8 && dev->descriptor.idProduct == 0xff30) { goto found_dev; } } } exit(0); found_dev: dev_handle = usb_open(dev); usb_set_configuration(dev_handle, 1); usb_claim_interface(dev_handle, 1); ep_address = dev->config->interface->altsetting->endpoint->bEndpointAddress; usb_resetep(dev_handle, ep_address); dump_input(); usb_release_interface(dev_handle, 1); usb_close(dev_handle); exit(0); } /* * Local Variables: * mode: linux-c * End: */
/* lirc_imon - USB Soundgraph iMON remote driver * based on USB streamzap remote driver by Adrian Dewhurst * * Copyright (C) 2002-2003 Adrian Dewhurst <[EMAIL PROTECTED]> * Copyright (C) 2003 Changwoo Ryu * * 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, 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/config.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/slab.h> static int debug = 1; #define DRIVER_VERSION "v0.01" #define DRIVER_AUTHOR "Changwoo Ryu, <[EMAIL PROTECTED]>" #define DRIVER_DESC "lirc USB Soundgraph iMON remote driver" #define USB_IMON_VENDOR_ID 0x04e8 /* Samsung Electronics? */ #define USB_IMON_PRODUCT_ID 0xff30 static struct usb_device_id imon_table[] = { { USB_DEVICE(USB_IMON_VENDOR_ID, USB_IMON_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, imon_table); struct usb_imon { struct usb_device *udev; /* usb device struct; NULL upon disconnect */ #if 0 struct lirc_plugin plugin; /* our lirc_dev device info */ #endif struct urb irq; /* our interrupt URB */ __u16 int_in_size; /* size of interrupt buffer */ unsigned char *int_in_buffer; /* URB interrupt buffer */ unsigned char tmpbuf; /* for tracking long codes between interrupts */ unsigned char ign; #if 0 struct lirc_buffer outbuf; /* kernel space -> user space buffer */ #endif spinlock_t interrupt_lock; unsigned int open_count; struct semaphore lock; /* locks this structure */ }; /* USB functions */ static void imon_irq(struct urb *urb); static void * imon_probe(struct usb_device *dev, unsigned intf, const struct usb_device_id *id); static void imon_disconnect(struct usb_device *dev, void *data); static struct usb_driver imon_driver = { owner: THIS_MODULE, name: "lirc_imon", probe: imon_probe, disconnect: imon_disconnect, id_table: imon_table, }; /* LIRC functions */ #if 0 static struct lirc_plugin imon_lirc_template = { minor: -1, sample_rate: 0, code_length: 32, features: LIRC_CAN_REC_MODE2, data: NULL, rbuf: NULL, set_use_inc: imon_use_inc, set_use_dec: imon_use_dec, }; #endif static void * imon_probe(struct usb_device *dev, unsigned intf, const struct usb_device_id *id) { struct usb_interface *iface; struct usb_interface_descriptor *iface_desc; struct usb_endpoint_descriptor *endpoint; struct usb_imon *imon = NULL; int pipe; /* Get the device */ iface = &dev->actconfig->interface[intf]; iface_desc = &iface->altsetting[iface->act_altsetting]; /* Find out if we want it */ if ((dev->descriptor.idVendor != USB_IMON_VENDOR_ID) || (dev->descriptor.idProduct != USB_IMON_PRODUCT_ID)) { return NULL; } /* Find the endpoint */ if (iface_desc->bNumEndpoints != 1) return NULL; endpoint = iface_desc->endpoint + 0; if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN) return NULL; if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return NULL; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* Allocate our device struct */ if (!(imon = kmalloc(sizeof(*imon), GFP_KERNEL))) { err("Out of memory allocating iMON device"); return NULL; } memset(imon, 0, sizeof(*imon)); /* Initialize locks and data */ imon->udev = dev; init_MUTEX(&imon->lock); spin_lock_init(&imon->interrupt_lock); /* Allocate the URB buffer */ imon->int_in_size = 128; if (!(imon->int_in_buffer = kmalloc(imon->int_in_size, GFP_KERNEL))) { err("Out of memory allocating imon interrupt buffer"); goto error; } FILL_INT_URB(&imon->irq, dev, pipe, imon->int_in_buffer, imon->int_in_size, imon_irq, imon, endpoint->bInterval); goto exit; error: kfree(imon); imon = NULL; exit: return imon; } static void imon_disconnect(struct usb_device *dev, void *data) { struct usb_imon *imon = (struct usb_imon*)data; imon->udev = NULL; kfree(imon); } static inline void usb_streamzap_debug_data(const char *function, int size, const unsigned char *data) { int i; if (!debug) return; printk(KERN_DEBUG __FILE__": %s: length %d, data ", function, size); for (i = 0; i < size; ++i) { printk("%.2x ", data[i]); } printk("\n"); } static void imon_irq(struct urb *urb) { struct usb_imon *imon = urb->context; printk("imon_irq called\n"); if (urb->status) return; if (urb->actual_length == 0) return; if (!imon) { dbg("imon_irq called with no context"); usb_unlink_urb(urb); return; } usb_streamzap_debug_data(__FUNCTION__, urb->actual_length, urb->transfer_buffer); } static int __init imon_init(void) { int result; if ((result = usb_register(&imon_driver)) < 0) { err("usb_register failed. Error number %d", result); return -EINVAL; } return 0; } static void __exit imon_exit(void) { usb_deregister(&imon_driver); dbg("Exiting"); } module_init(imon_init); module_exit(imon_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* * Local Variables: * mode: c * c-default-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */