Commit b24be862 authored by Vojtech Pavlik's avatar Vojtech Pavlik

[PATCH] Updates for hiddev by Paul Stewart

I've merged a patch Paul Stewart sent me some time ago, which should make life
easier for the guys writing UPS daemons.
parent 182147c1
...@@ -18,7 +18,7 @@ normalised event interface - see Documentation/input/input.txt ...@@ -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 data flow for a HID event produced by a device is something like
the following : 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 --> hiddev.c ----> POWER / MONITOR CONTROL
...@@ -106,6 +106,15 @@ returns -1. You can find out beforehand how many application ...@@ -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 collections the device has from the num_applications field from the
hiddev_devinfo structure. 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) HIDIOCGDEVINFO - struct hiddev_devinfo (read)
Gets a hiddev_devinfo structure which describes the device. 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 ...@@ -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 hiddev_usage_ref structure as above, but additionally fills in
the value field. 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) HIDIOCGFLAG - int (read)
HIDIOCSFLAG - int (write) HIDIOCSFLAG - int (write)
These operations respectively inspect and replace the mode flags These operations respectively inspect and replace the mode flags
......
...@@ -127,18 +127,41 @@ static int open_collection(struct hid_parser *parser, unsigned type) ...@@ -127,18 +127,41 @@ static int open_collection(struct hid_parser *parser, unsigned type)
usage = parser->local.usage[0]; 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) { if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
dbg("collection stack overflow"); dbg("collection stack overflow");
return -1; 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->type = type;
collection->usage = usage; collection->usage = usage;
collection->level = parser->collection_stack_ptr - 1;
if (type == HID_COLLECTION_APPLICATION)
parser->device->maxapplication++;
return 0; return 0;
} }
...@@ -166,8 +189,8 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) ...@@ -166,8 +189,8 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
{ {
int n; int n;
for (n = parser->collection_stack_ptr - 1; n >= 0; n--) for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
if (parser->collection_stack[n].type == type) if (parser->device->collection[parser->collection_stack[n]].type == type)
return parser->collection_stack[n].usage; return parser->device->collection[parser->collection_stack[n]].usage;
return 0; /* we know nothing about this usage type */ return 0; /* we know nothing about this usage type */
} }
...@@ -181,7 +204,11 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage) ...@@ -181,7 +204,11 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage)
dbg("usage index exceeded"); dbg("usage index exceeded");
return -1; 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; return 0;
} }
...@@ -221,8 +248,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -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->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); 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].hid = parser->local.usage[i];
field->usage[i].collection_index =
parser->local.collection_index[i];
}
field->maxusage = usages; field->maxusage = usages;
field->flags = flags; field->flags = flags;
...@@ -460,7 +490,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ...@@ -460,7 +490,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
switch (item->tag) { switch (item->tag) {
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
ret = open_collection(parser, data & 3); ret = open_collection(parser, data & 0xff);
break; break;
case HID_MAIN_ITEM_TAG_END_COLLECTION: case HID_MAIN_ITEM_TAG_END_COLLECTION:
ret = close_collection(parser); ret = close_collection(parser);
...@@ -621,17 +651,30 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) ...@@ -621,17 +651,30 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
return NULL; return NULL;
memset(device, 0, sizeof(struct hid_device)); 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++) for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&device->report_enum[i].report_list); INIT_LIST_HEAD(&device->report_enum[i].report_list);
if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) { if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
kfree(device->collection);
kfree(device); kfree(device);
return NULL; return NULL;
} }
memcpy(device->rdesc, start, size); memcpy(device->rdesc, start, size);
device->rsize = size;
if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
kfree(device->rdesc); kfree(device->rdesc);
kfree(device->collection);
kfree(device); kfree(device);
return NULL; return NULL;
} }
...@@ -643,6 +686,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) ...@@ -643,6 +686,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (item.format != HID_ITEM_FORMAT_SHORT) { if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg("unexpected long global item"); dbg("unexpected long global item");
kfree(device->rdesc);
kfree(device->collection);
hid_free_device(device); hid_free_device(device);
kfree(parser); kfree(parser);
return NULL; return NULL;
...@@ -651,6 +696,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) ...@@ -651,6 +696,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (dispatch_type[item.type](parser, &item)) { if (dispatch_type[item.type](parser, &item)) {
dbg("item %u %u %u %u parsing failed\n", dbg("item %u %u %u %u parsing failed\n",
item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
kfree(device->rdesc);
kfree(device->collection);
hid_free_device(device); hid_free_device(device);
kfree(parser); kfree(parser);
return NULL; return NULL;
...@@ -659,12 +706,16 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) ...@@ -659,12 +706,16 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (start == end) { if (start == end) {
if (parser->collection_stack_ptr) { if (parser->collection_stack_ptr) {
dbg("unbalanced collection at end of report description"); dbg("unbalanced collection at end of report description");
kfree(device->rdesc);
kfree(device->collection);
hid_free_device(device); hid_free_device(device);
kfree(parser); kfree(parser);
return NULL; return NULL;
} }
if (parser->local.delimiter_depth) { if (parser->local.delimiter_depth) {
dbg("unbalanced delimiter at end of report description"); dbg("unbalanced delimiter at end of report description");
kfree(device->rdesc);
kfree(device->collection);
hid_free_device(device); hid_free_device(device);
kfree(parser); kfree(parser);
return NULL; return NULL;
...@@ -675,6 +726,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) ...@@ -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)); dbg("item fetching failed at offset %d\n", (int)(end - start));
kfree(device->rdesc);
kfree(device->collection);
hid_free_device(device); hid_free_device(device);
kfree(parser); kfree(parser);
return NULL; return NULL;
...@@ -1284,6 +1337,10 @@ void hid_init_reports(struct hid_device *hid) ...@@ -1284,6 +1337,10 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 #define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #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 { struct hid_blacklist {
__u16 idVendor; __u16 idVendor;
__u16 idProduct; __u16 idProduct;
...@@ -1301,6 +1358,8 @@ struct hid_blacklist { ...@@ -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_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, 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_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 } { 0, 0 }
}; };
...@@ -1438,6 +1497,27 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) ...@@ -1438,6 +1497,27 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
return NULL; 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, static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
...@@ -1462,7 +1542,7 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1462,7 +1542,7 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
hid->claimed |= HID_CLAIMED_HIDDEV; hid->claimed |= HID_CLAIMED_HIDDEV;
if (!hid->claimed) { if (!hid->claimed) {
hid_free_device(hid); hid_disconnect(dev, hid);
return NULL; return NULL;
} }
...@@ -1476,11 +1556,14 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1476,11 +1556,14 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
printk("hiddev%d", hid->minor); printk("hiddev%d", hid->minor);
c = "Device"; c = "Device";
for (i = 0; i < hid->maxapplication; i++) for (i = 0; i < hid->maxcollection; i++) {
if ((hid->application[i] & 0xffff) < ARRAY_SIZE(hid_types)) { if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
c = hid_types[hid->application[i] & 0xffff]; (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; break;
} }
}
usb_make_path(dev, path, 63); usb_make_path(dev, path, 63);
...@@ -1490,27 +1573,6 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1490,27 +1573,6 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
return hid; 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 [] = { static struct usb_device_id hid_usb_ids [] = {
{ match_flags: USB_DEVICE_ID_MATCH_INT_CLASS, { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
bInterfaceClass: USB_INTERFACE_CLASS_HID }, bInterfaceClass: USB_INTERFACE_CLASS_HID },
......
...@@ -352,12 +352,6 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) { ...@@ -352,12 +352,6 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) {
unsigned i,k; unsigned i,k;
static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; 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++) { for (i = 0; i < HID_REPORT_TYPES; i++) {
report_enum = device->report_enum + i; report_enum = device->report_enum + i;
list = report_enum->report_list.next; list = report_enum->report_list.next;
......
...@@ -474,11 +474,12 @@ int hidinput_connect(struct hid_device *hid) ...@@ -474,11 +474,12 @@ int hidinput_connect(struct hid_device *hid)
struct list_head *list; struct list_head *list;
int i, j, k; int i, j, k;
for (i = 0; i < hid->maxapplication; i++) for (i = 0; i < hid->maxcollection; i++)
if (IS_INPUT_APPLICATION(hid->application[i])) if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
IS_INPUT_APPLICATION(hid->collection[i].usage))
break; break;
if (i == hid->maxapplication) if (i == hid->maxcollection)
return -1; return -1;
hid->input.private = hid; hid->input.private = hid;
......
...@@ -205,6 +205,7 @@ struct hid_item { ...@@ -205,6 +205,7 @@ struct hid_item {
#define HID_QUIRK_NOTOUCH 0x02 #define HID_QUIRK_NOTOUCH 0x02
#define HID_QUIRK_IGNORE 0x04 #define HID_QUIRK_IGNORE 0x04
#define HID_QUIRK_NOGET 0x08 #define HID_QUIRK_NOGET 0x08
#define HID_QUIRK_HIDDEV 0x10
/* /*
* This is the global enviroment of the parser. This information is * This is the global enviroment of the parser. This information is
...@@ -231,10 +232,11 @@ struct hid_global { ...@@ -231,10 +232,11 @@ struct hid_global {
#define HID_MAX_DESCRIPTOR_SIZE 4096 #define HID_MAX_DESCRIPTOR_SIZE 4096
#define HID_MAX_USAGES 1024 #define HID_MAX_USAGES 1024
#define HID_MAX_APPLICATIONS 16 #define HID_DEFAULT_NUM_COLLECTIONS 16
struct hid_local { struct hid_local {
unsigned usage[HID_MAX_USAGES]; /* usage array */ unsigned usage[HID_MAX_USAGES]; /* usage array */
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
unsigned usage_index; unsigned usage_index;
unsigned usage_minimum; unsigned usage_minimum;
unsigned delimiter_depth; unsigned delimiter_depth;
...@@ -249,10 +251,12 @@ struct hid_local { ...@@ -249,10 +251,12 @@ struct hid_local {
struct hid_collection { struct hid_collection {
unsigned type; unsigned type;
unsigned usage; unsigned usage;
unsigned level;
}; };
struct hid_usage { struct hid_usage {
unsigned hid; /* hid usage code */ unsigned hid; /* hid usage code */
unsigned collection_index; /* index into collection array */
__u16 code; /* input driver code */ __u16 code; /* input driver code */
__u8 type; /* input driver type */ __u8 type; /* input driver type */
__s8 hat_min; /* hat switch fun */ __s8 hat_min; /* hat switch fun */
...@@ -319,7 +323,9 @@ struct hid_control_fifo { ...@@ -319,7 +323,9 @@ struct hid_control_fifo {
struct hid_device { /* device report descriptor */ struct hid_device { /* device report descriptor */
__u8 *rdesc; __u8 *rdesc;
unsigned rsize; 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 maxapplication; /* Number of applications */
unsigned version; /* HID version */ unsigned version; /* HID version */
unsigned country; /* HID country */ unsigned country; /* HID country */
...@@ -374,7 +380,7 @@ struct hid_parser { ...@@ -374,7 +380,7 @@ struct hid_parser {
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
unsigned global_stack_ptr; unsigned global_stack_ptr;
struct hid_local local; struct hid_local local;
struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE]; unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
unsigned collection_stack_ptr; unsigned collection_stack_ptr;
struct hid_device *device; struct hid_device *device;
}; };
......
...@@ -80,6 +80,7 @@ extern struct usb_driver hiddev_driver; ...@@ -80,6 +80,7 @@ extern struct usb_driver hiddev_driver;
static struct hid_report * static struct hid_report *
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) 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 hid_report_enum *report_enum;
struct list_head *list; struct list_head *list;
...@@ -88,27 +89,28 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) ...@@ -88,27 +89,28 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
report_enum = hid->report_enum + report_enum = hid->report_enum +
(rinfo->report_type - HID_REPORT_TYPE_MIN); (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: switch (flags) {
return NULL; 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]; return report_enum->report_id_hash[rinfo->report_id];
...@@ -256,8 +258,7 @@ static int hiddev_open(struct inode * inode, struct file * file) { ...@@ -256,8 +258,7 @@ static int hiddev_open(struct inode * inode, struct file * file) {
/* /*
* "write" file op * "write" file op
*/ */
static ssize_t hiddev_write(struct file * file, const char * buffer, static ssize_t hiddev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
{ {
return -EINVAL; return -EINVAL;
} }
...@@ -265,8 +266,7 @@ static ssize_t hiddev_write(struct file * file, const char * buffer, ...@@ -265,8 +266,7 @@ static ssize_t hiddev_write(struct file * file, const char * buffer,
/* /*
* "read" file op * "read" file op
*/ */
static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
loff_t *ppos)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
struct hiddev_list *list = file->private_data; struct hiddev_list *list = file->private_data;
...@@ -354,17 +354,20 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait) ...@@ -354,17 +354,20 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait)
/* /*
* "ioctl" file op * "ioctl" file op
*/ */
static int hiddev_ioctl(struct inode *inode, struct file *file, static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
unsigned int cmd, unsigned long arg)
{ {
struct hiddev_list *list = file->private_data; struct hiddev_list *list = file->private_data;
struct hiddev *hiddev = list->hiddev; struct hiddev *hiddev = list->hiddev;
struct hid_device *hid = hiddev->hid; struct hid_device *hid = hiddev->hid;
struct usb_device *dev = hid->dev; struct usb_device *dev = hid->dev;
struct hiddev_collection_info cinfo;
struct hiddev_report_info rinfo; struct hiddev_report_info rinfo;
struct hiddev_field_info finfo;
struct hiddev_usage_ref uref; struct hiddev_usage_ref uref;
struct hiddev_devinfo dinfo;
struct hid_report *report; struct hid_report *report;
struct hid_field *field; struct hid_field *field;
int i;
if (!hiddev->exist) return -EIO; if (!hiddev->exist) return -EIO;
...@@ -376,11 +379,18 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -376,11 +379,18 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
case HIDIOCAPPLICATION: case HIDIOCAPPLICATION:
if (arg < 0 || arg >= hid->maxapplication) if (arg < 0 || arg >= hid->maxapplication)
return -EINVAL; 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: case HIDIOCGDEVINFO:
{
struct hiddev_devinfo dinfo;
dinfo.bustype = BUS_USB; dinfo.bustype = BUS_USB;
dinfo.busnum = dev->bus->busnum; dinfo.busnum = dev->bus->busnum;
dinfo.devnum = dev->devnum; dinfo.devnum = dev->devnum;
...@@ -390,7 +400,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -390,7 +400,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
dinfo.version = dev->descriptor.bcdDevice; dinfo.version = dev->descriptor.bcdDevice;
dinfo.num_applications = hid->maxapplication; dinfo.num_applications = hid->maxapplication;
return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
}
case HIDIOCGFLAG: case HIDIOCGFLAG:
return put_user(list->flags, (int *) arg); return put_user(list->flags, (int *) arg);
...@@ -438,7 +447,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -438,7 +447,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
} }
case HIDIOCINITREPORT: case HIDIOCINITREPORT:
hid_init_reports(hid); hid_init_reports(hid);
return 0; return 0;
...@@ -483,8 +491,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -483,8 +491,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return copy_to_user((void *) arg, &rinfo, sizeof(rinfo)); return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
case HIDIOCGFIELDINFO: case HIDIOCGFIELDINFO:
{
struct hiddev_field_info finfo;
if (copy_from_user(&finfo, (void *) arg, sizeof(finfo))) if (copy_from_user(&finfo, (void *) arg, sizeof(finfo)))
return -EFAULT; return -EFAULT;
rinfo.report_type = finfo.report_type; rinfo.report_type = finfo.report_type;
...@@ -513,7 +519,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -513,7 +519,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
finfo.unit = field->unit; finfo.unit = field->unit;
return copy_to_user((void *) arg, &finfo, sizeof(finfo)); return copy_to_user((void *) arg, &finfo, sizeof(finfo));
}
case HIDIOCGUCODE: case HIDIOCGUCODE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
...@@ -536,9 +541,14 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -536,9 +541,14 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return copy_to_user((void *) arg, &uref, sizeof(uref)); return copy_to_user((void *) arg, &uref, sizeof(uref));
case HIDIOCGUSAGE: case HIDIOCGUSAGE:
case HIDIOCSUSAGE:
case HIDIOCGCOLLECTIONINDEX:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
return -EFAULT; return -EFAULT;
if (cmd != HIDIOCGUSAGE && uref.report_type == HID_REPORT_TYPE_INPUT)
return -EINVAL;
if (uref.report_id == HID_REPORT_ID_UNKNOWN) { if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, &uref); field = hiddev_lookup_usage(hid, &uref);
if (field == NULL) if (field == NULL)
...@@ -557,37 +567,35 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -557,37 +567,35 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return -EINVAL; return -EINVAL;
} }
uref.value = field->value[uref.usage_index]; switch (cmd) {
case HIDIOCGUSAGE:
return copy_to_user((void *) arg, &uref, sizeof(uref)); uref.value = field->value[uref.usage_index];
return copy_to_user((void *) arg, &uref, sizeof(uref));
return 0;
case HIDIOCSUSAGE: case HIDIOCSUSAGE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) field->value[uref.usage_index] = uref.value;
return -EFAULT; return 0;
if (uref.report_type == HID_REPORT_TYPE_INPUT) case HIDIOCGCOLLECTIONINDEX:
return -EINVAL; return field->usage[uref.usage_index].collection_index;
}
if (uref.report_id == HID_REPORT_ID_UNKNOWN) { return 0;
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) case HIDIOCGCOLLECTIONINFO:
return -EINVAL; if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo)))
return -EFAULT;
field = report->field[uref.field_index]; if (cinfo.index >= hid->maxcollection)
if (uref.usage_index >= field->maxusage) return -EINVAL;
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; return 0;
default: default:
...@@ -628,11 +636,13 @@ int hiddev_connect(struct hid_device *hid) ...@@ -628,11 +636,13 @@ int hiddev_connect(struct hid_device *hid)
int retval; int retval;
char devfs_name[16]; char devfs_name[16];
for (i = 0; i < hid->maxapplication; i++) for (i = 0; i < hid->maxcollection; i++)
if (!IS_INPUT_APPLICATION(hid->application[i])) if (hid->collection[i].type ==
HID_COLLECTION_APPLICATION &&
!IS_INPUT_APPLICATION(hid->collection[i].usage))
break; break;
if (i == hid->maxapplication) if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
return -1; return -1;
retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor); retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor);
...@@ -657,10 +667,8 @@ int hiddev_connect(struct hid_device *hid) ...@@ -657,10 +667,8 @@ int hiddev_connect(struct hid_device *hid)
sprintf(devfs_name, "hiddev%d", minor); sprintf(devfs_name, "hiddev%d", minor);
hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name, hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name,
DEVFS_FL_DEFAULT, USB_MAJOR, DEVFS_FL_DEFAULT, USB_MAJOR, minor + HIDDEV_MINOR_BASE,
minor + HIDDEV_MINOR_BASE, S_IFCHR | S_IRUGO | S_IWUSR, &hiddev_fops, NULL);
S_IFCHR | S_IRUGO | S_IWUSR,
&hiddev_fops, NULL);
hid->minor = minor; hid->minor = minor;
hid->hiddev = hiddev; hid->hiddev = hiddev;
......
...@@ -49,6 +49,13 @@ struct hiddev_devinfo { ...@@ -49,6 +49,13 @@ struct hiddev_devinfo {
unsigned num_applications; unsigned num_applications;
}; };
struct hiddev_collection_info {
unsigned index;
unsigned type;
unsigned usage;
unsigned level;
};
#define HID_STRING_SIZE 256 #define HID_STRING_SIZE 256
struct hiddev_string_descriptor { struct hiddev_string_descriptor {
int index; int index;
...@@ -64,9 +71,9 @@ struct hiddev_report_info { ...@@ -64,9 +71,9 @@ struct hiddev_report_info {
/* To do a GUSAGE/SUSAGE, fill in at least usage_code, report_type and /* 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 * 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 * 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 * successful GUSAGE call to save time. To actually send a value to the
* to the device, perform a SUSAGE first, followed by a SREPORT. If an * device, perform a SUSAGE first, followed by a SREPORT. An INITREPORT or a
* INITREPORT is done, a GREPORT isn't necessary before a GUSAGE. * GREPORT isn't necessary for a GUSAGE to return valid data.
*/ */
#define HID_REPORT_ID_UNKNOWN 0xffffffff #define HID_REPORT_ID_UNKNOWN 0xffffffff
#define HID_REPORT_ID_FIRST 0x00000100 #define HID_REPORT_ID_FIRST 0x00000100
...@@ -129,7 +136,7 @@ struct hiddev_usage_ref { ...@@ -129,7 +136,7 @@ struct hiddev_usage_ref {
* Protocol version. * Protocol version.
*/ */
#define HID_VERSION 0x010003 #define HID_VERSION 0x010004
/* /*
* IOCTLs (0x00 - 0x7f) * IOCTLs (0x00 - 0x7f)
...@@ -150,6 +157,8 @@ struct hiddev_usage_ref { ...@@ -150,6 +157,8 @@ struct hiddev_usage_ref {
#define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) #define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref)
#define HIDIOCGFLAG _IOR('H', 0x0E, int) #define HIDIOCGFLAG _IOR('H', 0x0E, int)
#define HIDIOCSFLAG _IOW('H', 0x0F, 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 * Flags to be used in HIDIOCSFLAG
...@@ -197,7 +206,7 @@ void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref); ...@@ -197,7 +206,7 @@ void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref);
int __init hiddev_init(void); int __init hiddev_init(void);
void __exit hiddev_exit(void); void __exit hiddev_exit(void);
#else #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_disconnect(struct hid_device *hid) { }
static inline void hiddev_event(struct hid_device *hid, unsigned int usage, int value) { } static inline void hiddev_event(struct hid_device *hid, unsigned int usage, int value) { }
static inline int hiddev_init(void) { return 0; } static inline int hiddev_init(void) { return 0; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment