Re: Supporting 64-bit Digitizer Serial Numbers?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Kenneth,

On Wed, Mar 24, 2021 at 8:53 PM Kenneth Albanowski <kenalba@xxxxxxxxxx> wrote:
>
> Hi folks,
>
> I'm looking at being able to retrieve an accurate (not mangled or
> truncated) Digitizer.Transducer Serial Number from user-space, primarily
> through the power_supply node constructed for HID devices which expose
> Battery Strength.
>
> For the digitizers I'm looking at (USI) the Serial Number is a 64-bit
> report field, making it not fit directly into evdev, or the hid-input
> core.

Thanks for raising that issue. Before I jump into commenting on your
PoC, I'd like to ask you to provide the report descriptor of this
particular device (ideally with hid-recorder from the hid-tolls repo
below).
AFAICT, the HID spec we currently implement doesn't allow for data of
more than 32 bits (section 6.2.2.2 of
https://www.usb.org/sites/default/files/hid1_11.pdf).
The main problem is that a field is usually associated with a logical
max, and this part makes us limit the field up to 32bits.

There is a "long items" section right after (6.2.2.3) that allows for
longer data, and I don't think the current implementation supports it.

One other thing: this whole problem is very much a good candidate for
adding regression tests on
https://gitlab.freedesktop.org/libevdev/hid-tools.

So I'd be glad if we can work around the problem in the kernel *and*
add the matching test in hid-tools so we don't break this in the
future.

More comments inlined...

>
> I'm appending a proof-of-concept which implements this; I describe the
> details below. (This is based on 5.4, but it looks clean for 5.10 as
> well as relatively straightforward to backport.)
>
> I'd appreciate any feedback on this approach.
>
> My thought is to perform minimal modifications to allow hid-core to
> transfer reports up to 64-bits in size to hid-input, so that it can
> process this field and emit it to the power_supply integration, as well
> as through classic MSC_SERIAL (for the low 32 bits) and a new
> MSC_SERIAL2 (the upper bits) for applications which want it inline with
> stylus event data.

The wacom driver already has this problem and handles it in a creative
way (as usual ;-P)
If I am not mistaken, it splits the 64 bits serial on 2 events as
well: MSC_SERIAL and ABS_MISC (Jason might correct me here).

I am not saying this is what needs to be done, but just what some
userspace tools already expect.


>
> hid_field.value is expanded to an s64 array from s32, and there are
> a few knock-on effects to code that holds pointers to these fields.
> No other interfaces, in-kernel, or user-space, are modified to support
> HID reports/evdev events > 32 bits.

I also think both problems are orthogonal (which is why you haven't
tried to change the evdev protocol).

I don't think we can do much on the evdev side to support 64 bits or
bigger data.
Dmitry and Peter might find a good way to work around that, but I'm OK
with adding MSC_SERIAL_2 from my point of view.

>
> The Transducer Serial Number would show up as in the power_supply as a
> serial_number of "DG:0123456789ABCDEF" (or however many hex digits are
> warranted by the report size), and "DG:" being a fixed prefix -- we do
> know it is a digitizer.
>
> I realize this doesn't provide a scalable solution in the
> hid-core/hid-input code for (hypothetical) fields larger than 64 bits,
> although extending evdev with MSC_SERIAL3, 4, etc. would be trivial. (I
> don't have a good sense of how common these larger fields are, although
> obviously the code has survived until now with only 32-bit support, and
> one comment saying that anything larger than 16-bit is unusual.)

I would prefer having a generic solution for items longer than 32 bits
instead of having a special case for 64 bits only.

The spec allows for items longer than 32 bits (still section 6.2.2.3
of the HID spec) so we should probably handle that case in a generic
way to not have to deal with 128 bits next time a creative device
maker wants to use it.
The nice thing about the hid-tools test suite is that we can be
creative and produce this 128 bits new device, and have the tests for
it already :)

>
> (The other approach I was looking at was leaving hid_field.value
> unchanged, and instead just having hid-core recognize this particular
> 64 bit field, edit the hid_usage to turn it into a 2-count 32-bit field,
> apply a unique usage code to the second half, and then have hid-input
> recognize these usages and paste everything back together. This is
> scalable to larger fields, but is putting an unprecedented amount of
> magic in hid-core.)

Not really. There is already a lot of magic in hid-core, and handling
longer-than-32bits doesn't seem that far away. The report descriptor
should give us all the information we need, so we can surely have a
special case for long items and data. Also hid-input doesn't have to
be tied to evdev. So nothing prevents us to mark the field as being a
long one, and change the internal representation of the various 32
bits chunks of its data.

>
> The other change is MSC_SERIAL2 would be added: in the specific case of
> Usage(Digitizer.Transducer Serial Number) being present with
> report_size > 32 then MSC_SERIAL is reliably the full lowest 32 bits,
> and MSC_SERIAL2 is the next set of 32 bits; no clamping or scaling
> applied, regardless of what the descriptor says. Both MSC_SERIAL* are
> emitted for every hid record, there is no caching. If report_size <=
> 32, then there is no difference to existing behaviour, only MSC_SERIAL
> is emitted. I'm assuming the cost of one additional event per sync, for
> stylus data, is negligible.

I am not in the best position to comment, but as mentioned previously,
it works for me.

>
> This does potentially alter the generation of MSC_SERIAL on existing
> devices, but only ones where the report_size > 32 bits and the min/max
> is not 0/~0 -- user space would see unclamped values and conceivably
> could be surprised with a larger value in MSC_SERIAL than previously
> seen.

Honestly it shouldn't introduce any regressions. I still have to work
on the tablet side of the regression tests, but so far only Wacom was
reliably sending serial numbers (and bigger than 32 bits). So I am not
worried from this side of things.

>
> If changing MSC_SERIAL is deemed too risky for compatibility, then it's
> easy to just leave MSC_SERIAL as-is, and put in a separate MSC_SERIAL1
> (MSC_SERIAL0?) that has the pristine low bits -- just at a cost of yet
> one more MSC_ field per report, and using up the last MSC_ bit before
> we need to bump MSC_MAX.
>
> This approach doesn't affect Wacom evdev events at all, all of that
> logic is separate from hid-input.

There is a whole ecosystem for wacom, but we are pretty much in
control of all the pieces. So I would prefer involving wacom from the
beginning, and have a common representation to the user space.

Besides that, I skimmed through the  patch below. I do have comments,
but in addition to the above, they are mostly "hey this hunk should be
in its own separate patch". But I guess the final submission will have
this separation, so there is no real point in going too deep in the
details IMO.

Cheers,
Benjamin

>
> Kind regards and good health,
> - Kenneth Albanowski
>
> From 9948f77d758c5688d5ac5dee8f7218aa36cbd20d Mon Sep 17 00:00:00 2001
> From: Kenneth Albanowski <kenalba@xxxxxxxxxx>
> Date: Mon, 22 Mar 2021 11:40:13 -0700
> Subject: [PATCH] input: limited 64-bit usages; MSC_SERIAL2,
>  POWER_SUPPLY_SERIAL_NUMBER
>
> Extend hid-core to process 64-bit usages, updating hid-input
> to recognize digitizer transducer serial numbers up to 64 bits,
> emitting MSC_SERIAL and MSC_SERIAL2 (new) for second 32-bit word.
>
> 64-bit usages are only fed to hid-input, all other existing
> frameworks and interfaces only see the traditional 32-bit
> truncated values; the majority of hid-input still only uses
> traditional 32-bit clamped values.
>
> Behaviour is changed only for HID reports that have
> Usage(Digitizer.TransducerSerialNumber) where size > 32 bits;
> logical min/max are no longer used to clamp the value, so
> MSC_SERIAL may be different from prior logic and now emits
> entire lowest 32-bits, instead of clamped value. Upper bits
> (32-64) are emitted to MSC_SERIAL2, also unclamped.
>
> power_supply integration extended to emit
> Digitizer.TransducerSerialNumber as POWER_SUPPLY_SERIAL_NUMBER
> in format: "DG:0123456789ABCDEF", with appropriate number of
> digits for actual field length.
>
> Change-Id: Ic8f9c90081f324ff76f34b0906bca1fcd5eac06a
> ---
>  Documentation/hid/hiddev.rst           |   6 +-
>  drivers/hid/hid-core.c                 |  80 +++++++++++---------
>  drivers/hid/hid-debug.c                |   4 +-
>  drivers/hid/hid-input.c                | 101 +++++++++++++++++++++++--
>  drivers/hid/hid-multitouch.c           |  22 +++---
>  drivers/hid/hid-sony.c                 |   2 +-
>  drivers/hid/usbhid/hid-pidff.c         |   2 +-
>  include/linux/hid-debug.h              |   2 +-
>  include/linux/hid.h                    |  13 +++-
>  include/uapi/linux/input-event-codes.h |   3 +-
>  10 files changed, 173 insertions(+), 62 deletions(-)
>
> diff --git a/Documentation/hid/hiddev.rst b/Documentation/hid/hiddev.rst
> index 209e6ba4e019e..06eee00847e50 100644
> --- a/Documentation/hid/hiddev.rst
> +++ b/Documentation/hid/hiddev.rst
> @@ -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
> +is a single value, usually a signed 32 bit value, which is what
> +the hiddev API supports. (hid-core can process 64 bit values, but
> +these are not currently exposed through hiddev.)
>
>  read():
>  -------
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 1489f49dc53d0..5f4c7f06d0e96 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -101,14 +101,14 @@ static struct hid_field
> *hid_register_field(struct hid_report *report, unsigned
>
>   field = kzalloc((sizeof(struct hid_field) +
>   usages * sizeof(struct hid_usage) +
> - usages * sizeof(unsigned)), GFP_KERNEL);
> + usages * sizeof(s64)), GFP_KERNEL);
>   if (!field)
>   return NULL;
>
>   field->index = report->maxfield++;
>   report->field[field->index] = field;
>   field->usage = (struct hid_usage *)(field + 1);
> - field->value = (s32 *)(field->usage + usages);
> + field->value = (s64 *)(field->usage + usages);
>   field->report = report;
>
>   return field;
> @@ -337,7 +337,8 @@ static int hid_add_field(struct hid_parser
> *parser, unsigned report_type, unsign
>  }
>
>  /*
> - * Read data value from item.
> + * Read data value from global items, which are
> + * a maximum of 32 bits in size.
>   */
>
>  static u32 item_udata(struct hid_item *item)
> @@ -763,7 +764,7 @@ static u8 *fetch_item(__u8 *start, __u8 *end,
> struct hid_item *item)
>   return start;
>
>   case 3:
> - item->size++;
> + item->size = 4;
>   if ((end - start) < 4)
>   return NULL;
>   item->data.u32 = get_unaligned_le32(start);
> @@ -1016,7 +1017,7 @@ static int hid_calculate_multiplier(struct
> hid_device *hid,
>       struct hid_field *multiplier)
>  {
>   int m;
> - __s32 v = *multiplier->value;
> + __s64 v = *multiplier->value;
>   __s32 lmin = multiplier->logical_minimum;
>   __s32 lmax = multiplier->logical_maximum;
>   __s32 pmin = multiplier->physical_minimum;
> @@ -1299,27 +1300,17 @@ int hid_open_report(struct hid_device *device)
>  }
>  EXPORT_SYMBOL_GPL(hid_open_report);
>
> -/*
> - * Convert a signed n-bit integer to signed 32-bit integer. Common
> - * cases are done through the compiler, the screwed things has to be
> - * done by hand.
> - */
> -
> -static s32 snto32(__u32 value, unsigned n)
> +s32 hid_snto32(__u32 value, unsigned n)
>  {
> - switch (n) {
> - case 8:  return ((__s8)value);
> - case 16: return ((__s16)value);
> - case 32: return ((__s32)value);
> - }
> - return value & (1 << (n - 1)) ? value | (~0U << n) : value;
> + return sign_extend32(value, n);
>  }
> +EXPORT_SYMBOL_GPL(hid_snto32);
>
> -s32 hid_snto32(__u32 value, unsigned n)
> +s64 hid_snto64(__u64 value, unsigned n)
>  {
> - return snto32(value, n);
> + return sign_extend64(value, n);
>  }
> -EXPORT_SYMBOL_GPL(hid_snto32);
> +EXPORT_SYMBOL_GPL(hid_snto64);
>
>  /*
>   * Convert a signed 32-bit integer to a signed n-bit integer.
> @@ -1342,20 +1333,21 @@ 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.
>   * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
>   */
>
> -static u32 __extract(u8 *report, unsigned offset, int n)
> +static u64 __extract(u8 *report, unsigned offset, int n)
>  {
>   unsigned int idx = offset / 8;
>   unsigned int bit_nr = 0;
>   unsigned int bit_shift = offset % 8;
>   int bits_to_copy = 8 - bit_shift;
> - u32 value = 0;
> - u32 mask = n < 32 ? (1U << n) - 1 : ~0U;
> + u64 value = 0;
> + u64 mask = n < 64 ? (1ULL << n) - 1 : ~0ULL;
>
>   while (n > 0) {
> - value |= ((u32)report[idx] >> bit_shift) << bit_nr;
> + value |= ((u64)report[idx] >> bit_shift) << bit_nr;
>   n -= bits_to_copy;
>   bit_nr += bits_to_copy;
>   bits_to_copy = 8;
> @@ -1366,6 +1358,10 @@ static u32 __extract(u8 *report, unsigned offset, int n)
>   return value & mask;
>  }
>
> +/*
> + * Classic HID code assumes 32-bit truncation for fields. Provided
> + * for compatibility and consistency of warnings.
> + */
>  u32 hid_field_extract(const struct hid_device *hid, u8 *report,
>   unsigned offset, unsigned n)
>  {
> @@ -1379,6 +1375,22 @@ u32 hid_field_extract(const struct hid_device
> *hid, u8 *report,
>  }
>  EXPORT_SYMBOL_GPL(hid_field_extract);
>
> +/*
> + * Extract larger, 64-bit fields.
> + */
> +u64 hid_field_extract64(const struct hid_device *hid, u8 *report,
> + unsigned offset, unsigned n)
> +{
> + if (n > 64) {
> + hid_warn_once(hid, "%s() called with n (%d) > 64! (%s)\n",
> +      __func__, n, current->comm);
> + n = 64;
> + }
> +
> + return __extract(report, offset, n);
> +}
> +EXPORT_SYMBOL_GPL(hid_field_extract64);
> +
>  /*
>   * "implement" : set bits in a little endian bit stream.
>   * Same concepts as "extract" (see comments above).
> @@ -1438,7 +1450,7 @@ static void implement(const struct hid_device
> *hid, u8 *report,
>   * Search an array for a value.
>   */
>
> -static int search(__s32 *array, __s32 value, unsigned n)
> +static int search(__s64 *array, __s64 value, unsigned n)
>  {
>   while (n--) {
>   if (*array++ == value)
> @@ -1497,7 +1509,7 @@ 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, __s64 value, int interrupt)
>  {
>   struct hid_driver *hdrv = hid->driver;
>   int ret;
> @@ -1506,7 +1518,7 @@ static void hid_process_event(struct hid_device
> *hid, struct hid_field *field,
>   hid_dump_input(hid, usage, value);
>
>   if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
> - ret = hdrv->event(hid, field, usage, value);
> + ret = hdrv->event(hid, field, usage, (__s32)value);
>   if (ret != 0) {
>   if (ret < 0)
>   hid_err(hid, "%s's event failed with %d\n",
> @@ -1536,18 +1548,18 @@ static void hid_input_field(struct hid_device
> *hid, struct hid_field *field,
>   unsigned size = field->report_size;
>   __s32 min = field->logical_minimum;
>   __s32 max = field->logical_maximum;
> - __s32 *value;
> + __s64 *value;
>
> - value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
> + value = kmalloc_array(count, sizeof(__s64), GFP_ATOMIC);
>   if (!value)
>   return;
>
>   for (n = 0; n < count; n++) {
>
>   value[n] = min < 0 ?
> - snto32(hid_field_extract(hid, data, offset + n * size,
> + sign_extend64(hid_field_extract64(hid, data, offset + n * size,
>         size), size) :
> - hid_field_extract(hid, data, offset + n * size, size);
> + hid_field_extract64(hid, data, offset + n * size, size);
>
>   /* Ignore report if ErrorRollOver */
>   if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
> @@ -1577,7 +1589,7 @@ static void hid_input_field(struct hid_device
> *hid, struct hid_field *field,
>   hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
>   }
>
> - memcpy(field->value, value, count * sizeof(__s32));
> + memcpy(field->value, value, count * sizeof(__s64));
>  exit:
>   kfree(value);
>  }
> @@ -1672,7 +1684,7 @@ int hid_set_field(struct hid_field *field,
> unsigned offset, __s32 value)
>   return -1;
>   }
>   if (field->logical_minimum < 0) {
> - if (value != snto32(s32ton(value, size), size)) {
> + if (value != hid_snto32(s32ton(value, size), size)) {
>   hid_err(field->report->device, "value %d is out of range\n", value);
>   return -1;
>   }
> diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
> index 9453147d020db..badd51a8ea4ba 100644
> --- a/drivers/hid/hid-debug.c
> +++ b/drivers/hid/hid-debug.c
> @@ -692,7 +692,7 @@ 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,
> __s64 value)
>  {
>   char *buf;
>   int len;
> @@ -701,7 +701,7 @@ void hid_dump_input(struct hid_device *hdev,
> struct hid_usage *usage, __s32 valu
>   if (!buf)
>   return;
>   len = strlen(buf);
> - snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value);
> + snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %lld\n", value);
>
>   hid_debug_event(hdev, buf);
>
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index bceccd75b488e..87ade0d6963cf 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -52,6 +52,7 @@ static const struct {
>  #define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
>  #define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
>  #define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
> +#define map_msc(c) hid_map_usage(hidinput, usage, &bit, &max, EV_MSC, (c))
>
>  #define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
>   &max, EV_ABS, (c))
> @@ -286,6 +287,7 @@ static enum power_supply_property
> hidinput_battery_props[] = {
>   POWER_SUPPLY_PROP_ONLINE,
>   POWER_SUPPLY_PROP_CAPACITY,
>   POWER_SUPPLY_PROP_MODEL_NAME,
> + POWER_SUPPLY_PROP_SERIAL_NUMBER,
>   POWER_SUPPLY_PROP_STATUS,
>   POWER_SUPPLY_PROP_SCOPE,
>  };
> @@ -402,6 +404,22 @@ static int hidinput_get_battery_property(struct
> power_supply *psy,
>   val->strval = dev->name;
>   break;
>
> + case POWER_SUPPLY_PROP_SERIAL_NUMBER:
> + /* Serial number does not have an active HID query
> + * mechanism like hidinput_query_battery_capacity, as the
> + * only devices expected to have serial numbers are digitizers,
> + * which are unlikely to be able to pull the serial number from
> + * an untethered pen on demand.
> + */
> + if (dev->battery_serial_number == 0) {
> + /* Make no claims about S/N format if we haven't actually seen a value yet. */
> + strcpy(dev->battery_serial_number_str, "");
> + } else {
> + snprintf(dev->battery_serial_number_str,
> sizeof(dev->battery_serial_number_str), "DG:%0*llX",
> (dev->battery_serial_number_bits+3)/4, dev->battery_serial_number);
> + }
> + val->strval = dev->battery_serial_number_str;
> + break;
> +
>   case POWER_SUPPLY_PROP_STATUS:
>   if (dev->battery_status != HID_BATTERY_REPORTED &&
>      !dev->battery_avoid_query) {
> @@ -485,6 +503,8 @@ static int hidinput_setup_battery(struct
> hid_device *dev, unsigned report_type,
>   dev->battery_max = max;
>   dev->battery_report_type = report_type;
>   dev->battery_report_id = field->report->id;
> + dev->battery_changed = false;
> + dev->battery_reported = false;
>
>   /*
>   * Stylus is normally not connected to the device and thus we
> @@ -526,7 +546,7 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
>   dev->battery = NULL;
>  }
>
> -static void hidinput_update_battery(struct hid_device *dev, int value)
> +static void hidinput_update_battery_capacity(struct hid_device *dev,
> __s32 value)
>  {
>   int capacity;
>
> @@ -538,11 +558,47 @@ static void hidinput_update_battery(struct
> hid_device *dev, int value)
>
>   capacity = hidinput_scale_battery_capacity(dev, value);
>
> + if (capacity != dev->battery_capacity) {
> + dev->battery_capacity = capacity;
> + dev->battery_changed = true;
> + }
> + dev->battery_reported = true;
> +}
> +
> +static void hidinput_update_battery_serial(struct hid_device *dev,
> __u64 value, int bits)
> +{
> + if (!dev->battery)
> + return;
> +
> + if (value == 0)
> + return;
> +
> + if (value != dev->battery_serial_number) {
> + dev->battery_serial_number = value;
> + dev->battery_serial_number_bits = bits;
> + dev->battery_changed = true;
> + }
> + dev->battery_reported = true;
> +}
> +
> +static void hidinput_flush_battery(struct hid_device *dev)
> +{
> + if (!dev->battery)
> + return;
> +
> + /* Only consider pushing a battery change if there is a
> + * battery field in this report.
> + */
> + if (!dev->battery_reported)
> + return;
> +
> + dev->battery_reported = false;
> +
>   if (dev->battery_status != HID_BATTERY_REPORTED ||
> -    capacity != dev->battery_capacity ||
> + dev->battery_changed ||
>      ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
> - dev->battery_capacity = capacity;
>   dev->battery_status = HID_BATTERY_REPORTED;
> + dev->battery_changed = false;
>   dev->battery_ratelimit_time =
>   ktime_add_ms(ktime_get_coarse(), 30 * 1000);
>   power_supply_changed(dev->battery);
> @@ -559,7 +615,15 @@ static void hidinput_cleanup_battery(struct
> hid_device *dev)
>  {
>  }
>
> -static void hidinput_update_battery(struct hid_device *dev, int value)
> +static void hidinput_update_battery_capacity(struct hid_device *dev,
> __s32 value)
> +{
> +}
> +
> +static void hidinput_update_battery_serial(struct hid_device *dev,
> __u64 value, int bits)
> +{
> +}
> +
> +static void hidinput_flush_battery(struct hid_device *dev)
>  {
>  }
>  #endif /* CONFIG_HID_BATTERY_STRENGTH */
> @@ -850,6 +914,13 @@ static void hidinput_configure_usage(struct
> hid_input *hidinput, struct hid_fiel
>   usage->code = MSC_SERIAL;
>   bit = input->mscbit;
>   max = MSC_MAX;
> + if (field->report_size > 32) {
> + /* Deliver up to 64 bits of TransducerSerialNumber via
> + * MSC_SERIAL and MSC_SERIAL2.
> + */
> + set_bit(MSC_SERIAL2, input->mscbit);
> + set_bit(EV_MSC, input->evbit);
> + }
>   break;
>
>   default:  goto unknown;
> @@ -1243,7 +1314,7 @@ static void hidinput_configure_usage(struct
> hid_input *hidinput, struct hid_fiel
>
>  static void hidinput_handle_scroll(struct hid_usage *usage,
>     struct input_dev *input,
> -   __s32 value)
> +   __s64 value)
>  {
>   int code;
>   int hi_res, lo_res;
> @@ -1273,8 +1344,9 @@ 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, __s64 value64)
>  {
> + __s32 value = value64;
>   struct input_dev *input;
>   unsigned *quirks = &hid->quirks;
>
> @@ -1282,15 +1354,28 @@ void hidinput_hid_event(struct hid_device
> *hid, struct hid_field *field, struct
>   return;
>
>   if (usage->type == EV_PWR) {
> - hidinput_update_battery(hid, value);
> + hidinput_update_battery_capacity(hid, value);
>   return;
>   }
> + if (usage->type == EV_MSC && usage->code == MSC_SERIAL) {
> + hidinput_update_battery_serial(hid, value64, field->report_size);
> + }
>
>   if (!field->hidinput)
>   return;
>
>   input = field->hidinput->input;
>
> + if (usage->type == EV_MSC && usage->code == MSC_SERIAL &&
> field->report_size > 32) {
> + /* min/max are limited to 32-bits by spec and cannot be relevant if field is
> + * this large, ignore them and send 64 bits through. We leave fields <= 32 bits
> + * to be processed later with normal clamping, for compatibility.
> + */
> + input_event(input, usage->type, MSC_SERIAL, (__u32)value64);
> + input_event(input, usage->type, MSC_SERIAL2, value64 >> 32);
> + return;
> + }
> +
>   if (usage->hat_min < usage->hat_max || usage->hat_dir) {
>   int hat_dir = usage->hat_dir;
>   if (!hat_dir)
> @@ -1415,6 +1500,8 @@ void hidinput_report_event(struct hid_device
> *hid, struct hid_report *report)
>  {
>   struct hid_input *hidinput;
>
> + hidinput_flush_battery(hid);
> +
>   if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
>   return;
>
> diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
> index d91e6679afb18..23868a2e31187 100644
> --- a/drivers/hid/hid-multitouch.c
> +++ b/drivers/hid/hid-multitouch.c
> @@ -95,11 +95,11 @@ static const __s32 mzero; /* default for 0 */
>
>  struct mt_usages {
>   struct list_head list;
> - __s32 *x, *y, *cx, *cy, *p, *w, *h, *a;
> - __s32 *contactid; /* the device ContactID assigned to this slot */
> - bool *tip_state; /* is the touch valid? */
> - bool *inrange_state; /* is the finger in proximity of the sensor? */
> - bool *confidence_state; /* is the touch made by a finger? */
> + __s64 *x, *y, *cx, *cy, *p, *w, *h, *a;
> + __s64 *contactid; /* the device ContactID assigned to this slot */
> + __s64 *tip_state; /* is the touch valid? */
> + __s64 *inrange_state; /* is the finger in proximity of the sensor? */
> + __s64 *confidence_state; /* is the touch made by a finger? */
>  };
>
>  struct mt_application {
> @@ -110,10 +110,10 @@ struct mt_application {
>
>   __s32 quirks;
>
> - __s32 *scantime; /* scantime reported */
> + __s64 *scantime; /* scantime reported */
>   __s32 scantime_logical_max; /* max value for raw scantime */
>
> - __s32 *raw_cc; /* contact count in the report */
> + __s64 *raw_cc; /* contact count in the report */
>   int left_button_state; /* left button state */
>   unsigned int mt_flags; /* flags to pass to input-mt */
>
> @@ -642,11 +642,11 @@ static struct mt_report_data
> *mt_find_report_data(struct mt_device *td,
>
>  static void mt_store_field(struct hid_device *hdev,
>     struct mt_application *application,
> -   __s32 *value,
> +   __s64 *value,
>     size_t offset)
>  {
>   struct mt_usages *usage;
> - __s32 **target;
> + __s64 **target;
>
>   if (list_empty(&application->mt_usages))
>   usage = mt_allocate_usage(hdev, application);
> @@ -658,7 +658,7 @@ static void mt_store_field(struct hid_device *hdev,
>   if (!usage)
>   return;
>
> - target = (__s32 **)((char *)usage + offset);
> + target = (__s64 **)((char *)usage + offset);
>
>   /* the value has already been filled, create a new slot */
>   if (*target != DEFAULT_TRUE &&
> @@ -675,7 +675,7 @@ static void mt_store_field(struct hid_device *hdev,
>   if (!usage)
>   return;
>
> - target = (__s32 **)((char *)usage + offset);
> + target = (__s64 **)((char *)usage + offset);
>   }
>
>   *target = value;
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index 2f073f5360706..6501f33dbc725 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -1827,7 +1827,7 @@ static void buzz_set_leds(struct sony_sc *sc)
>   &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
>   struct hid_report *report = list_entry(report_list->next,
>   struct hid_report, list);
> - s32 *value = report->field[0]->value;
> + s64 *value = report->field[0]->value;
>
>   BUILD_BUG_ON(MAX_LEDS < 4);
>
> diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
> index fddac7c72f645..a8fb81cccb981 100644
> --- a/drivers/hid/usbhid/hid-pidff.c
> +++ b/drivers/hid/usbhid/hid-pidff.c
> @@ -138,7 +138,7 @@ static const u8 pidff_effect_operation_status[] =
> { 0x79, 0x7b };
>
>  struct pidff_usage {
>   struct hid_field *field;
> - s32 *value;
> + s64 *value;
>  };
>
>  struct pidff_device {
> diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
> index ea7b23d13bfdf..280de6b7904e7 100644
> --- a/include/linux/hid-debug.h
> +++ b/include/linux/hid-debug.h
> @@ -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 *, __s64);
>  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 *);
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 58e635bf4ed09..f0520417a91d5 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -389,6 +389,7 @@ struct hid_item {
>
>  struct hid_global {
>   unsigned usage_page;
> + /* HID Global fields are constrained by spec to 32-bits */
>   __s32    logical_minimum;
>   __s32    logical_maximum;
>   __s32    physical_minimum;
> @@ -458,7 +459,7 @@ struct hid_field {
>   unsigned  report_size; /* size of this field in the report */
>   unsigned  report_count; /* number of this field in the report */
>   unsigned  report_type; /* (input,output,feature) */
> - __s32    *value; /* last known value(s) */
> + __s64    *value; /* last known value(s); 64-bit values are the
> largest we process */
>   __s32     logical_minimum;
>   __s32     logical_maximum;
>   __s32     physical_minimum;
> @@ -584,8 +585,13 @@ struct hid_device { /* device report descriptor */
>   __s32 battery_max;
>   __s32 battery_report_type;
>   __s32 battery_report_id;
> + __u64 battery_serial_number;
> + int battery_serial_number_bits; /* Actual number of digits in SN */
> + char battery_serial_number_str[20]; /* Space for "DG:" + max 16 hex digits */
>   enum hid_battery_status battery_status;
>   bool battery_avoid_query;
> + bool battery_changed;
> + bool battery_reported;
>   ktime_t battery_ratelimit_time;
>  #endif
>
> @@ -875,7 +881,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 *, __s64);
>  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 *);
> @@ -913,8 +919,11 @@ const struct hid_device_id
> *hid_match_device(struct hid_device *hdev,
>  bool hid_compare_device_paths(struct hid_device *hdev_a,
>        struct hid_device *hdev_b, char separator);
>  s32 hid_snto32(__u32 value, unsigned n);
> +s64 hid_snto64(__u64 value, unsigned n);
>  __u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
>       unsigned offset, unsigned n);
> +__u64 hid_field_extract64(const struct hid_device *hid, __u8 *report,
> +     unsigned offset, unsigned n);
>
>  /**
>   * hid_device_io_start - enable HID input during probe, remove
> diff --git a/include/uapi/linux/input-event-codes.h
> b/include/uapi/linux/input-event-codes.h
> index 472cd5bc55676..612ca91a87326 100644
> --- a/include/uapi/linux/input-event-codes.h
> +++ b/include/uapi/linux/input-event-codes.h
> @@ -816,12 +816,13 @@
>   * Misc events
>   */
>
> -#define MSC_SERIAL 0x00
> +#define MSC_SERIAL 0x00 /* First 32-bits of serial number */
>  #define MSC_PULSELED 0x01
>  #define MSC_GESTURE 0x02
>  #define MSC_RAW 0x03
>  #define MSC_SCAN 0x04
>  #define MSC_TIMESTAMP 0x05
> +#define MSC_SERIAL2 0x06 /* Second 32-bits of serial number */
>  #define MSC_MAX 0x07
>  #define MSC_CNT (MSC_MAX+1)
>
> --
> 2.29.2
>




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux