ChangeSet 1.1317, 2003/05/29 22:36:35-07:00, bcollins@debian.org [PATCH] USB Multi-input quirk drivers/usb/input/hid-core.c | 8 -- drivers/usb/input/hid-input.c | 139 +++++++++++++++++++++++++++++++++--------- drivers/usb/input/hid-lgff.c | 11 +-- drivers/usb/input/hid-tmff.c | 11 +-- drivers/usb/input/hid.h | 10 ++- drivers/usb/input/pid.c | 13 ++- 6 files changed, 141 insertions(+), 51 deletions(-) diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c --- a/drivers/usb/input/hid-core.c Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/hid-core.c Fri May 30 11:35:12 2003 @@ -1387,9 +1387,9 @@ { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_HIDDEV }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_HIDDEV }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, @@ -1628,8 +1628,6 @@ hid_init_reports(hid); hid_dump_device(hid); - - hid_ff_init(hid); if (!hidinput_connect(hid)) hid->claimed |= HID_CLAIMED_INPUT; diff -Nru a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c --- a/drivers/usb/input/hid-input.c Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/hid-input.c Fri May 30 11:35:12 2003 @@ -60,9 +60,36 @@ __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; -static void hidinput_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage) +static struct input_dev *find_input(struct hid_device *hid, struct hid_field *field) { - struct input_dev *input = &device->input; + struct list_head *lh; + struct hid_input *hidinput; + + list_for_each (lh, &hid->inputs) { + int i; + + hidinput = list_entry(lh, struct hid_input, list); + + for (i = 0; i < hidinput->maxfield; i++) + if (hidinput->fields[i] == field) + return &hidinput->input; + } + + /* Assume we only have one input and use it */ + if (!list_empty(&hid->inputs)) { + hidinput = list_entry(hid->inputs.next, struct hid_input, list); + return &hidinput->input; + } + + /* This is really a bug */ + return NULL; +} + +static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage) +{ + struct input_dev *input = &hidinput->input; + struct hid_device *device = hidinput->input.private; int max; int is_abs = 0; unsigned long *bit; @@ -388,9 +415,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs) { - struct input_dev *input = &hid->input; + struct input_dev *input = find_input(hid, field); int *quirks = &hid->quirks; + if (!input) + return; + input_regs(input, regs); if (usage->hat_min != usage->hat_max) { @@ -443,7 +473,13 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { - input_sync(&hid->input); + struct list_head *lh; + struct hid_input *hidinput; + + list_for_each (lh, &hid->inputs) { + hidinput = list_entry(lh, struct hid_input, list); + input_sync(&hidinput->input); + } } static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) @@ -490,7 +526,10 @@ struct hid_report_enum *report_enum; struct hid_report *report; struct list_head *list; - int i, j, k; + struct hid_input *hidinput = NULL; + int i, j; + + INIT_LIST_HEAD(&hid->inputs); for (i = 0; i < hid->maxcollection; i++) if (hid->collection[i].type == HID_COLLECTION_APPLICATION && @@ -500,37 +539,79 @@ if (i == hid->maxcollection) return -1; - hid->input.private = hid; - hid->input.event = hidinput_input_event; - hid->input.open = hidinput_open; - hid->input.close = hidinput_close; - - hid->input.name = hid->name; - hid->input.phys = hid->phys; - hid->input.uniq = hid->uniq; - hid->input.id.bustype = BUS_USB; - hid->input.id.vendor = dev->descriptor.idVendor; - hid->input.id.product = dev->descriptor.idProduct; - hid->input.id.version = dev->descriptor.bcdDevice; - - for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { - report_enum = hid->report_enum + k; - list = report_enum->report_list.next; - while (list != &report_enum->report_list) { - report = (struct hid_report *) list; - for (i = 0; i < report->maxfield; i++) - for (j = 0; j < report->field[i]->maxusage; j++) - hidinput_configure_usage(hid, report->field[i], report->field[i]->usage + j); - list = list->next; + report_enum = hid->report_enum + HID_INPUT_REPORT; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + + if (!report->maxfield) + continue; + + if (!hidinput) { + hidinput = kmalloc(sizeof(*hidinput), GFP_KERNEL); + if (!hidinput) { + err("Out of memory during hid input probe"); + return -1; + } + memset(hidinput, 0, sizeof(*hidinput)); + + list_add_tail(&hidinput->list, &hid->inputs); + + hidinput->input.private = hid; + hidinput->input.event = hidinput_input_event; + hidinput->input.open = hidinput_open; + hidinput->input.close = hidinput_close; + + hidinput->input.name = hid->name; + hidinput->input.phys = hid->phys; + hidinput->input.uniq = hid->uniq; + hidinput->input.id.bustype = BUS_USB; + hidinput->input.id.vendor = dev->descriptor.idVendor; + hidinput->input.id.product = dev->descriptor.idProduct; + hidinput->input.id.version = dev->descriptor.bcdDevice; } + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hidinput_configure_usage(hidinput, report->field[i], + report->field[i]->usage + j); + + if (hid->quirks & HID_QUIRK_MULTI_INPUT) { + /* This will leave hidinput NULL, so that it + * allocates another one if we have more inputs on + * the same interface. Some devices (e.g. Happ's + * UGCI) cram a lot of unrelated inputs into the + * same interface. */ + hidinput->fields = report->field; + hidinput->maxfield = report->maxfield; + + input_register_device(&hidinput->input); + hidinput = NULL; + } + + list = list->next; } - input_register_device(&hid->input); + /* This only gets called when we are a single-input (most of the + * time). IOW, not a HID_QUIRK_MULTI_INPUT. The hid_ff_init() is + * only useful in this case, and not for multi-input quirks. */ + if (hidinput) { + hid_ff_init(hid); + input_register_device(&hidinput->input); + } return 0; } void hidinput_disconnect(struct hid_device *hid) { - input_unregister_device(&hid->input); + struct list_head *lh, *next; + struct hid_input *hidinput; + + list_for_each_safe (lh, next, &hid->inputs) { + hidinput = list_entry(lh, struct hid_input, list); + input_unregister_device(&hidinput->input); + list_del(&hidinput->list); + kfree(hidinput); + } } diff -Nru a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c --- a/drivers/usb/input/hid-lgff.c Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/hid-lgff.c Fri May 30 11:35:12 2003 @@ -254,6 +254,7 @@ signed short* ff; u16 idVendor = hid->dev->descriptor.idVendor; u16 idProduct = hid->dev->descriptor.idProduct; + struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) dev++; @@ -261,15 +262,15 @@ ff = dev->ff; while (*ff >= 0) { - set_bit(*ff, hid->input.ffbit); + set_bit(*ff, hidinput->input.ffbit); ++ff; } - hid->input.upload_effect = hid_lgff_upload_effect; - hid->input.flush = hid_lgff_flush; + hidinput->input.upload_effect = hid_lgff_upload_effect; + hidinput->input.flush = hid_lgff_flush; - set_bit(EV_FF, hid->input.evbit); - hid->input.ff_effects_max = LGFF_EFFECTS; + set_bit(EV_FF, hidinput->input.evbit); + hidinput->input.ff_effects_max = LGFF_EFFECTS; } static void hid_lgff_exit(struct hid_device* hid) diff -Nru a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c --- a/drivers/usb/input/hid-tmff.c Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/hid-tmff.c Fri May 30 11:35:12 2003 @@ -110,6 +110,7 @@ { struct tmff_device *private; struct list_head *pos; + struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL); if (!private) @@ -154,7 +155,7 @@ private->report = report; private->rumble = field; - set_bit(FF_RUMBLE, hid->input.ffbit); + set_bit(FF_RUMBLE, hidinput->input.ffbit); break; default: @@ -163,11 +164,11 @@ } /* Fallthrough to here only when a valid usage is found */ - hid->input.upload_effect = hid_tmff_upload_effect; - hid->input.flush = hid_tmff_flush; + hidinput->input.upload_effect = hid_tmff_upload_effect; + hidinput->input.flush = hid_tmff_flush; - set_bit(EV_FF, hid->input.evbit); - hid->input.ff_effects_max = TMFF_EFFECTS; + set_bit(EV_FF, hidinput->input.evbit); + hidinput->input.ff_effects_max = TMFF_EFFECTS; } } diff -Nru a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h --- a/drivers/usb/input/hid.h Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/hid.h Fri May 30 11:35:12 2003 @@ -207,6 +207,7 @@ #define HID_QUIRK_NOGET 0x08 #define HID_QUIRK_HIDDEV 0x10 #define HID_QUIRK_BADPAD 0x20 +#define HID_QUIRK_MULTI_INPUT 0x40 /* * This is the global environment of the parser. This information is @@ -321,6 +322,13 @@ #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 +struct hid_input { + struct list_head list; + struct hid_field **fields; + int maxfield; + struct input_dev input; +}; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -360,7 +368,7 @@ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ - struct input_dev input; /* The input structure */ + struct list_head inputs; /* The list of inputs */ void *hiddev; /* The hiddev structure */ int minor; /* Hiddev minor number */ diff -Nru a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c --- a/drivers/usb/input/pid.c Fri May 30 11:35:12 2003 +++ b/drivers/usb/input/pid.c Fri May 30 11:35:12 2003 @@ -269,7 +269,8 @@ int hid_pid_init(struct hid_device *hid) { struct hid_ff_pid *private; - + struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); + private = hid->ff_private = kmalloc(sizeof(struct hid_ff_pid), GFP_KERNEL); if (!private) return -1; @@ -289,11 +290,11 @@ } usb_fill_control_urb(private->urbffout, hid->dev,0,(void *) &private->ffcr,private->ctrl_buffer,8,hid_pid_ctrl_out,hid); - hid->input.upload_effect = hid_pid_upload_effect; - hid->input.flush = hid_pid_flush; - hid->input.ff_effects_max = 8; // A random default - set_bit(EV_FF, hid->input.evbit); - set_bit(EV_FF_STATUS, hid->input.evbit); + hidinput->input.upload_effect = hid_pid_upload_effect; + hidinput->input.flush = hid_pid_flush; + hidinput->input.ff_effects_max = 8; // A random default + set_bit(EV_FF, hidinput->input.evbit); + set_bit(EV_FF_STATUS, hidinput->input.evbit); spin_lock_init(&private->lock);