On Sun, Mar 17, 2002 at 10:57:40AM -0800, Greg KH wrote:
> [EMAIL PROTECTED], 2002-03-17 10:14:24-08:00, [EMAIL PROTECTED]
> 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.
>
> drivers/usb/hid-core.c | 34 +++++++++++++-
> drivers/usb/hid.h | 1
> drivers/usb/hiddev.c | 114 ++++++++++++++++++++++++++++++++++---------------
> include/linux/hiddev.h | 12 ++++-
> 4 files changed, 123 insertions(+), 38 deletions(-)
# 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 [EMAIL PROTECTED] 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
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel