blob: 09b600a69809c525aec9726963cb57745cad0c6d [file] [log] [blame]
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