diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c --- linux-2.6.15.orig/drivers/usb/input/hid-core.c 2006-01-03 11:21:10.000000000 +0800 +++ linux-2.6.15/drivers/usb/input/hid-core.c 2006-01-04 16:59:03.000000000 +0800 @@ -4,6 +4,8 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2005 Liyu + * To support simple HID device driver interface. */ /* @@ -35,6 +37,8 @@ #include "hid.h" #include +#include "hid-simple.h" + /* * Version Information */ @@ -46,6 +50,15 @@ static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; + +/* + * The global data structure for simple device driver interface. + */ +static spinlock_t matched_lock; +static spinlock_t simple_lock; +static struct list_head matched_devices_list; +static struct list_head simple_devices_list; + /* * Module parameters. */ @@ -793,8 +806,11 @@ static __inline__ int search(__s32 *arra static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs) { hid_dump_input(usage, value); - if (hid->claimed & HID_CLAIMED_INPUT) + if (hid->claimed & HID_CLAIMED_INPUT) { hidinput_hid_event(hid, field, usage, value, regs); + if (hid->simple && hid->simple->event) + hid->simple->event(hid, field, usage, value, regs); + } if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt) hiddev_hid_event(hid, field, usage, value, regs); } @@ -1820,6 +1836,8 @@ fail: static void hid_disconnect(struct usb_interface *intf) { struct hid_device *hid = usb_get_intfdata (intf); + struct list_head *node; + struct matched_device *matched; if (!hid) return; @@ -1829,8 +1847,38 @@ static void hid_disconnect(struct usb_in usb_kill_urb(hid->urbout); usb_kill_urb(hid->urbctrl); - if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_disconnect(hid); + if (hid->claimed & HID_CLAIMED_INPUT) { + /* disconnect simple device if need */ + if (hid->simple) { + if (hid->simple->disconnect) + hid->simple->disconnect(hid); + /* save them to matche other hid_device later */ + spin_lock(&simple_lock); + list_add(&hid->simple->node, &simple_devices_list); + spin_unlock(&simple_lock); + hid->simple->intf = NULL; + hid->simple = NULL; + } + /* scan matched_devices_list to free our matched_device */ + matched = NULL; /* shut up gcc */ + + spin_lock(&matched_lock); + list_for_each(node, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + if (matched->intf == intf) { + list_del(&matched->node); + break; + } + matched = NULL; + } + spin_unlock(&matched_lock); + if (matched) { + matched->intf = 0; + kfree(matched); + } + hidinput_disconnect(hid); + } + if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_disconnect(hid); @@ -1843,13 +1891,162 @@ static void hid_disconnect(struct usb_in hid_free_device(hid); } +static void +hidinput_simple_device_bind_foreach(void) +{ + struct hidinput_simple_device *simple=0; + struct matched_device *matched=0; + struct list_head *simple_node; + struct list_head *matched_node; + struct hid_device *hid; + + spin_lock(&matched_lock); + list_for_each(matched_node, &matched_devices_list) { + matched = list_entry(matched_node, struct matched_device, node); + spin_lock(&simple_lock); + list_for_each(simple_node, &simple_devices_list) { + simple = list_entry(simple_node, struct hidinput_simple_device, node); + if (usb_match_id(matched->intf, simple->id)) + break; + simple = 0; + } + spin_unlock(&simple_lock); + if (simple) + break; + } + spin_unlock(&matched_lock); + /* no such simple device match this hid_device, also ok */ + if (0 == simple) + return; + + hid = usb_get_intfdata(matched->intf); + if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) { + simple->intf = matched->intf; + hid->simple = simple; + spin_lock(&simple_lock); + list_del(&simple->node); + spin_unlock(&simple_lock); + hidinput_simple_device_setup_usage(hid); + printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name); + } +} + +static void +hidinput_simple_device_bind(struct usb_interface *intf) +{ + struct hidinput_simple_device *simple; + struct list_head *node; + struct hid_device *hid; + + if (!intf) + return; + + simple = 0; + spin_lock(&simple_lock); + list_for_each(node, &simple_devices_list) { + simple = list_entry(node, struct hidinput_simple_device, node); + if (usb_match_id(intf, simple->id)) + break; + simple = 0; + } + spin_unlock(&simple_lock); + /* no such simple device match this hid_device, also ok */ + if (0 == simple) + return; + + hid = usb_get_intfdata (intf); + if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) { + simple->intf = intf; + hid->simple = simple; + spin_lock(&simple_lock); + list_del(&simple->node); + spin_unlock(&simple_lock); + hidinput_simple_device_setup_usage(hid); + printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name); + } +} + +int +hidinput_register_simple_device(struct hidinput_simple_device *simple) +{ + struct list_head *node; + struct matched_device *matched; + + if (!simple || !simple->name || simple->intf) + return -1; + + simple->flags = 0; + matched = 0; + + spin_lock(&matched_lock); + list_for_each(node, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + if (usb_match_id(matched->intf, simple->id)) + break; + matched = 0; + } + spin_unlock(&matched_lock); + + if (matched) {/* We called hid_probe() on this usb_interface ago */ + struct hid_device *hid; + + hid = usb_get_intfdata (matched->intf); + if (hid->simple) + goto device_busy; + if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) { + simple->intf = matched->intf; + hid->simple = simple; + hidinput_simple_device_setup_usage(hid); + printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name); + return 0; + } + } + return 0; + + device_busy: + spin_lock(&simple_lock); + list_add(&simple->node, &simple_devices_list); + spin_unlock(&simple_lock); + return 1; +} + +EXPORT_SYMBOL(hidinput_register_simple_device); + +void +hidinput_unregister_simple_device(struct hidinput_simple_device *simple) +{ + if (simple->intf) { + struct hid_device *hid; + hid = usb_get_intfdata (simple->intf); + + if (hid->simple != simple) + printk(KERN_ERR"failed to check simple device for consistency at %s %d \n", __FUNCTION__, __LINE__); + hidinput_simple_device_clear_usage(hid); + if (simple->disconnect) + simple->disconnect(hid); + hid->simple = 0; + simple->intf = 0; + simple->flags = 0; + } else { + spin_lock(&simple_lock); + list_del(&simple->node); + spin_unlock(&simple_lock); + } + /* to active simple device that matched current HID device is waiting */ + hidinput_simple_device_bind_foreach(); + printk(KERN_INFO"The simple HID device \'%s\' unregistered.\n", simple->name); +} + +EXPORT_SYMBOL(hidinput_unregister_simple_device); + static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct hid_device *hid; char path[64]; int i; char *c; - + struct matched_device *matched; + dbg("HID probe called for ifnum %d", intf->altsetting->desc.bInterfaceNumber); @@ -1858,14 +2055,23 @@ static int hid_probe(struct usb_interfac hid_init_reports(hid); hid_dump_device(hid); - - if (!hidinput_connect(hid)) + + usb_set_intfdata(intf, hid); + + if (!hidinput_connect(hid)) { + matched = kmalloc(sizeof(struct matched_device), GFP_KERNEL); + if (matched) { + matched->intf = intf; + spin_lock(&matched_lock); + list_add(&matched->node, &matched_devices_list); + spin_unlock(&matched_lock); + hidinput_simple_device_bind(intf); + } hid->claimed |= HID_CLAIMED_INPUT; + } if (!hiddev_connect(hid)) hid->claimed |= HID_CLAIMED_HIDDEV; - usb_set_intfdata(intf, hid); - if (!hid->claimed) { printk ("HID device not claimed by input or hiddev\n"); hid_disconnect(intf); @@ -1945,6 +2151,12 @@ static int __init hid_init(void) retval = hiddev_init(); if (retval) goto hiddev_init_fail; + + spin_lock_init(&matched_lock); + spin_lock_init(&simple_lock); + INIT_LIST_HEAD(&matched_devices_list); + INIT_LIST_HEAD(&simple_devices_list); + retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h --- linux-2.6.15.orig/drivers/usb/input/hid.h 2006-01-03 11:21:10.000000000 +0800 +++ linux-2.6.15/drivers/usb/input/hid.h 2006-01-04 10:17:10.000000000 +0800 @@ -6,6 +6,7 @@ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2005 Liyu To support simple HID device. */ /* @@ -374,6 +375,8 @@ struct hid_input { struct input_dev *input; }; +struct hidinput_simple_device; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -431,6 +434,7 @@ struct hid_device { /* device repo void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ int (*ff_event)(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); + struct hidinput_simple_device *simple; }; #define HID_GLOBAL_STACK_SIZE 4 @@ -471,7 +475,6 @@ struct hid_descriptor { #define resolv_event(a,b) do { } while (0) #endif -#endif #ifdef CONFIG_USB_HIDINPUT /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ @@ -515,3 +518,5 @@ static inline int hid_ff_event(struct hi return hid->ff_event(hid, input, type, code, value); return -ENOSYS; } + +#endif diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c --- linux-2.6.15.orig/drivers/usb/input/hid-input.c 2006-01-03 11:21:10.000000000 +0800 +++ linux-2.6.15/drivers/usb/input/hid-input.c 2006-01-04 10:17:11.000000000 +0800 @@ -36,6 +36,7 @@ #undef DEBUG #include "hid.h" +#include "hid-simple.h" #define unk KEY_UNKNOWN @@ -676,6 +677,49 @@ int hidinput_connect(struct hid_device * return 0; } +/* + * To give one simple device a configure usage chance. + * The most code of this function is copied from hidinput_connect() + */ +void hidinput_simple_device_configure_usage(struct hid_device *hid) +{ + struct hid_report *report; + int i, j, k; + void (*do_usage)(struct hid_field *, struct hid_usage *); + + if (!hid->simple) + return; + do_usage = 0; + if (hid->simple->flags & HIDINPUT_SIMPLE_SETUP_USAGE) + do_usage = hid->simple->setup_usage; + else + do_usage = hid->simple->clear_usage; + if (!do_usage) + return; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == HID_COLLECTION_APPLICATION || + hid->collection[i].type == HID_COLLECTION_PHYSICAL) + if (IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; + + if (i == hid->maxcollection) + return; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + + if (!report->maxfield) + continue; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + do_usage(report->field[i], report->field[i]->usage + j); + } + + return; +} + void hidinput_disconnect(struct hid_device *hid) { struct hid_input *hidinput, *next; diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-simple.h linux-2.6.15/drivers/usb/input/hid-simple.h --- linux-2.6.15.orig/drivers/usb/input/hid-simple.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.15/drivers/usb/input/hid-simple.h 2006-01-04 10:17:11.000000000 +0800 @@ -0,0 +1,62 @@ +#ifndef __HID_SIMPLE_H +#define __HID_SIMPLE_H + +#include +#include "hid.h" + +/************ The private section for simple device implement only **************/ + +/* The element of matched_device list is inserted at hidinput_connect(), + and is removed at hidinput_disconnect(). + */ +struct matched_device { + struct usb_interface *intf; + struct list_head node; +}; + +/* simple device internal flags */ +#define HIDINPUT_SIMPLE_SETUP_USAGE 0x1 /* the reverse is to call clear_usage */ + +#define hidinput_simple_device_setup_usage(hid) \ +do {\ + if (hid->simple) {\ + hid->simple->flags |= HIDINPUT_SIMPLE_SETUP_USAGE; \ + hidinput_simple_device_configure_usage(hid); \ + }\ +} while (0) + +#define hidinput_simple_device_clear_usage(hid) \ +do {\ + if (hid->simple) {\ + hid->simple->flags &= (~HIDINPUT_SIMPLE_SETUP_USAGE); \ + hidinput_simple_device_configure_usage(hid); \ + }\ +} while (0) + +/* It is defined at hid_input.c, however is called at hid-core.c */ +void hidinput_simple_device_configure_usage(struct hid_device *hid); + +/******************** The private section end. *****************************/ + + +/********************* The public interface for simple device driver ***********/ +struct hidinput_simple_device { +/* private */ + struct list_head node; + struct usb_interface *intf; + int flags; +/* public */ + char *name; + int (*connect)(struct hid_device *); + void (*setup_usage)(struct hid_field *, struct hid_usage *); + void (*event)(const struct hid_device *, const struct hid_field *, const struct hid_usage *, const __s32, const struct pt_regs *regs); + void (*clear_usage)(struct hid_field *, struct hid_usage *); + void (*disconnect)(struct hid_device *); + struct usb_device_id id[2]; /* variable length member */ +}; + +int hidinput_register_simple_device(struct hidinput_simple_device *device); +void hidinput_unregister_simple_device(struct hidinput_simple_device *device); +/********************* The public section end ***********/ + +#endif /* __HID_SIMPLE_H */