@@ -72,8 +72,10 @@ The hiddev API uses a read() interface, and a set of ioctl() calls.
HID devices exchange data with the host computer using data
bundles called "reports". Each report is divided into "fields",
-each of which can have one or more "usages". In the hid-core,
-each one of these usages has a single signed 32 bit value.
+each of which can have one or more "usages". Each of these usages
+has a value, usually a 32 bit or smaller signed value. (The
+hid-core can process larger values, but these are not currently
+exposed through hiddev.)
read():
-------
@@ -86,22 +86,35 @@ struct hid_report *hid_register_report(struct hid_device *device,
}
EXPORT_SYMBOL_GPL(hid_register_report);
+// How many 32-bit values are needed to store a field?
+static unsigned hid_field_size_in_values(unsigned flags, unsigned size_in_bits)
+{
+ if (!(flags & HID_MAIN_ITEM_VARIABLE)) {
+ return 1;
+ } else {
+ return DIV_ROUND_UP(size_in_bits, 32);
+ }
+}
+
/*
* Register a new field for this report.
*/
-static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages)
+static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned flags, unsigned size_in_bits)
{
struct hid_field *field;
+ unsigned size_in_values;
if (report->maxfield == HID_MAX_FIELDS) {
hid_err(report->device, "too many fields in report\n");
return NULL;
}
+ size_in_values = hid_field_size_in_values(flags, size_in_bits);
+
field = kzalloc((sizeof(struct hid_field) +
usages * sizeof(struct hid_usage) +
- usages * sizeof(unsigned)), GFP_KERNEL);
+ usages * size_in_values * sizeof(s32)), GFP_KERNEL);
if (!field)
return NULL;
@@ -300,7 +313,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
usages = max_t(unsigned, parser->local.usage_index,
parser->global.report_count);
- field = hid_register_field(report, usages);
+ field = hid_register_field(report, usages, flags, parser->global.report_size);
if (!field)
return 0;
@@ -1340,6 +1353,9 @@ static u32 s32ton(__s32 value, unsigned n)
* While the USB HID spec allows unlimited length bit fields in "report
* descriptors", most devices never use more than 16 bits.
* One model of UPS is claimed to report "LINEV" as a 32-bit field.
+ * Some digitizers report stylus transducer IDs as 64-bit fields.
+ * The outer routines will extract multiple 32-bit parts if necessary
+ * to retrieve an entire field.
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/
@@ -1495,16 +1511,19 @@ static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage)
}
static void hid_process_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, int interrupt)
+ struct hid_usage *usage, const __s32 * values, unsigned value_count, int interrupt)
{
struct hid_driver *hdrv = hid->driver;
int ret;
+ if (unlikely(value_count == 0))
+ return;
+
if (!list_empty(&hid->debug_list))
- hid_dump_input(hid, usage, value);
+ hid_dump_input(hid, usage, values, value_count);
if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
- ret = hdrv->event(hid, field, usage, value);
+ ret = hdrv->event(hid, field, usage, values[0]);
if (ret != 0) {
if (ret < 0)
hid_err(hid, "%s's event failed with %d\n",
@@ -1514,9 +1533,9 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
}
if (hid->claimed & HID_CLAIMED_INPUT)
- hidinput_hid_event(hid, field, usage, value);
+ hidinput_hid_event(hid, field, usage, values, value_count);
if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event)
- hid->hiddev_hid_event(hid, field, usage, value);
+ hid->hiddev_hid_event(hid, field, usage, values[0]);
}
/*
@@ -1528,24 +1547,41 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
static void hid_input_field(struct hid_device *hid, struct hid_field *field,
__u8 *data, int interrupt)
{
- unsigned n;
+ unsigned n, v;
unsigned count = field->report_count;
unsigned offset = field->report_offset;
- unsigned size = field->report_size;
+ unsigned size_in_bits = field->report_size;
+ unsigned size_in_values; /* storage size in 32-bit values, always >= 1 */
+ unsigned last_value_size_in_bits; /* bits in most significant/last value */
+ unsigned bit_pos;
__s32 min = field->logical_minimum;
__s32 max = field->logical_maximum;
__s32 *value;
+ static const __s32 zero = 0;
+ static const __s32 one = 1;
+
+ size_in_values = hid_field_size_in_values(field->flags, size_in_bits);
+ last_value_size_in_bits = (size_in_bits % 32) ?: 32;
- value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
+ value = kmalloc_array(count * size_in_values, sizeof(__s32), GFP_ATOMIC);
if (!value)
return;
- for (n = 0; n < count; n++) {
+ for (n = 0; n < count * size_in_values; n += size_in_values) {
+ v = 0;
+ bit_pos = offset + (n * size_in_bits);
+
+ // Extract least significant values for fields longer than 32 bits.
+ if (size_in_values > 1) {
+ for (; v < size_in_values - 1; v++, bit_pos += 32)
+ value[n+v] = hid_field_extract(hid, data, bit_pos, 32);
+ }
- value[n] = min < 0 ?
- snto32(hid_field_extract(hid, data, offset + n * size,
- size), size) :
- hid_field_extract(hid, data, offset + n * size, size);
+ // May need to sign extend the most significant value.
+ value[n+v] = min < 0 ?
+ sign_extend32(hid_field_extract(hid, data, bit_pos,
+ last_value_size_in_bits), last_value_size_in_bits) :
+ hid_field_extract(hid, data, bit_pos, last_value_size_in_bits);
/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
@@ -1555,10 +1591,10 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
goto exit;
}
- for (n = 0; n < count; n++) {
+ for (n = 0; n < count * size_in_values; n += size_in_values) {
if (HID_MAIN_ITEM_VARIABLE & field->flags) {
- hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
+ hid_process_event(hid, field, &field->usage[n], value + n, size_in_values, interrupt);
continue;
}
@@ -1566,16 +1602,16 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
&& field->value[n] - min < field->maxusage
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
- hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
+ hid_process_event(hid, field, &field->usage[field->value[n] - min], &zero, 1, interrupt);
if (value[n] >= min && value[n] <= max
&& value[n] - min < field->maxusage
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
- hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
+ hid_process_event(hid, field, &field->usage[value[n] - min], &one, 1, interrupt);
}
- memcpy(field->value, value, count * sizeof(__s32));
+ memcpy(field->value, value, count * size_in_values * sizeof(__s32));
exit:
kfree(value);
}
@@ -1662,7 +1698,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
size = field->report_size;
- hid_dump_input(field->report->device, field->usage + offset, value);
+ hid_dump_input(field->report->device, field->usage + offset, &value, 1);
if (offset >= field->report_count) {
hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n",
@@ -692,21 +692,30 @@ void hid_dump_report(struct hid_device *hid, int type, u8 *data,
}
EXPORT_SYMBOL_GPL(hid_dump_report);
-void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value)
+void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, const __s32 *values, unsigned value_count)
{
char *buf;
int len;
+ unsigned n;
- buf = hid_resolv_usage(usage->hid, NULL);
- if (!buf)
- return;
- len = strlen(buf);
- snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value);
+ for (n = 0; n < value_count; n++) {
- hid_debug_event(hdev, buf);
+ buf = hid_resolv_usage(usage->hid, NULL);
+ if (!buf)
+ return;
- kfree(buf);
- wake_up_interruptible(&hdev->debug_wait);
+ len = strlen(buf);
+
+ if (value_count == 1)
+ snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", values[n]);
+ else
+ snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, "[%d] = %d (%08x)\n", n, values[n], values[n]);
+
+ hid_debug_event(hdev, buf);
+
+ kfree(buf);
+ wake_up_interruptible(&hdev->debug_wait);
+ }
}
EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -1273,10 +1273,18 @@ static void hidinput_handle_scroll(struct hid_usage *usage,
input_event(input, EV_REL, usage->code, hi_res);
}
-void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
+void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, const __s32 *values, unsigned value_count)
{
struct input_dev *input;
unsigned *quirks = &hid->quirks;
+ __s32 value;
+
+ if (unlikely(value_count == 0))
+ return;
+
+ // The majority of this code was writen to only understand 32-bit sized
+ // values, and anything larger was truncated: we continue that tradition.
+ value = values[0];
if (!usage->type)
return;
@@ -16,7 +16,7 @@
#define HID_DEBUG_BUFSIZE 512
#define HID_DEBUG_FIFOSIZE 512
-void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
+void hid_dump_input(struct hid_device *, struct hid_usage *, const __s32 *, unsigned);
void hid_dump_report(struct hid_device *, int , u8 *, int);
void hid_dump_device(struct hid_device *, struct seq_file *);
void hid_dump_field(struct hid_field *, int, struct seq_file *);
@@ -37,7 +37,7 @@ struct hid_debug_list {
#else
-#define hid_dump_input(a,b,c) do { } while (0)
+#define hid_dump_input(a,b,c,d) do { } while (0)
#define hid_dump_report(a,b,c,d) do { } while (0)
#define hid_dump_device(a,b) do { } while (0)
#define hid_dump_field(a,b,c) do { } while (0)
@@ -878,7 +878,7 @@ extern void hid_unregister_driver(struct hid_driver *);
module_driver(__hid_driver, hid_register_driver, \
hid_unregister_driver)
-extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
+extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, const __s32 *values, unsigned value_count);
extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
extern int hidinput_connect(struct hid_device *hid, unsigned int force);
extern void hidinput_disconnect(struct hid_device *);
Extends hid-core to process HID fields larger than 32 bits, by storing them as multiple consecutive 32-bit values. hid-input and hid-debug are specifically changed to match. Currently, no other hid consumers get an interface to see fields larger than 32 bits. All existing code, including hid-input processing, will by default see the least significant 32-bits of the field, so they have unchanged behaviour (sign extension does not change: for any field longer than 32 bits, extension was already a no-op, so the least significant 32-bit value will be identical. The most significant value, at the far end, will now be extended to fill its 32-bit value.) Logical min/max interaction with larger fields is limited, as min/max and other item parameters can only be described with 32-bit values (sometimes signed). hid-input is expected to ignore min/max in scenarios where a specific larger field is involved. hid-debug 'events' debugfs text report format is changed, but only for HID fields larger than 32 bits. Signed-off-by: Kenneth Albanowski <kenalba@google.com> --- Documentation/hid/hiddev.rst | 6 ++- drivers/hid/hid-core.c | 80 ++++++++++++++++++++++++++---------- drivers/hid/hid-debug.c | 27 ++++++++---- drivers/hid/hid-input.c | 10 ++++- include/linux/hid-debug.h | 4 +- include/linux/hid.h | 2 +- 6 files changed, 92 insertions(+), 37 deletions(-)