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

Reply via email to