Commit eea3860a authored by James Lamanna's avatar James Lamanna Committed by Vojtech Pavlik

input: Add a new ioctl to hiddev, which allows multiple usages to

       be set in a single request. Also fixes sizes of fields
       in hiddev structs to use _uXX types.
parent c5757b01
...@@ -403,7 +403,8 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -403,7 +403,8 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
struct hiddev_collection_info cinfo; struct hiddev_collection_info cinfo;
struct hiddev_report_info rinfo; struct hiddev_report_info rinfo;
struct hiddev_field_info finfo; struct hiddev_field_info finfo;
struct hiddev_usage_ref uref; struct hiddev_usage_ref_multi uref_multi;
struct hiddev_usage_ref *uref = &uref_multi.uref;
struct hiddev_devinfo dinfo; struct hiddev_devinfo dinfo;
struct hid_report *report; struct hid_report *report;
struct hid_field *field; struct hid_field *field;
...@@ -575,68 +576,98 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -575,68 +576,98 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return 0; return 0;
case HIDIOCGUCODE: case HIDIOCGUCODE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) if (copy_from_user(uref, (void *) arg, sizeof(*uref)))
return -EFAULT; return -EFAULT;
rinfo.report_type = uref.report_type; rinfo.report_type = uref->report_type;
rinfo.report_id = uref.report_id; rinfo.report_id = uref->report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL; return -EINVAL;
if (uref.field_index >= report->maxfield) if (uref->field_index >= report->maxfield)
return -EINVAL; return -EINVAL;
field = report->field[uref.field_index]; field = report->field[uref->field_index];
if (uref.usage_index >= field->maxusage) if (uref->usage_index >= field->maxusage)
return -EINVAL; return -EINVAL;
uref.usage_code = field->usage[uref.usage_index].hid; uref->usage_code = field->usage[uref->usage_index].hid;
if (copy_to_user((void *) arg, &uref, sizeof(uref))) if (copy_to_user((void *) arg, uref, sizeof(*uref)))
return -EFAULT; return -EFAULT;
return 0; return 0;
case HIDIOCGUSAGE: case HIDIOCGUSAGE:
case HIDIOCSUSAGE: case HIDIOCSUSAGE:
case HIDIOCGUSAGES:
case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX: case HIDIOCGCOLLECTIONINDEX:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
if (copy_from_user(&uref_multi, (void *) arg,
sizeof(uref_multi)))
return -EFAULT;
} else {
if (copy_from_user(uref, (void *) arg, sizeof(*uref)))
return -EFAULT; return -EFAULT;
}
if (cmd != HIDIOCGUSAGE && uref.report_type == HID_REPORT_TYPE_INPUT) if (cmd != HIDIOCGUSAGE &&
cmd != HIDIOCGUSAGES &&
uref->report_type == HID_REPORT_TYPE_INPUT)
return -EINVAL; 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)
return -EINVAL; return -EINVAL;
} else { } else {
rinfo.report_type = uref.report_type; rinfo.report_type = uref->report_type;
rinfo.report_id = uref.report_id; rinfo.report_id = uref->report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL; return -EINVAL;
if (uref.field_index >= report->maxfield) if (uref->field_index >= report->maxfield)
return -EINVAL;
field = report->field[uref->field_index];
if (uref->usage_index >= field->maxusage)
return -EINVAL; return -EINVAL;
field = report->field[uref.field_index]; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
if (uref.usage_index >= field->maxusage) if (uref_multi.num_values >= HID_MAX_USAGES ||
uref->usage_index >= field->maxusage ||
(uref->usage_index + uref_multi.num_values) >= field->maxusage)
return -EINVAL; return -EINVAL;
} }
}
switch (cmd) { switch (cmd) {
case HIDIOCGUSAGE: case HIDIOCGUSAGE:
uref.value = field->value[uref.usage_index]; uref->value = field->value[uref->usage_index];
if (copy_to_user((void *) arg, &uref, sizeof(uref))) if (copy_to_user((void *) arg, uref, sizeof(*uref)))
return -EFAULT; return -EFAULT;
return 0; return 0;
case HIDIOCSUSAGE: case HIDIOCSUSAGE:
field->value[uref.usage_index] = uref.value; field->value[uref->usage_index] = uref->value;
return 0; return 0;
case HIDIOCGCOLLECTIONINDEX: case HIDIOCGCOLLECTIONINDEX:
return field->usage[uref.usage_index].collection_index; return field->usage[uref->usage_index].collection_index;
case HIDIOCGUSAGES:
for (i = 0; i < uref_multi.num_values; i++)
uref_multi.values[i] =
field->value[uref->usage_index + i];
if (copy_to_user((void *) arg, &uref_multi,
sizeof(uref_multi)))
return -EFAULT;
return 0;
case HIDIOCSUSAGES:
for (i = 0; i < uref_multi.num_values; i++)
field->value[uref->usage_index + i] =
uref_multi.values[i];
return 0;
} }
return 0; return 0;
......
...@@ -39,33 +39,33 @@ struct hiddev_event { ...@@ -39,33 +39,33 @@ struct hiddev_event {
}; };
struct hiddev_devinfo { struct hiddev_devinfo {
unsigned int bustype; __u32 bustype;
unsigned int busnum; __u32 busnum;
unsigned int devnum; __u32 devnum;
unsigned int ifnum; __u32 ifnum;
short vendor; __s16 vendor;
short product; __s16 product;
short version; __s16 version;
unsigned num_applications; __u32 num_applications;
}; };
struct hiddev_collection_info { struct hiddev_collection_info {
unsigned index; __u32 index;
unsigned type; __u32 type;
unsigned usage; __u32 usage;
unsigned level; __u32 level;
}; };
#define HID_STRING_SIZE 256 #define HID_STRING_SIZE 256
struct hiddev_string_descriptor { struct hiddev_string_descriptor {
int index; __s32 index;
char value[HID_STRING_SIZE]; char value[HID_STRING_SIZE];
}; };
struct hiddev_report_info { struct hiddev_report_info {
unsigned report_type; __u32 report_type;
unsigned report_id; __u32 report_id;
unsigned num_fields; __u32 num_fields;
}; };
/* 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
...@@ -88,20 +88,20 @@ struct hiddev_report_info { ...@@ -88,20 +88,20 @@ struct hiddev_report_info {
#define HID_REPORT_TYPE_MAX 3 #define HID_REPORT_TYPE_MAX 3
struct hiddev_field_info { struct hiddev_field_info {
unsigned report_type; __u32 report_type;
unsigned report_id; __u32 report_id;
unsigned field_index; __u32 field_index;
unsigned maxusage; __u32 maxusage;
unsigned flags; __u32 flags;
unsigned physical; /* physical usage for this field */ __u32 physical; /* physical usage for this field */
unsigned logical; /* logical usage for this field */ __u32 logical; /* logical usage for this field */
unsigned application; /* application usage for this field */ __u32 application; /* application usage for this field */
__s32 logical_minimum; __s32 logical_minimum;
__s32 logical_maximum; __s32 logical_maximum;
__s32 physical_minimum; __s32 physical_minimum;
__s32 physical_maximum; __s32 physical_maximum;
unsigned unit_exponent; __u32 unit_exponent;
unsigned unit; __u32 unit;
}; };
/* Fill in report_type, report_id and field_index to get the information on a /* Fill in report_type, report_id and field_index to get the information on a
...@@ -118,14 +118,22 @@ struct hiddev_field_info { ...@@ -118,14 +118,22 @@ struct hiddev_field_info {
#define HID_FIELD_BUFFERED_BYTE 0x100 #define HID_FIELD_BUFFERED_BYTE 0x100
struct hiddev_usage_ref { struct hiddev_usage_ref {
unsigned report_type; __u32 report_type;
unsigned report_id; __u32 report_id;
unsigned field_index; __u32 field_index;
unsigned usage_index; __u32 usage_index;
unsigned usage_code; __u32 usage_code;
__s32 value; __s32 value;
}; };
/* hiddev_usage_ref_multi is used for sending multiple bytes to a control.
* It really manifests itself as setting the value of consecutive usages */
struct hiddev_usage_ref_multi {
struct hiddev_usage_ref uref;
__u32 num_values;
__s32 values[HID_MAX_USAGES];
};
/* FIELD_INDEX_NONE is returned in read() data from the kernel when flags /* 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 * is set to (HIDDEV_FLAG_UREF | HIDDEV_FLAG_REPORT) and a new report has
* been sent by the device * been sent by the device
...@@ -161,6 +169,10 @@ struct hiddev_usage_ref { ...@@ -161,6 +169,10 @@ struct hiddev_usage_ref {
#define HIDIOCGCOLLECTIONINFO _IOWR('H', 0x11, struct hiddev_collection_info) #define HIDIOCGCOLLECTIONINFO _IOWR('H', 0x11, struct hiddev_collection_info)
#define HIDIOCGPHYS(len) _IOC(_IOC_READ, 'H', 0x12, len) #define HIDIOCGPHYS(len) _IOC(_IOC_READ, 'H', 0x12, len)
/* For writing/reading to multiple/consecutive usages */
#define HIDIOCGUSAGES _IOWR('H', 0x13, struct hiddev_usage_ref_multi)
#define HIDIOCSUSAGES _IOW('H', 0x14, struct hiddev_usage_ref_multi)
/* /*
* Flags to be used in HIDIOCSFLAG * Flags to be used in HIDIOCSFLAG
*/ */
......
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