All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kenneth Albanowski <kenalba@google.com>
To: "open list:HID CORE LAYER" <linux-input@vger.kernel.org>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Benjamin Tissoires <benjamin.tissoires@redhat.com>,
	Jiri Kosina <jikos@kernel.org>,
	Peter Hutterer <peter.hutterer@who-t.net>,
	Jason Gerecke <jason.gerecke@wacom.com>,
	Kenneth Albanowski <kenalba@google.com>
Subject: [PATCH 3/3] [hid] Emit digitizer serial number through power_supply
Date: Wed, 19 May 2021 17:22:49 -0700	[thread overview]
Message-ID: <20210519143836.3.I9d559632e582daaecdafd995ce7dfd9c89e64838@changeid> (raw)
In-Reply-To: <20210520002249.361821-1-kenalba@google.com>

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.

If the field is present and non-zero, the serial number
will be 'DG-ABCD' where 'ABCD' is up to sixteen hex
digits -- field lengths of up to 64-bits are supported,
the largest currently known about.

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 this field.

Signed-off-by: Kenneth Albanowski <kenalba@google.com>
---

 drivers/hid/hid-input.c | 100 +++++++++++++++++++++++++++++++++++++---
 include/linux/hid.h     |   5 ++
 2 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ee9e8d31a45ba..c5767ceb4a61c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -286,6 +286,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 +403,26 @@ 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",
+				 DIV_ROUND_UP(dev->battery_serial_number_bits, 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 +506,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 +549,8 @@ 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 +562,57 @@ 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,
+					   const __s32 *values, int bits)
+{
+	__u64 value;
+
+	if (!dev->battery)
+		return;
+
+	if (bits > 64)
+		bits = 64;
+
+	value = (__u64)(__u32)values[0];
+	if (bits > 32)
+		value |= (__u64)values[1] << 32;
+
+	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 +629,17 @@ 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,
+					   const __s32 *values, int bits)
+{
+}
+
+static void hidinput_flush_battery(struct hid_device *dev)
 {
 }
 #endif	/* CONFIG_HID_BATTERY_STRENGTH */
@@ -1273,7 +1353,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, const __s32 *values, unsigned value_count)
+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;
@@ -1290,9 +1372,13 @@ 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, values, field->report_size);
+		/* fall through to normal standard MSC_SERIAL processing */
+	}
 
 	if (!field->hidinput)
 		return;
@@ -1423,6 +1509,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 8494b1995b10b..d5585a99b5ad9 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -587,8 +587,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 bits 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
 
-- 
2.31.0


  parent reply	other threads:[~2021-05-20  0:23 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-20  0:22 [PATCH 0/3] 64-bit Digitizer Serial Numbers Kenneth Albanowski
2021-05-20  0:22 ` [PATCH 1/3] [hid] Minor cleanup Kenneth Albanowski
2021-05-20  0:22 ` [PATCH 2/3] [hid] Enable processing of fields larger than 32 bits Kenneth Albanowski
2021-05-20  0:22 ` Kenneth Albanowski [this message]
2021-06-09 20:52 ` [PATCH 0/3] 64-bit Digitizer Serial Numbers Kenneth Albanowski
2021-09-15 14:56 ` Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210519143836.3.I9d559632e582daaecdafd995ce7dfd9c89e64838@changeid \
    --to=kenalba@google.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jason.gerecke@wacom.com \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=peter.hutterer@who-t.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.