diff options
Diffstat (limited to 'drivers/usb/hid.c')
-rw-r--r-- | drivers/usb/hid.c | 158 |
1 files changed, 122 insertions, 36 deletions
diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c index bdd540ac2..ec1a4639d 100644 --- a/drivers/usb/hid.c +++ b/drivers/usb/hid.c @@ -2,7 +2,7 @@ * hid.c Version 0.8 * * Copyright (c) 1999 Andreas Gal - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 2000 Vojtech Pavlik * * USB HID support for the Linux input drivers * @@ -38,10 +38,10 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/smp_lock.h> -#include <linux/config.h> #include <linux/spinlock.h> #undef DEBUG +#undef DEBUG_DATA #include "usb.h" #include "hid.h" @@ -710,7 +710,7 @@ static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) { report += (offset >> 5) << 2; offset &= 31; - *(__u64*)report &= cpu_to_le64(~((1ULL << n) - 1) << offset); + *(__u64*)report &= cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)); *(__u64*)report |= cpu_to_le64((__u64)value << offset); } @@ -763,6 +763,12 @@ static void hid_configure_usage(struct hid_device *device, struct hid_field *fie } break; + case HID_UP_LED: + + usage->code = (usage->hid - 1) & 0xf; + usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; + break; + default: if (field->flags & HID_MAIN_ITEM_RELATIVE) { @@ -905,7 +911,7 @@ static void hid_irq(struct urb *urb) return; } -#ifdef DEBUG +#ifdef DEBUG_DATA printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered) = ", len, report_enum->numbered ? "" : "un"); for (n = 0; n < len; n++) printk(" %02x", data[n]); @@ -953,10 +959,10 @@ static void hid_irq(struct urb *urb) static void hid_read_report(struct hid_device *hid, struct hid_report *report) { #if 0 - int rlen = ((report->size - 1) >> 3) + 1 + report_enum->numbered; + int rlen = ((report->size - 1) >> 3) + 1 + hid->report_enum[HID_INPUT_REPORT].numbered; char rdata[rlen]; struct urb urb; - int read; + int read, j; memset(&urb, 0, sizeof(struct urb)); memset(rdata, 0, rlen); @@ -974,7 +980,7 @@ static void hid_read_report(struct hid_device *hid, struct hid_report *report) for (j = 0; j < rlen; j++) printk(" %02x", rdata[j]); printk("\n"); #endif - continue; + return; } hid_irq(&urb); @@ -982,33 +988,6 @@ static void hid_read_report(struct hid_device *hid, struct hid_report *report) } /* - * Configure the input layer interface - * Read all reports and initalize the absoulte field values. - */ - -static void hid_init_input(struct hid_device *hid) -{ - struct hid_report_enum *report_enum = hid->report_enum + HID_INPUT_REPORT; - struct list_head *list; - int i, j; - - list = report_enum->report_list.next; - - while (list != &report_enum->report_list) { - - struct hid_report *report = (struct hid_report *) list; - - list = list->next; - - for (i = 0; i < report->maxfield; i++) - for (j = 0; j < report->field[i]->maxusage; j++) - hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); - - hid_read_report(hid, report); - } -} - -/* * Output the field into the report. */ @@ -1035,9 +1014,11 @@ void hid_output_report(struct hid_report *report, __u8 *data) { unsigned n; +#if 0 /* skip the ID if we have a single report */ if (report->device->report_enum[report->type].numbered) *data++ = report->id; +#endif for (n = 0; n < report->maxfield; n++) hid_output_field(report->field[n], data); @@ -1052,6 +1033,8 @@ void hid_output_report(struct hid_report *report, __u8 *data) int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { unsigned size = field->report_size; + + hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { dbg("offset exceeds report_count"); @@ -1072,10 +1055,104 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) return 0; } +static int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field) +{ + struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT; + struct list_head *list = report_enum->report_list.next; + int i, j; + + while (list != &report_enum->report_list) { + struct hid_report *report = (struct hid_report *) list; + list = list->next; + for (i = 0; i < report->maxfield; i++) { + *field = report->field[i]; + for (j = 0; j < (*field)->maxusage; j++) + if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) + return j; + } + } + return -1; +} + +static void hid_ctrl(struct urb *urb) +{ + if (urb->status) + warn("ctrl urb status %d received", urb->status); +} + +static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = dev->private; + struct hid_field *field = NULL; + int offset; + + if ((offset = hid_find_field(hid, type, code, &field)) == -1) { + warn("event field not found"); + return -1; + } + + hid_set_field(field, offset, value); + + if (hid->urbout.status == -EINPROGRESS) { + warn("had to kill output urb"); + usb_unlink_urb(&hid->urbout); + } + + hid_output_report(field->report, hid->bufout); + + hid->dr.value = 0x200 | field->report->id; + hid->dr.length = ((field->report->size - 1) >> 3) + 1; + hid->urbout.transfer_buffer_length = hid->dr.length; + + if (usb_submit_urb(&hid->urbout)) { + err("usb_submit_urb(out) failed"); + return -1; + } + + return 0; +} + +/* + * Configure the input layer interface + * Read all reports and initalize the absoulte field values. + */ + +static void hid_init_input(struct hid_device *hid) +{ + struct hid_report_enum *report_enum; + struct list_head *list; + int i, j, k; + + hid->input.private = hid; + hid->input.event = hid_event; + + 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) { + + struct hid_report *report = (struct hid_report *) list; + + list = list->next; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); + + if (k == HID_INPUT_REPORT) { + usb_set_idle(hid->dev, 0, report->id); + hid_read_report(hid, report); + } + } + } +} + static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) { struct usb_interface_descriptor *interface = &dev->actconfig->interface[ifnum].altsetting[0]; - struct usb_hid_descriptor *hdesc; + struct hid_descriptor *hdesc; struct hid_device *hid; unsigned rsize = 0; int n; @@ -1106,7 +1183,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) return NULL; } -#ifdef DEBUG +#ifdef DEBUG_DATA printk(KERN_DEBUG __FILE__ ": report (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) printk(" %02x", (unsigned) rdesc[n]); @@ -1149,6 +1226,15 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) return NULL; } + hid->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + hid->dr.request = USB_REQ_SET_REPORT; + hid->dr.value = 0x200; + hid->dr.index = interface->bInterfaceNumber; + hid->dr.length = 1; + + FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), + (void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid); + hid->version = hdesc->bcdHID; hid->country = hdesc->bCountryCode; hid->dev = dev; |