| From df4d26589a34af04d8d2075744c357208983d9de Mon Sep 17 00:00:00 2001 |
| From: Kenneth Albanowski <kenalba@google.com> |
| Date: Thu, 10 Jun 2021 15:46:17 -0700 |
| Subject: [PATCH] CHROMIUM: hid: Emit digitizer serial number through |
| power_supply |
| |
| (This is intended to be be upstreamed, it is pending USB |
| HID standardization on a new Usage in this patch stack.) |
| |
| HID devices that expose a battery strength can have |
| associated power_supply nodes. This fills in the |
| SERIAL_NUMBER power_supply field if the same HID device |
| also has a Digitizer.Transducer Serial Number* usage, |
| effectively allowing that particular stylus to be |
| identified. |
| |
| One or both of Usage(Digitizer.Transducer Serial |
| Number) and the proposed Usage(Digitizer.Transducer |
| Serial Number Second 32 Bits) will be utilized. |
| |
| If the field(s) are present and non-zero, the serial number |
| will be either 8 or 16 hex digits. |
| |
| Devices are expected to emit zero if the transducer |
| does not have a serial number, or the serial number |
| has not yet been acquired; zeros will be ignored. |
| |
| Note that logical min/max (and other HID item |
| parameters) will be ignored for these fields. |
| |
| BUG=b:184854727 |
| TEST=Checked operation + power_supply functionality on entire patch stack; coachz, dedede |
| |
| Signed-off-by: Kenneth Albanowski <kenalba@google.com> |
| Change-Id: Ib70ba97463c0a6c187c1dbcf9fee0781094b5b6a |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2954604 |
| Reviewed-by: Dmitry Torokhov <dtor@chromium.org> |
| (cherry picked from commit 07595a190782aa84fc9aa440308d5fe38ba6512c) |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2988724 |
| Commit-Queue: Dmitry Torokhov <dtor@chromium.org> |
| |
| [rebase61(tzungbi): |
| Squashed: |
| FIXUP: CHROMIUM: hid: Emit digitizer serial number through |
| ] |
| Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org> |
| --- |
| drivers/hid/hid-input.c | 127 ++++++++++++++++++++++++++++++++++++++-- |
| include/linux/hid.h | 6 ++ |
| 2 files changed, 128 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c |
| index f0c67baddc8d08562469aec5684b80b8ea51917e..b56a14a8192c2c056e01532366b04fd3548c97c2 100644 |
| --- a/drivers/hid/hid-input.c |
| +++ b/drivers/hid/hid-input.c |
| @@ -333,6 +333,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, |
| }; |
| @@ -476,6 +477,33 @@ 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 { |
| + if (!dev->battery_sn_64bit) { |
| + snprintf(dev->battery_serial_number_str, |
| + sizeof(dev->battery_serial_number_str), |
| + "%08llX", |
| + dev->battery_serial_number); |
| + } else { |
| + snprintf(dev->battery_serial_number_str, |
| + sizeof(dev->battery_serial_number_str), |
| + "%016llX", |
| + 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) { |
| @@ -559,6 +587,9 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, |
| dev->battery_report_type = report_type; |
| dev->battery_report_id = field->report->id; |
| dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; |
| + dev->battery_state_changed = false; |
| + dev->battery_reported = false; |
| + dev->battery_sn_64bit = false; |
| |
| /* |
| * Stylus is normally not connected to the device and thus we |
| @@ -603,7 +634,13 @@ 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_set_battery_sn_64bit(struct hid_device *dev) |
| +{ |
| + dev->battery_sn_64bit = true; |
| +} |
| + |
| +static void hidinput_update_battery_capacity(struct hid_device *dev, |
| + __s32 value) |
| { |
| int capacity; |
| |
| @@ -615,15 +652,70 @@ 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_state_changed = true; |
| + } |
| + dev->battery_reported = true; |
| +} |
| + |
| +static void hidinput_update_battery_serial(struct hid_device *dev, |
| + const __s32 value, bool top_32_bits) |
| +{ |
| + __u64 sn; |
| + __u32 sn_hi, sn_lo; |
| + |
| + if (!dev->battery) |
| + return; |
| + |
| + if (!top_32_bits) { |
| + sn_lo = (__u32)value; |
| + sn_hi = (__u32)(dev->battery_new_serial_number >> 32); |
| + } else { |
| + sn_lo = (__u32)dev->battery_new_serial_number; |
| + sn_hi = (__u32)value; |
| + } |
| + |
| + sn = (((__u64)sn_hi) << 32) | (__u64)sn_lo; |
| + |
| + dev->battery_new_serial_number = sn; |
| + 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; |
| + |
| + /* As we have the entire S/N now, check if it changed, and is non-zero. |
| + * We do want to ignore actual updates of zero, as they are expected to |
| + * convey 'no information', instead of 'no stylus present'. |
| + */ |
| + if (dev->battery_new_serial_number != 0 && |
| + dev->battery_new_serial_number != dev->battery_serial_number) { |
| + dev->battery_serial_number = dev->battery_new_serial_number; |
| + dev->battery_state_changed = true; |
| + } |
| + |
| if (dev->battery_status != HID_BATTERY_REPORTED || |
| - capacity != dev->battery_capacity || |
| + dev->battery_state_changed || |
| ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { |
| - dev->battery_capacity = capacity; |
| dev->battery_status = HID_BATTERY_REPORTED; |
| + dev->battery_state_changed = false; |
| dev->battery_ratelimit_time = |
| ktime_add_ms(ktime_get_coarse(), 30 * 1000); |
| power_supply_changed(dev->battery); |
| } |
| + |
| + /* Clean up for next report */ |
| + dev->battery_reported = false; |
| + dev->battery_new_serial_number = 0; |
| } |
| |
| static bool hidinput_set_battery_charge_status(struct hid_device *dev, |
| @@ -650,7 +742,21 @@ static void hidinput_cleanup_battery(struct hid_device *dev) |
| { |
| } |
| |
| -static void hidinput_update_battery(struct hid_device *dev, int value) |
| +static void hidinput_set_battery_sn_64bit(struct hid_device *dev) |
| +{ |
| +} |
| + |
| +static void hidinput_update_battery_capacity(struct hid_device *dev, |
| + __s32 value) |
| +{ |
| +} |
| + |
| +static void hidinput_update_battery_serial(struct hid_device *dev, |
| + const __s32 value, bool top_32_bits) |
| +{ |
| +} |
| + |
| +static void hidinput_flush_battery(struct hid_device *dev) |
| { |
| } |
| |
| @@ -1020,6 +1126,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel |
| case 0x5b: /* TransducerSerialNumber */ |
| case 0x6e: /* TransducerSerialNumber2 */ |
| map_msc(MSC_SERIAL); |
| + hidinput_set_battery_sn_64bit(device); |
| break; |
| |
| default: goto unknown; |
| @@ -1505,10 +1612,18 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct |
| bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value); |
| |
| if (!handled) |
| - hidinput_update_battery(hid, value); |
| + hidinput_update_battery_capacity(hid, value); |
| |
| return; |
| } |
| + if (usage->type == EV_MSC && usage->hid == (HID_UP_DIGITIZER | 0x006e)) { /* TransducerSerialNumberSecond32Bits */ |
| + hidinput_update_battery_serial(hid, value, true); |
| + return; |
| + } |
| + if (usage->type == EV_MSC && usage->code == MSC_SERIAL) { |
| + hidinput_update_battery_serial(hid, value, false); |
| + /* fall through to standard MSC_SERIAL processing */ |
| + } |
| |
| if (!field->hidinput) |
| return; |
| @@ -1704,6 +1819,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/include/linux/hid.h b/include/linux/hid.h |
| index 19b59455836d1343e6ac8470e66866017387e06c..9c5b00c76534056fd7d99fb94a42b26fe1c53867 100644 |
| --- a/include/linux/hid.h |
| +++ b/include/linux/hid.h |
| @@ -615,9 +615,15 @@ struct hid_device { /* device report descriptor */ |
| __s32 battery_max; |
| __s32 battery_report_type; |
| __s32 battery_report_id; |
| + __u64 battery_serial_number; |
| + __u64 battery_new_serial_number; /* gather entire updated 64-bit SN here for end of report */ |
| + char battery_serial_number_str[17]; /* Space for max 16 hex digits */ |
| __s32 battery_charge_status; |
| enum hid_battery_status battery_status; |
| bool battery_avoid_query; |
| + bool battery_state_changed; /* a battery field has been changed within the current report */ |
| + bool battery_reported; |
| + bool battery_sn_64bit; /* whether battery S/N is 32 or 64 bits long */ |
| ktime_t battery_ratelimit_time; |
| #endif |
| |
| -- |
| 2.38.3 |
| |