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:
*/