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@xxxxxxxxxx> --- 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