]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Updates for hiddev by Paul Stewart
authorVojtech Pavlik <vojtech@suse.cz>
Mon, 15 Jul 2002 10:18:42 +0000 (03:18 -0700)
committerVojtech Pavlik <vojtech@suse.cz>
Mon, 15 Jul 2002 10:18:42 +0000 (03:18 -0700)
I've merged a patch Paul Stewart sent me some time ago, which should make life
easier for the guys writing UPS daemons.

Documentation/usb/hiddev.txt
drivers/usb/input/hid-core.c
drivers/usb/input/hid-debug.h
drivers/usb/input/hid-input.c
drivers/usb/input/hid.h
drivers/usb/input/hiddev.c
include/linux/hiddev.h

index 31250f38b45f9f878064f63df19fae88cb9f7400..470840b5be5f8b5240c74131c9a52fef3cb7fb28 100644 (file)
@@ -18,7 +18,7 @@ normalised event interface - see Documentation/input/input.txt
 The data flow for a HID event produced by a device is something like
 the following :
 
- usb.c ---> hid-core.c  ----> input.c ----> [keyboard/mouse/joystick/event]
+ usb.c ---> hid-core.c  ----> hid-input.c ----> [keyboard/mouse/joystick/event]
                          |
                          |
                           --> hiddev.c ----> POWER / MONITOR CONTROL 
@@ -106,6 +106,15 @@ returns -1. You can find out beforehand how many application
 collections the device has from the num_applications field from the
 hiddev_devinfo structure. 
 
+HIDIOCGCOLLECTIONINFO - struct hiddev_collection_info (read/write)
+This returns a superset of the information above, providing not only
+application collections, but all the collections the device has.  It
+also returns the level the collection lives in the hierarchy.
+The user passes in a hiddev_collection_info struct with the index 
+field set to the index that should be returned.  The ioctl fills in 
+the other fields.  If the index is larger than the last collection 
+index, the ioctl returns -1 and sets errno to -EINVAL.
+
 HIDIOCGDEVINFO - struct hiddev_devinfo (read)
 Gets a hiddev_devinfo structure which describes the device.
 
@@ -172,6 +181,10 @@ Sets the value of a usage in an output report.  The user fills in
 the hiddev_usage_ref structure as above, but additionally fills in
 the value field.
 
+HIDIOGCOLLECTIONINDEX - struct hiddev_usage_ref (write)
+Returns the collection index associated with this usage.  This
+indicates where in the collection hierarchy this usage sits.
+
 HIDIOCGFLAG - int (read)
 HIDIOCSFLAG - int (write)
 These operations respectively inspect and replace the mode flags
index ca7b3f198390beefb95e415abf52d56ffc80af78..6f82975e4838f6395a51a052b6318cde823c6f6e 100644 (file)
@@ -127,18 +127,41 @@ static int open_collection(struct hid_parser *parser, unsigned type)
 
        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 +189,8 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
 {
        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 +204,11 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage)
                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;
 }
 
@@ -221,8 +248,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
        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;
@@ -460,7 +490,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
 
        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);
@@ -621,17 +651,30 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
                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;
        }
@@ -643,6 +686,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
 
                if (item.format != HID_ITEM_FORMAT_SHORT) {
                        dbg("unexpected long global item");
+                       kfree(device->rdesc);
+                       kfree(device->collection);
                        hid_free_device(device);
                        kfree(parser);
                        return NULL;
@@ -651,6 +696,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
                if (dispatch_type[item.type](parser, &item)) {
                        dbg("item %u %u %u %u parsing failed\n",
                                item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
+                       kfree(device->rdesc);
+                       kfree(device->collection);
                        hid_free_device(device);
                        kfree(parser);
                        return NULL;
@@ -659,12 +706,16 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
                if (start == end) {
                        if (parser->collection_stack_ptr) {
                                dbg("unbalanced collection at end of report description");
+                               kfree(device->rdesc);
+                               kfree(device->collection);
                                hid_free_device(device);
                                kfree(parser);
                                return NULL;
                        }
                        if (parser->local.delimiter_depth) {
                                dbg("unbalanced delimiter at end of report description");
+                               kfree(device->rdesc);
+                               kfree(device->collection);
                                hid_free_device(device);
                                kfree(parser);
                                return NULL;
@@ -675,6 +726,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
        }
 
        dbg("item fetching failed at offset %d\n", (int)(end - start));
+       kfree(device->rdesc);
+       kfree(device->collection);
        hid_free_device(device);
        kfree(parser);
        return NULL;
@@ -1284,6 +1337,10 @@ void hid_init_reports(struct hid_device *hid)
 #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;
@@ -1301,6 +1358,8 @@ struct hid_blacklist {
        { 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 }
 };
 
@@ -1438,6 +1497,27 @@ fail:
        return NULL;
 }
 
+static void hid_disconnect(struct usb_device *dev, void *ptr)
+{
+       struct hid_device *hid = ptr;
+
+       usb_unlink_urb(hid->urbin);
+       usb_unlink_urb(hid->urbout);
+       usb_unlink_urb(hid->urbctrl);
+
+       if (hid->claimed & HID_CLAIMED_INPUT)
+               hidinput_disconnect(hid);
+       if (hid->claimed & HID_CLAIMED_HIDDEV)
+               hiddev_disconnect(hid);
+
+       usb_free_urb(hid->urbin);
+       usb_free_urb(hid->urbctrl);
+       if (hid->urbout)
+               usb_free_urb(hid->urbout);
+
+       hid_free_device(hid);
+}
+
 static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
                       const struct usb_device_id *id)
 {
@@ -1462,7 +1542,7 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
                hid->claimed |= HID_CLAIMED_HIDDEV;
 
        if (!hid->claimed) {
-               hid_free_device(hid);
+               hid_disconnect(dev, hid);
                return NULL;
        }
 
@@ -1476,11 +1556,14 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
                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;
                }
+       }
 
        usb_make_path(dev, path, 63);
 
@@ -1490,27 +1573,6 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
        return hid;
 }
 
-static void hid_disconnect(struct usb_device *dev, void *ptr)
-{
-       struct hid_device *hid = ptr;
-
-       usb_unlink_urb(hid->urbin);
-       usb_unlink_urb(hid->urbout);
-       usb_unlink_urb(hid->urbctrl);
-
-       if (hid->claimed & HID_CLAIMED_INPUT)
-               hidinput_disconnect(hid);
-       if (hid->claimed & HID_CLAIMED_HIDDEV)
-               hiddev_disconnect(hid);
-
-       usb_free_urb(hid->urbin);
-       usb_free_urb(hid->urbctrl);
-       if (hid->urbout)
-               usb_free_urb(hid->urbout);
-
-       hid_free_device(hid);
-}
-
 static struct usb_device_id hid_usb_ids [] = {
        { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
            bInterfaceClass: USB_INTERFACE_CLASS_HID },
index 62757fa2fe0506ead8efa71560c05e88d029b72c..9ae94a95d7d3cc9563d4de00213b924629bc84e3 100644 (file)
@@ -352,12 +352,6 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) {
        unsigned i,k;
        static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
        
-       for (i = 0; i < device->maxapplication; i++) {
-               printk("Application(");
-               resolv_usage(device->application[i]);
-               printk(")\n");
-       }
-
        for (i = 0; i < HID_REPORT_TYPES; i++) {
                report_enum = device->report_enum + i;
                list = report_enum->report_list.next;
index 37f12424cb6fa9ab6a00d5808b5adbda1081d4f1..a036b23f6fa69db28de5b8a50c053cc51bf19b49 100644 (file)
@@ -474,11 +474,12 @@ int hidinput_connect(struct hid_device *hid)
        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;
index 7862be44893888bb53a889bd98144a0d1dead786..93075dee2db759944d3e30cf806ee8405e418b22 100644 (file)
@@ -205,6 +205,7 @@ struct hid_item {
 #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
@@ -231,10 +232,11 @@ struct hid_global {
 
 #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;
@@ -249,10 +251,12 @@ struct hid_local {
 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 */
@@ -319,7 +323,9 @@ struct hid_control_fifo {
 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 */
@@ -374,7 +380,7 @@ struct hid_parser {
        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;
 };
index 8b93b2f0a1bcfeb4a175d115d5d379f5f5c8c864..8b5cd439eee774771a26f7a72f29cbf52ab5d12b 100644 (file)
@@ -80,6 +80,7 @@ extern struct usb_driver hiddev_driver;
 static struct hid_report *
 hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
 {
+       unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
        struct hid_report_enum *report_enum;
        struct list_head *list;
 
@@ -88,27 +89,28 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
 
        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];
@@ -256,8 +258,7 @@ static int hiddev_open(struct inode * inode, struct file * file) {
 /*
  * "write" file op
  */
-static ssize_t hiddev_write(struct file * file, const char * buffer,
-                           size_t count, loff_t *ppos)
+static ssize_t hiddev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
 {
        return -EINVAL;
 }
@@ -265,8 +266,7 @@ static ssize_t hiddev_write(struct file * file, const char * buffer,
 /*
  * "read" file op
  */
-static ssize_t hiddev_read(struct file * file, char * buffer, size_t count,
-                          loff_t *ppos)
+static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
 {
        DECLARE_WAITQUEUE(wait, current);
        struct hiddev_list *list = file->private_data;
@@ -354,17 +354,20 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait)
 /*
  * "ioctl" file op
  */
-static int hiddev_ioctl(struct inode *inode, struct file *file,
-                       unsigned int cmd, unsigned long arg)
+static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct hiddev_list *list = file->private_data;
        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;
 
@@ -376,11 +379,18 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
        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;
@@ -390,7 +400,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                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);
@@ -438,7 +447,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                }
 
        case HIDIOCINITREPORT:
-
                hid_init_reports(hid);
 
                return 0;
@@ -483,8 +491,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                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;
@@ -513,7 +519,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                finfo.unit = field->unit;
 
                return copy_to_user((void *) arg, &finfo, sizeof(finfo));
-       }
 
        case HIDIOCGUCODE:
                if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
@@ -536,9 +541,14 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                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 != HIDIOCGUSAGE && 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)
@@ -557,37 +567,35 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
                                return -EINVAL;
                }
 
-               uref.value = field->value[uref.usage_index];
-
-               return copy_to_user((void *) arg, &uref, sizeof(uref));
+               switch (cmd) {
+                       case HIDIOCGUSAGE:
+                               uref.value = field->value[uref.usage_index];
+                               return copy_to_user((void *) arg, &uref, sizeof(uref));
+                               return 0;
 
-       case HIDIOCSUSAGE:
-               if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
-                       return -EFAULT;
+                       case HIDIOCSUSAGE:
+                               field->value[uref.usage_index] = uref.value;
+                               return 0;
 
-               if (uref.report_type == HID_REPORT_TYPE_INPUT)
-                       return -EINVAL;
+                       case HIDIOCGCOLLECTIONINDEX:
+                               return field->usage[uref.usage_index].collection_index;
+               }
 
-               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;
+               return 0;
 
-                       if (uref.field_index >= report->maxfield)
-                               return -EINVAL;
+       case HIDIOCGCOLLECTIONINFO:
+               if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo)))
+                       return -EFAULT;
 
-                       field = report->field[uref.field_index];
-                       if (uref.usage_index >= field->maxusage)
-                               return -EINVAL;
-               }
+               if (cinfo.index >= hid->maxcollection)
+                       return -EINVAL;
 
-               field->value[uref.usage_index] = uref.value;
+               cinfo.type = hid->collection[cinfo.index].type;
+               cinfo.usage = hid->collection[cinfo.index].usage;
+               cinfo.level = hid->collection[cinfo.index].level;
 
+               if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo)))
+                       return -EFAULT;
                return 0;
 
        default:
@@ -628,11 +636,13 @@ int hiddev_connect(struct hid_device *hid)
        int retval;
        char devfs_name[16];
 
-       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 && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
                return -1;
 
        retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor);
@@ -657,10 +667,8 @@ int hiddev_connect(struct hid_device *hid)
 
        sprintf(devfs_name, "hiddev%d", minor);
        hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name,
-                                      DEVFS_FL_DEFAULT, USB_MAJOR,
-                                      minor + HIDDEV_MINOR_BASE,
-                                      S_IFCHR | S_IRUGO | S_IWUSR,
-                                      &hiddev_fops, NULL);
+               DEVFS_FL_DEFAULT, USB_MAJOR, minor + HIDDEV_MINOR_BASE,
+               S_IFCHR | S_IRUGO | S_IWUSR, &hiddev_fops, NULL);
        hid->minor = minor;
        hid->hiddev = hiddev;
 
index eb948560836d8a54a7e892c9f6600595ec0d3228..05db98ccfdbb5e1d331e8dcce4af35dbdaa85857 100644 (file)
@@ -49,6 +49,13 @@ struct hiddev_devinfo {
        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;
@@ -64,9 +71,9 @@ struct hiddev_report_info {
 /* To do a GUSAGE/SUSAGE, fill in at least usage_code,  report_type and 
  * report_id.  Set report_id to REPORT_ID_UNKNOWN if the rest of the fields 
  * are unknown.  Otherwise use a usage_ref struct filled in from a previous 
- * successful GUSAGE/SUSAGE call to save time.  To actually send a value
- * to the device, perform a SUSAGE first, followed by a SREPORT.  If an
- * INITREPORT is done, a GREPORT isn't necessary before a GUSAGE.
+ * successful GUSAGE call to save time.  To actually send a value to the
+ * device, perform a SUSAGE first, followed by a SREPORT.  An INITREPORT or a
+ * GREPORT isn't necessary for a GUSAGE to return valid data.
  */
 #define HID_REPORT_ID_UNKNOWN 0xffffffff
 #define HID_REPORT_ID_FIRST   0x00000100
@@ -129,7 +136,7 @@ struct hiddev_usage_ref {
  * Protocol version.
  */
 
-#define HID_VERSION            0x010003
+#define HID_VERSION            0x010004
 
 /*
  * IOCTLs (0x00 - 0x7f)
@@ -150,6 +157,8 @@ 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
@@ -197,7 +206,7 @@ void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref);
 int __init hiddev_init(void);
 void __exit hiddev_exit(void);
 #else
-static inline void *hiddev_connect(struct hid_device *hid) { return NULL; }
+static inline int hiddev_connect(struct hid_device *hid) { return -1; }
 static inline void hiddev_disconnect(struct hid_device *hid) { }
 static inline void hiddev_event(struct hid_device *hid, unsigned int usage, int value) { }
 static inline int hiddev_init(void) { return 0; }