# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.525 -> 1.526 # drivers/usb/hid.h 1.9 -> 1.10 # include/linux/hiddev.h 1.1 -> 1.2 # drivers/usb/hiddev.c 1.5 -> 1.6 # drivers/usb/hid-core.c 1.16 -> 1.17 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/03/17 stewart@inverse.wetlogic.net 1.526 # USB Urefs for hid-core/hiddev # # I've written a patch Vojtech and I discussed for enhancing the # hiddev code to optionally provide more detailed output on read(). # The old functionality is still supported by default, and in # situations where HID usage codes are unique across reports, the # old method is still preferable due to its terseness. # # The new method provides the ability to determine exactly which # value has changed, in cases where the HID usage codes are not # unique. It also provides a means to optionally receive notification # when input reports are received from the device, whether or not # any of the values in the report have changed. # # The details of the changes are as follows: # # - All current code behaves identically # # - A new ioctl pair HIDIOCGFLAG/HIDIOCSFLAG gets and clears # flags on the hiddev device. # # - If you set the flag HIDDEV_FLAG_UREF, the read() call switches # from reading hiddev_event structures to hiddev_usage_ref # structures. The change takes effect immediately, even to # already queued events that haven't been read() yet. Here's # an example of enabling FLAG_UREF: # # { # int flag = HIDDEV_FLAG_UREF; # if (ioctl(fd, HIDIOCSFLAG, &flag) != 0) { # perror("ioctl"); # exit(1); # } # } # # - With the HIDDEV_FLAG_REPORT set (which is only allowed if # HIDDEV_FLAG_UREF is also set), there is a special uref that # will be read() in addition to the ones corresponding to # changes in the device state: when uref.field_index is set to # HID_FIELD_INDEX_NONE, this uref is a notification that the # report referred to by report_type and report_id has been # received from the device. This can be useful in situations # when the notification of the arrival of a report is useful # even if there is no change in state. # -------------------------------------------- # diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c --- a/drivers/usb/hid-core.c Sun Mar 17 11:01:42 2002 +++ b/drivers/usb/hid-core.c Sun Mar 17 11:01:42 2002 @@ -110,10 +110,11 @@ memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + values * sizeof(unsigned)); - report->field[report->maxfield++] = field; + report->field[report->maxfield] = field; field->usage = (struct hid_usage *)(field + 1); field->value = (unsigned *)(field->usage + usages); field->report = report; + field->index = report->maxfield++; return field; } @@ -741,8 +742,20 @@ if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value); #ifdef CONFIG_USB_HIDDEV - if (hid->claimed & HID_CLAIMED_HIDDEV) - hiddev_hid_event(hid, usage->hid, value); + if (hid->claimed & HID_CLAIMED_HIDDEV) { + struct hiddev_usage_ref uref; + unsigned type = field->report_type; + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + uref.report_id = field->report->id; + uref.field_index = field->index; + uref.usage_index = (usage - field->usage); + uref.usage_code = usage->hid; + uref.value = value; + hiddev_hid_event(hid, &uref); + } #endif } @@ -838,6 +851,21 @@ dbg("undefined report_id %d received", n); return -1; } + +#ifdef CONFIG_USB_HIDDEV + /* Notify listeners that a report has been received */ + if (hid->claimed & HID_CLAIMED_HIDDEV) { + struct hiddev_usage_ref uref; + memset(&uref, 0, sizeof(uref)); + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + uref.report_id = report->id; + uref.field_index = HID_FIELD_INDEX_NONE; + hiddev_hid_event(hid, &uref); + } +#endif size = ((report->size - 1) >> 3) + 1; diff -Nru a/drivers/usb/hid.h b/drivers/usb/hid.h --- a/drivers/usb/hid.h Sun Mar 17 11:01:42 2002 +++ b/drivers/usb/hid.h Sun Mar 17 11:01:42 2002 @@ -276,6 +276,7 @@ __s32 unit_exponent; unsigned unit; struct hid_report *report; /* associated report */ + unsigned index; /* index into report->field[] */ }; #define HID_MAX_FIELDS 64 diff -Nru a/drivers/usb/hiddev.c b/drivers/usb/hiddev.c --- a/drivers/usb/hiddev.c Sun Mar 17 11:01:42 2002 +++ b/drivers/usb/hiddev.c Sun Mar 17 11:01:42 2002 @@ -50,9 +50,10 @@ }; struct hiddev_list { - struct hiddev_event buffer[HIDDEV_BUFFER_SIZE]; + struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; int head; int tail; + unsigned flags; struct fasync_struct *fasync; struct hiddev *hiddev; struct hiddev_list *next; @@ -146,17 +147,19 @@ * This is where hid.c calls into hiddev to pass an event that occurred over * the interrupt pipe */ -void hiddev_hid_event(struct hid_device *hid, unsigned int usage, int value) +void hiddev_hid_event(struct hid_device *hid, struct hiddev_usage_ref *uref) { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list = hiddev->list; while (list) { - list->buffer[list->head].hid = usage; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1); - - kill_fasync(&list->fasync, SIGIO, POLL_IN); + if (uref->field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + list->buffer[list->head] = *uref; + list->head = (list->head + 1) & + (HIDDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } list = list->next; } @@ -257,43 +260,67 @@ { DECLARE_WAITQUEUE(wait, current); struct hiddev_list *list = file->private_data; + int event_size; int retval = 0; - if (list->head == list->tail) { - - add_wait_queue(&list->hiddev->wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); + event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? + sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); - while (list->head == list->tail) { + if (count < event_size) return 0; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - if (!list->hiddev->exist) { - retval = -EIO; - break; + while (retval == 0) { + if (list->head == list->tail) { + add_wait_queue(&list->hiddev->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (!list->hiddev->exist) { + retval = -EIO; + break; + } + + schedule(); } - schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->hiddev->wait, &wait); } - set_current_state(TASK_RUNNING); - remove_wait_queue(&list->hiddev->wait, &wait); - } + if (retval) + return retval; - if (retval) - return retval; - while (list->head != list->tail && retval + sizeof(struct hiddev_event) <= count) { - if (copy_to_user(buffer + retval, list->buffer + list->tail, - sizeof(struct hiddev_event))) return -EFAULT; - list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); - retval += sizeof(struct hiddev_event); + while (list->head != list->tail && + retval + event_size <= count) { + if ((list->flags & HIDDEV_FLAG_UREF) == 0) { + if (list->buffer[list->tail].field_index != + HID_FIELD_INDEX_NONE) { + struct hiddev_event event; + event.hid = list->buffer[list->tail].usage_code; + event.value = list->buffer[list->tail].value; + if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) + return -EFAULT; + retval += sizeof(struct hiddev_event); + } + } else { + if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) + return -EFAULT; + retval += sizeof(struct hiddev_usage_ref); + } + } + list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); + } + } return retval; @@ -357,6 +384,25 @@ dinfo.num_applications = hid->maxapplication; return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); } + + case HIDIOCGFLAG: + return put_user(list->flags, (int *) arg); + + case HIDIOCSFLAG: + { + int newflags; + if (get_user(newflags, (int *) arg)) + return -EFAULT; + + if ((newflags & ~HIDDEV_FLAGS) != 0 || + ((newflags & HIDDEV_FLAG_REPORT) != 0 && + (newflags & HIDDEV_FLAG_UREF) == 0)) + return -EINVAL; + + list->flags = newflags; + + return 0; + } case HIDIOCGSTRING: { diff -Nru a/include/linux/hiddev.h b/include/linux/hiddev.h --- a/include/linux/hiddev.h Sun Mar 17 11:01:42 2002 +++ b/include/linux/hiddev.h Sun Mar 17 11:01:42 2002 @@ -119,6 +119,7 @@ __s32 value; }; +#define HID_FIELD_INDEX_NONE 0xffffffff /* * Protocol version. @@ -143,6 +144,15 @@ #define HIDIOCGUSAGE _IOWR('H', 0x0B, struct hiddev_usage_ref) #define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref) #define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) +#define HIDIOCGFLAG _IOR('H', 0x0E, int) +#define HIDIOCSFLAG _IOW('H', 0x0F, int) + +/* + * Flags to be used in HIDIOCSFLAG + */ +#define HIDDEV_FLAG_UREF 0x1 +#define HIDDEV_FLAG_REPORT 0x2 +#define HIDDEV_FLAGS 0x3 /* To traverse the input report descriptor info for a HID device, perform the * following: @@ -179,7 +189,7 @@ #ifdef CONFIG_USB_HIDDEV int hiddev_connect(struct hid_device *); void hiddev_disconnect(struct hid_device *); -void hiddev_hid_event(struct hid_device *, unsigned int usage, int value); +void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref); int __init hiddev_init(void); void __exit hiddev_exit(void); #else