diff -Naurp input.orig/hid-core.c input/hid-core.c --- input.orig/hid-core.c 2006-07-16 03:00:43.000000000 +0800 +++ input/hid-core.c 2006-07-24 14:01:22.000000000 +0800 @@ -4,6 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006 Liyu For support simple HID device driver interface. */ /* @@ -26,6 +27,7 @@ #include #include #include +#include #undef DEBUG #undef DEBUG_DATA @@ -34,6 +36,7 @@ #include "hid.h" #include +#include "hid-simple.h" /* * Version Information @@ -46,6 +49,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 DECLARE_MUTEX(matched_lock); +static DECLARE_MUTEX(simple_lock); +static struct list_head matched_devices_list; +static struct list_head simple_drivers_list; + /* * Module parameters. */ @@ -785,8 +797,14 @@ 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) { + if (hid->simple && hid->simple->pre_event && + !hid->simple->pre_event(hid, field, usage, value, regs)) + return; hidinput_hid_event(hid, field, usage, value, regs); + if (hid->simple && hid->simple->post_event) + hid->simple->post_event(hid, field, usage, value, regs); + } if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt) hiddev_hid_event(hid, field, usage, value, regs); } @@ -832,7 +850,6 @@ static void hid_input_field(struct hid_d && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs); - if (value[n] >= min && value[n] <= max && field->usage[value[n] - min].hid && search(field->value, value[n], count)) @@ -1977,6 +1994,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; @@ -1991,8 +2010,29 @@ static void hid_disconnect(struct usb_in del_timer_sync(&hid->io_retry); flush_scheduled_work(); - if (hid->claimed & HID_CLAIMED_INPUT) + if (hid->claimed & HID_CLAIMED_INPUT) { + matched = NULL; + down(&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; + } + up(&matched_lock); + /* disconnect simple device driver if need */ + if (matched && hid->simple) { + hidinput_simple_driver_disconnect(hid); + hidinput_simple_driver_pop(hid->simple, matched); + } + if (matched) { + matched->intf = NULL; + kfree(matched); + } hidinput_disconnect(hid); + } if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_disconnect(hid); @@ -2005,12 +2045,138 @@ static void hid_disconnect(struct usb_in hid_free_device(hid); } + +/* called when we unregister a hidinput simple device driver */ +static void +hidinput_simple_driver_bind_foreach(void) +{ + struct hidinput_simple_driver *simple; + struct matched_device *matched=NULL; + struct list_head *matched_node = NULL; + struct list_head *simple_node = NULL; + struct hid_device *hid=NULL; + + down(&matched_lock); + list_for_each(matched_node, &matched_devices_list) { + matched = list_entry(matched_node, struct matched_device, node); + hid = usb_get_intfdata(matched->intf); + printk(KERN_DEBUG"foreach: %s\n", hid->name); + if (hid->simple) + continue; + down(&simple_lock); + list_for_each(simple_node, &simple_drivers_list) { + simple = list_entry(simple_node, struct hidinput_simple_driver, node); + printk(KERN_DEBUG"foreach inner: %s\n", simple->name); + if (!usb_match_id(matched->intf, simple->id_table)) + continue; + if (hidinput_simple_driver_connect(simple, hid)) + continue; + if (hidinput_simple_driver_push(simple, matched)) + continue; + hidinput_simple_driver_setup_usage(hid); + printk(KERN_INFO"The simple HID driver \'%s\' attach on \'%s\'\n", simple->name, hid->name); + } + up(&simple_lock); + } + up(&matched_lock); + +} + +/* called in hid_probe() */ +static void +hidinput_simple_driver_bind(struct matched_device *matched) +{ + struct hidinput_simple_driver *simple; + struct list_head *node; + struct hid_device *hid; + + if (!matched->intf) + return; + hid = usb_get_intfdata (matched->intf); + if (hid->simple) + return; + + simple = NULL; + down(&simple_lock); + list_for_each(node, &simple_drivers_list) { + simple = list_entry(node, struct hidinput_simple_driver, node); + if (usb_match_id(matched->intf, simple->id_table)) + break; + simple = NULL; + } + up(&simple_lock); + + if (!simple) + return; + if (hidinput_simple_driver_connect(simple, hid)) + return; + if (hidinput_simple_driver_push(simple, matched)) + return; + hidinput_simple_driver_setup_usage(hid); + + printk(KERN_INFO"The simple HID driver \'%s\' attach on \'%s\'\n", simple->name, hid->name); +} + +int +hidinput_register_simple_driver(struct hidinput_simple_driver *simple) +{ + struct list_head *node=NULL; + struct matched_device *matched; + struct hid_device *hid=NULL; + int ret=-ENODEV; + + if (!simple || !simple->name) + return -EINVAL; + + hidinput_simple_driver_init(simple); + + down(&matched_lock); + list_for_each(node, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + hid = usb_get_intfdata (matched->intf); + if (hid->simple) + continue; + if (!usb_match_id(matched->intf, simple->id_table)) + continue; + if (hidinput_simple_driver_connect(simple, hid)) + continue; + if (hidinput_simple_driver_push(simple, matched)) + continue; + hidinput_simple_driver_setup_usage(hid); + ret = 0; + printk(KERN_INFO"The simple HID driver \'%s\' attach on \'%s\'.\n", simple->name, hid->name); + } + up(&matched_lock); + + down(&simple_lock); + list_add(&simple->node, &simple_drivers_list); + up(&simple_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(hidinput_register_simple_driver); + +void +hidinput_unregister_simple_driver(struct hidinput_simple_driver *simple) +{ + hidinput_simple_driver_clear(simple); + down(&simple_lock); + list_del(&simple->node); + up(&simple_lock); + /* to active simple device driver that it is waiting */ + hidinput_simple_driver_bind_foreach(); + printk(KERN_INFO"The simple HID driver \'%s\' unregistered.\n", simple->name); +} + +EXPORT_SYMBOL_GPL(hidinput_unregister_simple_driver); + 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); @@ -2021,13 +2187,22 @@ 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; + down(&matched_lock); + list_add(&matched->node, &matched_devices_list); + up(&matched_lock); + hidinput_simple_driver_bind(matched); + } 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); @@ -2108,6 +2283,8 @@ static int __init hid_init(void) retval = hiddev_init(); if (retval) goto hiddev_init_fail; + INIT_LIST_HEAD(&matched_devices_list); + INIT_LIST_HEAD(&simple_drivers_list); retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; @@ -2122,7 +2299,15 @@ hiddev_init_fail: static void __exit hid_exit(void) { + struct list_head *node, *tmp; + struct matched_device *matched; + usb_deregister(&hid_driver); + list_for_each_safe(node, tmp, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + list_del(&matched->node); + kfree(matched); + } hiddev_exit(); } diff -Naurp input.orig/hid.h input/hid.h --- input.orig/hid.h 2006-07-16 03:00:43.000000000 +0800 +++ input/hid.h 2006-07-21 13:46:03.000000000 +0800 @@ -6,6 +6,7 @@ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2006 Liyu To support simple HID device. */ /* @@ -382,6 +383,8 @@ struct hid_input { struct input_dev *input; }; +struct hidinput_simple_driver; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -445,6 +448,8 @@ struct hid_device { /* device repo int (*ff_event)(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); + struct hidinput_simple_driver *simple; + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; diff -Naurp input.orig/hid-input.c input/hid-input.c --- input.orig/hid-input.c 2006-07-16 03:00:43.000000000 +0800 +++ input/hid-input.c 2006-07-22 15:05:35.000000000 +0800 @@ -36,6 +36,7 @@ #undef DEBUG #include "hid.h" +#include "hid-simple.h" #define unk KEY_UNKNOWN @@ -849,6 +850,153 @@ int hidinput_connect(struct hid_device * return 0; } +int +hidinput_simple_driver_init(struct hidinput_simple_driver *drv) +{ + if (unlikely(!drv)) + return -EINVAL; + INIT_LIST_HEAD(&drv->node); + INIT_LIST_HEAD(&drv->raw_devices); + drv->flags = 0; + 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_driver_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 = NULL; + 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; +} + +int +hidinput_simple_driver_push(struct hidinput_simple_driver *simple, struct matched_device *matched) +{ + struct raw_simple_device *raw_simple; + struct hid_device *hid; + + raw_simple = kmalloc(sizeof(struct raw_simple_device), GFP_KERNEL); + if (!raw_simple) + return -ENOMEM; + raw_simple->intf = matched->intf; + hid = usb_get_intfdata(matched->intf); + hid->simple = simple; + list_add(&raw_simple->node, &simple->raw_devices); + return 0; +} + +void +hidinput_simple_driver_pop(struct hidinput_simple_driver *simple, struct matched_device *matched) +{ + struct list_head *node; + struct raw_simple_device *raw_simple=NULL; + struct hid_device *hid; + + list_for_each (node, &simple->raw_devices) { + printk(KERN_DEBUG"list_entry\n"); + raw_simple = list_entry(node, struct raw_simple_device, node); + if (raw_simple && raw_simple->intf == matched->intf) { + printk(KERN_DEBUG"usb_get_intfdata\n"); + hid = usb_get_intfdata(matched->intf); + hid->simple = NULL; + printk(KERN_DEBUG"list_del\n"); + list_del(&raw_simple->node); + kfree(raw_simple); + return; + } + } +} + +void +hidinput_simple_driver_clear(struct hidinput_simple_driver *simple) +{ + struct raw_simple_device *raw_simple; + struct hid_device *hid; + + while (!list_empty_careful(&simple->raw_devices)) { + raw_simple = list_entry(simple->raw_devices.next, struct raw_simple_device, node); + hid = usb_get_intfdata (raw_simple->intf); + + if (hid->simple) { + BUG_ON(hid->simple != simple); + hid->simple = NULL; + } + hidinput_simple_driver_clear_usage(hid); + printk("device '%s' disconnect from one simple driver.\n", hid->name); + hidinput_simple_driver_disconnect(hid); + list_del_init(simple->raw_devices.next); + } +} + +/* modify from hidinput_disconnect() */ +int hidinput_simple_driver_connect(struct hidinput_simple_driver *simple, struct hid_device *hid) +{ + struct hid_input *hidinput, *next; + int ret = -ENODEV; + + if (!simple) + return ret; + if (!simple->connect) + return 0; + + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + if (!simple->connect(hid, hidinput)) { + hid->simple = simple; + ret = 0; + } + } + return ret; +} + + +/* modify from hidinput_disconnect() */ +void hidinput_simple_driver_disconnect(struct hid_device *hid) +{ + struct hid_input *hidinput, *next; + + if (!hid->simple || !hid->simple->disconnect) + return; + + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + hid->simple->disconnect(hid, hidinput); + } +} + void hidinput_disconnect(struct hid_device *hid) { struct hid_input *hidinput, *next; diff -Naurp input.orig/hid-simple.h input/hid-simple.h --- input.orig/hid-simple.h 1970-01-01 08:00:00.000000000 +0800 +++ input/hid-simple.h 2006-07-24 11:25:27.000000000 +0800 @@ -0,0 +1,101 @@ +/* + * NOTE: + * For use me , you must include hid.h in your source first. + */ +#ifndef __HID_SIMPLE_H +#define __HID_SIMPLE_H + +/* list_*_entry like interface */ +/* FIXME: Would you like move them to include/linux/list.h ? */ +#define list_for_each_continue(pos, head) \ + for ((pos) = (pos)->next; \ + prefetch((pos)->next), (pos) != (head); \ + (pos) = (pos)->next) +#define list_prepare(pos, head) ((pos) ? : head) + +#include +/******** The private section for simple device implement only ***********/ + +/* + * matched_device record one device which hid-subsystem handle, it may + * be one simple device can not handle. + * + * 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; + /* this need not atomic_t. beacause it only change while simple driver module insert or delete. thus, one module can not insert and delete at same time, even under SMP */ + int refcnt; +}; + +/* + * raw_simple_driver record one device which hid simple device handle. + * It used as one member of hid_simple_driver. + */ + +struct raw_simple_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_driver_setup_usage(hid) \ +do {\ + if (hid->simple) {\ + hid->simple->flags |= HIDINPUT_SIMPLE_SETUP_USAGE; \ + hidinput_simple_driver_configure_usage(hid); \ + }\ +} while (0) + +#define hidinput_simple_driver_clear_usage(hid) \ +do {\ + if (hid->simple) {\ + hid->simple->flags &= (~HIDINPUT_SIMPLE_SETUP_USAGE); \ + hidinput_simple_driver_configure_usage(hid); \ + }\ +} while (0) + +/* Note again here, you must include hid.h in your source first. */ + +struct hidinput_simple_driver; +void hidinput_simple_driver_configure_usage(struct hid_device *hid); +int hidinput_simple_driver_init(struct hidinput_simple_driver *simple); +int hidinput_simple_driver_push(struct hidinput_simple_driver *simple, struct matched_device *dev); +void hidinput_simple_driver_pop(struct hidinput_simple_driver *simple, struct matched_device *dev); +void hidinput_simple_driver_clear(struct hidinput_simple_driver *simple); +int hidinput_simple_driver_connect(struct hidinput_simple_driver *simple, struct hid_device *hid); +void hidinput_simple_driver_disconnect(struct hid_device *hid); + + +/******************** The private section end. *****************************/ + + +/********************* The public interface for simple device driver ***********/ +struct hidinput_simple_driver { +/* private */ + struct list_head node; /* link with simple_drivers_list */ + struct list_head raw_devices; + int flags; +/* public */ + char *name; + int (*connect)(struct hid_device *, struct hid_input *); + void (*setup_usage)(struct hid_field *, struct hid_usage *); + int (*pre_event)(const struct hid_device *, const struct hid_field *, const struct hid_usage *, const __s32, const struct pt_regs *regs); + int (*post_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 hid_input *); + void *private; + struct usb_device_id *id_table; +}; + + +int hidinput_register_simple_driver(struct hidinput_simple_driver *device); +void hidinput_unregister_simple_driver(struct hidinput_simple_driver *device); + +/********************* The public section end ***********/ +#endif /* __HID_SIMPLE_H */