The following patch from Paul Stewart (<[EMAIL PROTECTED]>) add two ioctl
flags
for managing objects tree of complex HID devices.
Those two ioctl are:
- HIDIOCGCOLLECTIONINDEX
- HIDIOCGCOLLECTIONINFO

This is needed to avoid problems with redondant HID objects (eg multiple
outlets for an UPS).

Please merge.

Arnaud

=======================================================================================

diff -ur linux-2.4.20-pre10-orig/drivers/usb/hid-core.c
linux-2.4.20-pre10/drivers/usb/hid-core.c
--- linux-2.4.20-pre10-orig/drivers/usb/hid-core.c      Tue Oct 15 08:01:27 2002
+++ linux-2.4.20-pre10/drivers/usb/hid-core.c      Wed Oct 16 11:00:34 2002
@@ -108,10 +108,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;
 }
@@ -127,18 +128,42 @@

     usage = parser->local.usage[0];

-    if (type == HID_COLLECTION_APPLICATION
-         && parser->device->maxapplication < HID_MAX_APPLICATIONS)
-              parser->device->application[parser->device->maxapplication++] =
usage;
-
     if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
          dbg("collection stack overflow");
          return -1;
     }

-    collection = parser->collection_stack + parser->collection_stack_ptr++;
+    if (parser->device->maxcollection == parser->device->collection_size) {
+         collection = kmalloc(sizeof(struct hid_collection) *
+                        parser->device->collection_size * 2,
+                        GFP_KERNEL);
+         if (collection == NULL) {
+              dbg("failed to reallocate collection array");
+              return -1;
+         }
+         memcpy(collection, parser->device->collection,
+                sizeof(struct hid_collection) *
+                parser->device->collection_size);
+         memset(collection + parser->device->collection_size, 0,
+                sizeof(struct hid_collection) *
+                parser->device->collection_size);
+         kfree(parser->device->collection);
+         parser->device->collection = collection;
+         parser->device->collection_size *= 2;
+    }
+
+    parser->collection_stack[parser->collection_stack_ptr++] =
+         parser->device->maxcollection;
+
+    collection = parser->device->collection +
+         parser->device->maxcollection++;
+
     collection->type = type;
     collection->usage = usage;
+    collection->level = parser->collection_stack_ptr - 1;
+
+    if (type == HID_COLLECTION_APPLICATION)
+         parser->device->maxapplication++;

     return 0;
 }
@@ -166,8 +191,9 @@
 {
     int n;
     for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
-         if (parser->collection_stack[n].type == type)
-              return parser->collection_stack[n].usage;
+         if (parser->device->collection[parser->collection_stack[n]].type ==
type)
+              return
parser->device->collection[parser->collection_stack[n]].usage;
+
     return 0; /* we know nothing about this usage type */
 }

@@ -181,7 +207,12 @@
          dbg("usage index exceeded");
          return -1;
     }
-    parser->local.usage[parser->local.usage_index++] = usage;
+    parser->local.usage[parser->local.usage_index] = usage;
+    parser->local.collection_index[parser->local.usage_index] =
+         parser->collection_stack_ptr ?
+         parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
+    parser->local.usage_index++;
+
     return 0;
 }

@@ -222,8 +253,11 @@
     field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
     field->application = hid_lookup_collection(parser,
HID_COLLECTION_APPLICATION);

-    for (i = 0; i < usages; i++)
+    for (i = 0; i < usages; i++) {
          field->usage[i].hid = parser->local.usage[i];
+         field->usage[i].collection_index =
+              parser->local.collection_index[i];
+    }

     field->maxusage = usages;
     field->flags = flags;
@@ -461,7 +495,7 @@

     switch (item->tag) {
          case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
-              ret = open_collection(parser, data & 3);
+              ret = open_collection(parser, data & 0xff);
               break;
          case HID_MAIN_ITEM_TAG_END_COLLECTION:
               ret = close_collection(parser);
@@ -530,6 +564,7 @@
     }

     if (device->rdesc) kfree(device->rdesc);
+    if (device->collection) kfree(device->collection);
 }

 /*
@@ -618,17 +653,30 @@
          return NULL;
     memset(device, 0, sizeof(struct hid_device));

+    if (!(device->collection = kmalloc(sizeof(struct hid_collection) *
+                           HID_DEFAULT_NUM_COLLECTIONS,
+                           GFP_KERNEL))) {
+         kfree(device);
+         return NULL;
+    }
+    memset(device->collection, 0, sizeof(struct hid_collection) *
+           HID_DEFAULT_NUM_COLLECTIONS);
+    device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
     for (i = 0; i < HID_REPORT_TYPES; i++)
          INIT_LIST_HEAD(&device->report_enum[i].report_list);

     if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
+         kfree(device->collection);
          kfree(device);
          return NULL;
     }
     memcpy(device->rdesc, start, size);
+    device->rsize = size;

     if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
          kfree(device->rdesc);
+         kfree(device->collection);
          kfree(device);
          return NULL;
     }
@@ -736,7 +784,7 @@
     if (hid->claimed & HID_CLAIMED_INPUT)
          hidinput_hid_event(hid, field, usage, value);
     if (hid->claimed & HID_CLAIMED_HIDDEV)
-         hiddev_hid_event(hid, usage->hid, value);
+         hiddev_hid_event(hid, field, usage, value);
 }


@@ -827,6 +875,9 @@
          return -1;
     }

+    if (hid->claimed & HID_CLAIMED_HIDDEV)
+         hiddev_report_event(hid, report);
+
     size = ((report->size - 1) >> 3) + 1;

     if (len < size) {
@@ -1086,6 +1137,10 @@
 #define USB_DEVICE_ID_ATEN_2PORTKVM     0x2204
 #define USB_DEVICE_ID_ATEN_4PORTKVM     0x2205

+#define USB_VENDOR_ID_MGE         0x0463
+#define USB_DEVICE_ID_MGE_UPS           0xffff
+#define USB_DEVICE_ID_MGE_UPS1          0x0001
+
 struct hid_blacklist {
     __u16 idVendor;
     __u16 idProduct;
@@ -1115,6 +1170,8 @@
     { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
     { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
     { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+    { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_HIDDEV },
+    { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_HIDDEV },
     { 0, 0 }
 };

@@ -1268,11 +1325,14 @@
          printk("hiddev%d", hid->minor);

     c = "Device";
-    for (i = 0; i < hid->maxapplication; i++)
-         if ((hid->application[i] & 0xffff) < ARRAY_SIZE(hid_types)) {
-              c = hid_types[hid->application[i] & 0xffff];
+    for (i = 0; i < hid->maxcollection; i++) {
+         if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
+            (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+            (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
+              c = hid_types[hid->collection[i].usage & 0xffff];
               break;
          }
+    }

     printk(": USB HID v%x.%02x %s [%s] on usb%d:%d.%d\n",
          hid->version >> 8, hid->version & 0xff, c, hid->name,
diff -ur linux-2.4.20-pre10-orig/drivers/usb/hid-input.c
linux-2.4.20-pre10/drivers/usb/hid-input.c
--- linux-2.4.20-pre10-orig/drivers/usb/hid-input.c     Sun Nov 11 10:09:37 2001
+++ linux-2.4.20-pre10/drivers/usb/hid-input.c     Wed Oct 16 10:24:54 2002
@@ -384,11 +384,12 @@
     struct list_head *list;
     int i, j, k;

-    for (i = 0; i < hid->maxapplication; i++)
-         if (IS_INPUT_APPLICATION(hid->application[i]))
+    for (i = 0; i < hid->maxcollection; i++)
+         if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
+             IS_INPUT_APPLICATION(hid->collection[i].usage))
               break;

-    if (i == hid->maxapplication)
+    if (i == hid->maxcollection)
          return -1;

     hid->input.private = hid;
diff -ur linux-2.4.20-pre10-orig/drivers/usb/hid.h
linux-2.4.20-pre10/drivers/usb/hid.h
--- linux-2.4.20-pre10-orig/drivers/usb/hid.h      Tue Oct 15 08:01:27 2002
+++ linux-2.4.20-pre10/drivers/usb/hid.h      Wed Oct 16 10:19:22 2002
@@ -186,6 +186,7 @@
 #define HID_QUIRK_NOTOUCH    0x02
 #define HID_QUIRK_IGNORE     0x04
 #define HID_QUIRK_NOGET      0x08
+#define HID_QUIRK_HIDDEV     0x10

 /*
  * This is the global enviroment of the parser. This information is
@@ -213,9 +214,11 @@
 #define HID_MAX_DESCRIPTOR_SIZE         4096
 #define HID_MAX_USAGES            1024
 #define HID_MAX_APPLICATIONS      16
+#define HID_DEFAULT_NUM_COLLECTIONS     16

 struct hid_local {
     unsigned usage[HID_MAX_USAGES]; /* usage array */
+    unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
     unsigned usage_index;
     unsigned usage_minimum;
     unsigned delimiter_depth;
@@ -230,10 +233,12 @@
 struct hid_collection {
     unsigned type;
     unsigned usage;
+    unsigned level;
 };

 struct hid_usage {
     unsigned  hid;           /* hid usage code */
+    unsigned  collection_index;   /* index into collection array */
     __u16     code;               /* input driver code */
     __u8      type;               /* input driver type */
     __s8   hat_min;          /* hat switch fun */
@@ -259,6 +264,7 @@
     unsigned  unit_exponent;
     unsigned  unit;
     struct hid_report *report;    /* associated report */
+    unsigned index;               /* index into report->field[] */
 };

 #define HID_MAX_FIELDS 64
@@ -297,7 +303,9 @@
 struct hid_device {                                    /* device report
descriptor */
      __u8 *rdesc;
     unsigned rsize;
-    unsigned application[HID_MAX_APPLICATIONS];             /* List of HID
applications */
+    struct hid_collection *collection;                              /* List of
HID collections */
+    unsigned collection_size;                                       /* Number
of allocated hid_collections */
+    unsigned maxcollection;                                         /* Number
of parsed collections */
     unsigned maxapplication;                      /* Number of applications */
     unsigned version;                             /* HID version */
     unsigned country;                             /* HID country */
@@ -332,7 +340,7 @@
     struct hid_global     global_stack[HID_GLOBAL_STACK_SIZE];
     unsigned              global_stack_ptr;
     struct hid_local      local;
-    struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE];
+    unsigned              collection_stack[HID_COLLECTION_STACK_SIZE];
     unsigned              collection_stack_ptr;
     struct hid_device    *device;
 };
diff -ur linux-2.4.20-pre10-orig/drivers/usb/hiddev.c
linux-2.4.20-pre10/drivers/usb/hiddev.c
--- linux-2.4.20-pre10-orig/drivers/usb/hiddev.c   Sat Oct 20 19:13:11 2001
+++ linux-2.4.20-pre10/drivers/usb/hiddev.c   Wed Oct 16 10:14:28 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;
@@ -70,7 +71,8 @@
 static struct hid_report *
 hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
 {
-    struct hid_report_enum *report_enum;
+    unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
+    struct hid_report_enum *report_enum = NULL;
     struct list_head *list;

     if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
@@ -78,27 +80,29 @@

     report_enum = hid->report_enum +
          (rinfo->report_type - HID_REPORT_TYPE_MIN);
-    if ((rinfo->report_id & ~HID_REPORT_ID_MASK) != 0) {
-         switch (rinfo->report_id & ~HID_REPORT_ID_MASK) {
-         case HID_REPORT_ID_FIRST:
-              list = report_enum->report_list.next;
-              if (list == &report_enum->report_list) return NULL;
-              rinfo->report_id = ((struct hid_report *) list)->id;
-              break;
-
-         case HID_REPORT_ID_NEXT:
-              list = (struct list_head *)
-                   report_enum->report_id_hash[rinfo->report_id &
-                                     HID_REPORT_ID_MASK];
-              if (list == NULL) return NULL;
-              list = list->next;
-              if (list == &report_enum->report_list) return NULL;
-              rinfo->report_id = ((struct hid_report *) list)->id;
-              break;
-
-         default:
-              return NULL;
-         }
+
+    switch (flags) {
+    case 0: /* Nothing to do -- report_id is already set correctly */
+         break;
+
+    case HID_REPORT_ID_FIRST:
+         list = report_enum->report_list.next;
+         if (list == &report_enum->report_list) return NULL;
+         rinfo->report_id = ((struct hid_report *) list)->id;
+         break;
+
+    case HID_REPORT_ID_NEXT:
+         list = (struct list_head *)
+              report_enum->report_id_hash[rinfo->report_id &
+                                 HID_REPORT_ID_MASK];
+         if (list == NULL) return NULL;
+         list = list->next;
+         if (list == &report_enum->report_list) return NULL;
+         rinfo->report_id = ((struct hid_report *) list)->id;
+         break;
+
+    default:
+         return NULL;
     }

     return report_enum->report_id_hash[rinfo->report_id];
@@ -142,21 +146,20 @@
     return NULL;
 }

-/*
- * 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)
+static void hiddev_send_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;
     }
@@ -165,6 +168,45 @@
 }

 /*
+ * 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, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+    unsigned type = field->report_type;
+    struct hiddev_usage_ref 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 = field->report->id;
+    uref.field_index = field->index;
+    uref.usage_index = (usage - field->usage);
+    uref.usage_code = usage->hid;
+    uref.value = value;
+
+    hiddev_send_event(hid, &uref);
+}
+
+
+void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
+{
+    unsigned type = report->type;
+    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;
+
+    hiddev_send_event(hid, &uref);
+}
+
+/*
  * fasync file op
  */
 static int hiddev_fasync(int fd, struct file *file, int on)
@@ -193,7 +235,6 @@
     struct hiddev_list *list = file->private_data;
     struct hiddev_list **listptr;

-    lock_kernel();
     listptr = &list->hiddev->list;
     hiddev_fasync(-1, file, 0);

@@ -209,7 +250,6 @@
     }

     kfree(list);
-    unlock_kernel();

     return 0;
 }
@@ -259,43 +299,66 @@
 {
     DECLARE_WAITQUEUE(wait, current);
     struct hiddev_list *list = file->private_data;
+    int event_size;
     int retval = 0;

-    if (list->head == list->tail) {
+    event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
+         sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);

-         add_wait_queue(&list->hiddev->wait, &wait);
-         set_current_state(TASK_INTERRUPTIBLE);
+    if (count < event_size) return 0;

-         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;
+    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 + 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);
+         }

-    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);
     }

     return retval;
@@ -329,10 +392,14 @@
     struct hiddev *hiddev = list->hiddev;
     struct hid_device *hid = hiddev->hid;
     struct usb_device *dev = hid->dev;
+    struct hiddev_collection_info cinfo;
     struct hiddev_report_info rinfo;
+    struct hiddev_field_info finfo;
     struct hiddev_usage_ref uref;
+    struct hiddev_devinfo dinfo;
     struct hid_report *report;
     struct hid_field *field;
+    int i;

     if (!hiddev->exist) return -EIO;

@@ -344,11 +411,18 @@
     case HIDIOCAPPLICATION:
          if (arg < 0 || arg >= hid->maxapplication)
               return -EINVAL;
-         return hid->application[arg];
+
+         for (i = 0; i < hid->maxcollection; i++)
+              if (hid->collection[i].type ==
+                  HID_COLLECTION_APPLICATION && arg-- == 0)
+                   break;
+
+         if (i == hid->maxcollection)
+              return -EINVAL;
+
+         return hid->collection[i].usage;

     case HIDIOCGDEVINFO:
-    {
-         struct hiddev_devinfo dinfo;
          dinfo.bustype = BUS_USB;
          dinfo.busnum = dev->bus->busnum;
          dinfo.devnum = dev->devnum;
@@ -358,7 +432,25 @@
          dinfo.version = dev->descriptor.bcdDevice;
          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:
          {
@@ -387,7 +479,6 @@
          }

     case HIDIOCINITREPORT:
-
          hid_init_reports(hid);

          return 0;
@@ -432,8 +523,6 @@
          return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));

     case HIDIOCGFIELDINFO:
-    {
-         struct hiddev_field_info finfo;
          if (copy_from_user(&finfo, (void *) arg, sizeof(finfo)))
               return -EFAULT;
          rinfo.report_type = finfo.report_type;
@@ -462,7 +551,6 @@
          finfo.unit = field->unit;

          return copy_to_user((void *) arg, &finfo, sizeof(finfo));
-    }

     case HIDIOCGUCODE:
          if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
@@ -485,9 +573,15 @@
          return copy_to_user((void *) arg, &uref, sizeof(uref));

     case HIDIOCGUSAGE:
+    case HIDIOCSUSAGE:
+    case HIDIOCGCOLLECTIONINDEX:
          if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
               return -EFAULT;

+         if (cmd == HIDIOCSUSAGE &&
+             uref.report_type == HID_REPORT_TYPE_INPUT)
+              return -EINVAL;
+
          if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
               field = hiddev_lookup_usage(hid, &uref);
               if (field == NULL)
@@ -506,38 +600,32 @@
                    return -EINVAL;
          }

-         uref.value = field->value[uref.usage_index];
+         switch (cmd) {
+         case HIDIOCGUSAGE:
+              uref.value = field->value[uref.usage_index];
+              return copy_to_user((void *) arg, &uref, sizeof(uref));
+
+         case HIDIOCSUSAGE:
+              field->value[uref.usage_index] = uref.value;
+              return 0;

-         return copy_to_user((void *) arg, &uref, sizeof(uref));
+         case HIDIOCGCOLLECTIONINDEX:
+              return field->usage[uref.usage_index].collection_index;
+         }
+         break;

-    case HIDIOCSUSAGE:
-         if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+    case HIDIOCGCOLLECTIONINFO:
+         if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo)))
               return -EFAULT;

-         if (uref.report_type == HID_REPORT_TYPE_INPUT)
+         if (cinfo.index >= hid->maxcollection)
               return -EINVAL;

-         if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
-              field = hiddev_lookup_usage(hid, &uref);
-              if (field == NULL)
-                   return -EINVAL;
-         } else {
-              rinfo.report_type = uref.report_type;
-              rinfo.report_id = uref.report_id;
-              if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
-                   return -EINVAL;
-
-              if (uref.field_index >= report->maxfield)
-                   return -EINVAL;
-
-              field = report->field[uref.field_index];
-              if (uref.usage_index >= field->maxusage)
-                   return -EINVAL;
-         }
+         cinfo.type = hid->collection[cinfo.index].type;
+         cinfo.usage = hid->collection[cinfo.index].usage;
+         cinfo.level = hid->collection[cinfo.index].level;

-         field->value[uref.usage_index] = uref.value;
-
-         return 0;
+         return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));

     default:

@@ -576,12 +664,18 @@
     int minor, i;
     char devfs_name[16];

-    for (i = 0; i < hid->maxapplication; i++)
-         if (!IS_INPUT_APPLICATION(hid->application[i]))
-              break;

-    if (i == hid->maxapplication)
-         return -1;
+
+    if ((hid->quirks & HID_QUIRK_HIDDEV) == 0) {
+         for (i = 0; i < hid->maxcollection; i++)
+              if (hid->collection[i].type ==
+                  HID_COLLECTION_APPLICATION &&
+                  !IS_INPUT_APPLICATION(hid->collection[i].usage))
+                   break;
+
+         if (i == hid->maxcollection)
+              return -1;
+    }

     for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
     if (minor == HIDDEV_MINORS) {
diff -ur linux-2.4.20-pre10-orig/include/linux/hiddev.h
linux-2.4.20-pre10/include/linux/hiddev.h
--- linux-2.4.20-pre10-orig/include/linux/hiddev.h      Tue Oct 15 08:01:28 2002
+++ linux-2.4.20-pre10/include/linux/hiddev.h      Wed Oct 16 10:19:22 2002
@@ -49,6 +49,13 @@
     unsigned num_applications;
 };

+struct hiddev_collection_info {
+    unsigned index;
+    unsigned type;
+    unsigned usage;
+    unsigned level;
+};
+
 #define HID_STRING_SIZE 256
 struct hiddev_string_descriptor {
     int index;
@@ -119,12 +126,17 @@
     __s32 value;
 };

+/* FIELD_INDEX_NONE is returned in read() data from the kernel when flags
+ * is set to (HIDDEV_FLAG_UREF | HIDDEV_FLAG_REPORT) and a new report has
+ * been sent by the device
+ */
+#define HID_FIELD_INDEX_NONE 0xffffffff

 /*
  * Protocol version.
  */

-#define HID_VERSION          0x010002
+#define HID_VERSION          0x010004

 /*
  * IOCTLs (0x00 - 0x7f)
@@ -138,11 +150,22 @@
 #define HIDIOCGNAME(len)     _IOC(_IOC_READ, 'H', 0x06, len)
 #define HIDIOCGREPORT        _IOW('H', 0x07, struct hiddev_report_info)
 #define HIDIOCSREPORT        _IOW('H', 0x08, struct hiddev_report_info)
-#define HIDIOCGREPORTINFO       _IOWR('H', 0x09, struct hiddev_report_info)
-#define HIDIOCGFIELDINFO        _IOWR('H', 0x0A, struct hiddev_field_info)
-#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 HIDIOCGREPORTINFO    _IOWR('H', 0x09, struct hiddev_report_info)
+#define HIDIOCGFIELDINFO     _IOWR('H', 0x0A, struct hiddev_field_info)
+#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)
+#define HIDIOCGCOLLECTIONINDEX    _IOW('H', 0x10, struct hiddev_usage_ref)
+#define HIDIOCGCOLLECTIONINFO     _IOWR('H', 0x11, struct
hiddev_collection_info)
+
+/*
+ * 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,13 +202,20 @@
 #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 *hid, struct hid_field *field,
+                      struct hid_usage *usage, __s32 value);
+void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
 int __init hiddev_init(void);
 void __exit hiddev_exit(void);
 #else
 static inline int hiddev_connect(struct hid_device *hid) { return -1; }
 static inline void hiddev_disconnect(struct hid_device *hid) { }
-static inline void hiddev_hid_event(struct hid_device *hid, unsigned int usage,
 int value) { }
+static inline void void hiddev_hid_event(struct hid_device *hid,
+                         struct hid_field *field,
+                         struct hid_usage *usage,
+                         __s32 value) { }
+static inline void hiddev_report_event(struct hid_device *hid,
+                          struct hid_report *report) { }
 static inline int hiddev_init(void) { return 0; }
 static inline void hiddev_exit(void) { }
 #endif




-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to