Skip to content

Commit 0549fba

Browse files
author
Jiri Kosina
committed
Merge branch 'for-6.4/logitech-hidpp' into for-linus
- support for ADC measurement (Bastien Nocera) - support for Logitech G935 (Bastien Nocera)
2 parents d411b5a + 539adfe commit 0549fba

6 files changed

Lines changed: 373 additions & 7 deletions

File tree

Documentation/ABI/testing/sysfs-bus-usb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ Description:
166166
The file will be present for all speeds of USB devices, and will
167167
always read "no" for USB 1.1 and USB 2.0 devices.
168168

169+
What: /sys/bus/usb/devices/<INTERFACE>/wireless_status
170+
Date: February 2023
171+
Contact: Bastien Nocera <hadess@hadess.net>
172+
Description:
173+
Some USB devices use a USB receiver dongle to communicate
174+
wirelessly with their device using proprietary protocols. This
175+
attribute allows user-space to know whether the device is
176+
connected to its receiver dongle, and, for example, consider
177+
the device to be absent when choosing whether to show the
178+
device's battery, show a headset in a list of outputs, or show
179+
an on-screen keyboard if the only wireless keyboard is
180+
turned off.
181+
This attribute is not to be used to replace protocol specific
182+
statuses available in WWAN, WLAN/Wi-Fi, Bluetooth, etc.
183+
If the device does not use a receiver dongle with a wireless
184+
device, then this attribute will not exist.
185+
169186
What: /sys/bus/usb/devices/.../<hub_interface>/port<X>
170187
Date: August 2012
171188
Contact: Lan Tianyu <tianyu.lan@intel.com>

drivers/hid/hid-logitech-hidpp.c

Lines changed: 249 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
7474
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
7575
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
7676
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29)
77+
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(30)
7778

7879
/* These are just aliases for now */
7980
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -94,6 +95,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
9495
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL BIT(7)
9596
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL BIT(8)
9697
#define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL BIT(9)
98+
#define HIDPP_CAPABILITY_ADC_MEASUREMENT BIT(10)
9799

98100
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
99101

@@ -145,6 +147,7 @@ struct hidpp_battery {
145147
u8 feature_index;
146148
u8 solar_feature_index;
147149
u8 voltage_feature_index;
150+
u8 adc_measurement_feature_index;
148151
struct power_supply_desc desc;
149152
struct power_supply *ps;
150153
char name[64];
@@ -471,6 +474,26 @@ static void hidpp_prefix_name(char **name, int name_length)
471474
*name = new_name;
472475
}
473476

477+
/*
478+
* Updates the USB wireless_status based on whether the headset
479+
* is turned on and reachable.
480+
*/
481+
static void hidpp_update_usb_wireless_status(struct hidpp_device *hidpp)
482+
{
483+
struct hid_device *hdev = hidpp->hid_dev;
484+
struct usb_interface *intf;
485+
486+
if (!(hidpp->quirks & HIDPP_QUIRK_WIRELESS_STATUS))
487+
return;
488+
if (!hid_is_usb(hdev))
489+
return;
490+
491+
intf = to_usb_interface(hdev->dev.parent);
492+
usb_set_wireless_status(intf, hidpp->battery.online ?
493+
USB_WIRELESS_STATUS_CONNECTED :
494+
USB_WIRELESS_STATUS_DISCONNECTED);
495+
}
496+
474497
/**
475498
* hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
476499
* events given a high-resolution wheel
@@ -853,8 +876,7 @@ static int hidpp_unifying_init(struct hidpp_device *hidpp)
853876
if (ret)
854877
return ret;
855878

856-
snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD",
857-
hdev->product, &serial);
879+
snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial);
858880
dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
859881

860882
name = hidpp_unifying_get_name(hidpp);
@@ -947,6 +969,54 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
947969
return 0;
948970
}
949971

972+
/* -------------------------------------------------------------------------- */
973+
/* 0x0003: Device Information */
974+
/* -------------------------------------------------------------------------- */
975+
976+
#define HIDPP_PAGE_DEVICE_INFORMATION 0x0003
977+
978+
#define CMD_GET_DEVICE_INFO 0x00
979+
980+
static int hidpp_get_serial(struct hidpp_device *hidpp, u32 *serial)
981+
{
982+
struct hidpp_report response;
983+
u8 feature_type;
984+
u8 feature_index;
985+
int ret;
986+
987+
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_DEVICE_INFORMATION,
988+
&feature_index,
989+
&feature_type);
990+
if (ret)
991+
return ret;
992+
993+
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
994+
CMD_GET_DEVICE_INFO,
995+
NULL, 0, &response);
996+
if (ret)
997+
return ret;
998+
999+
/* See hidpp_unifying_get_serial() */
1000+
*serial = *((u32 *)&response.rap.params[1]);
1001+
return 0;
1002+
}
1003+
1004+
static int hidpp_serial_init(struct hidpp_device *hidpp)
1005+
{
1006+
struct hid_device *hdev = hidpp->hid_dev;
1007+
u32 serial;
1008+
int ret;
1009+
1010+
ret = hidpp_get_serial(hidpp, &serial);
1011+
if (ret)
1012+
return ret;
1013+
1014+
snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial);
1015+
dbg_hid("HID++ DeviceInformation: Got serial: %s\n", hdev->uniq);
1016+
1017+
return 0;
1018+
}
1019+
9501020
/* -------------------------------------------------------------------------- */
9511021
/* 0x0005: GetDeviceNameType */
9521022
/* -------------------------------------------------------------------------- */
@@ -1357,7 +1427,7 @@ static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
13571427
* there are a few devices that use different battery technology.
13581428
*/
13591429

1360-
static const int voltages[] = {
1430+
static const int voltages[100] = {
13611431
4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075,
13621432
4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997,
13631433
3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929,
@@ -1372,8 +1442,6 @@ static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
13721442

13731443
int i;
13741444

1375-
BUILD_BUG_ON(ARRAY_SIZE(voltages) != 100);
1376-
13771445
if (unlikely(voltage < 3500 || voltage >= 5000))
13781446
hid_warn_once(hid_dev,
13791447
"%s: possibly using the wrong voltage curve\n",
@@ -1745,6 +1813,164 @@ static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
17451813
return ret;
17461814
}
17471815

1816+
/* -------------------------------------------------------------------------- */
1817+
/* 0x1f20: ADC measurement */
1818+
/* -------------------------------------------------------------------------- */
1819+
1820+
#define HIDPP_PAGE_ADC_MEASUREMENT 0x1f20
1821+
1822+
#define CMD_ADC_MEASUREMENT_GET_ADC_MEASUREMENT 0x00
1823+
1824+
#define EVENT_ADC_MEASUREMENT_STATUS_BROADCAST 0x00
1825+
1826+
static int hidpp20_map_adc_measurement_1f20_capacity(struct hid_device *hid_dev, int voltage)
1827+
{
1828+
/* NB: This voltage curve doesn't necessarily map perfectly to all
1829+
* devices that implement the ADC_MEASUREMENT feature. This is because
1830+
* there are a few devices that use different battery technology.
1831+
*
1832+
* Adapted from:
1833+
* https://github.com/Sapd/HeadsetControl/blob/acd972be0468e039b93aae81221f20a54d2d60f7/src/devices/logitech_g633_g933_935.c#L44-L52
1834+
*/
1835+
static const int voltages[100] = {
1836+
4030, 4024, 4018, 4011, 4003, 3994, 3985, 3975, 3963, 3951,
1837+
3937, 3922, 3907, 3893, 3880, 3868, 3857, 3846, 3837, 3828,
1838+
3820, 3812, 3805, 3798, 3791, 3785, 3779, 3773, 3768, 3762,
1839+
3757, 3752, 3747, 3742, 3738, 3733, 3729, 3724, 3720, 3716,
1840+
3712, 3708, 3704, 3700, 3696, 3692, 3688, 3685, 3681, 3677,
1841+
3674, 3670, 3667, 3663, 3660, 3657, 3653, 3650, 3646, 3643,
1842+
3640, 3637, 3633, 3630, 3627, 3624, 3620, 3617, 3614, 3611,
1843+
3608, 3604, 3601, 3598, 3595, 3592, 3589, 3585, 3582, 3579,
1844+
3576, 3573, 3569, 3566, 3563, 3560, 3556, 3553, 3550, 3546,
1845+
3543, 3539, 3536, 3532, 3529, 3525, 3499, 3466, 3433, 3399,
1846+
};
1847+
1848+
int i;
1849+
1850+
if (voltage == 0)
1851+
return 0;
1852+
1853+
if (unlikely(voltage < 3400 || voltage >= 5000))
1854+
hid_warn_once(hid_dev,
1855+
"%s: possibly using the wrong voltage curve\n",
1856+
__func__);
1857+
1858+
for (i = 0; i < ARRAY_SIZE(voltages); i++) {
1859+
if (voltage >= voltages[i])
1860+
return ARRAY_SIZE(voltages) - i;
1861+
}
1862+
1863+
return 0;
1864+
}
1865+
1866+
static int hidpp20_map_adc_measurement_1f20(u8 data[3], int *voltage)
1867+
{
1868+
int status;
1869+
u8 flags;
1870+
1871+
flags = data[2];
1872+
1873+
switch (flags) {
1874+
case 0x01:
1875+
status = POWER_SUPPLY_STATUS_DISCHARGING;
1876+
break;
1877+
case 0x03:
1878+
status = POWER_SUPPLY_STATUS_CHARGING;
1879+
break;
1880+
case 0x07:
1881+
status = POWER_SUPPLY_STATUS_FULL;
1882+
break;
1883+
case 0x0F:
1884+
default:
1885+
status = POWER_SUPPLY_STATUS_UNKNOWN;
1886+
break;
1887+
}
1888+
1889+
*voltage = get_unaligned_be16(data);
1890+
1891+
dbg_hid("Parsed 1f20 data as flag 0x%02x voltage %dmV\n",
1892+
flags, *voltage);
1893+
1894+
return status;
1895+
}
1896+
1897+
/* Return value is whether the device is online */
1898+
static bool hidpp20_get_adc_measurement_1f20(struct hidpp_device *hidpp,
1899+
u8 feature_index,
1900+
int *status, int *voltage)
1901+
{
1902+
struct hidpp_report response;
1903+
int ret;
1904+
u8 *params = (u8 *)response.fap.params;
1905+
1906+
*status = POWER_SUPPLY_STATUS_UNKNOWN;
1907+
*voltage = 0;
1908+
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
1909+
CMD_ADC_MEASUREMENT_GET_ADC_MEASUREMENT,
1910+
NULL, 0, &response);
1911+
1912+
if (ret > 0) {
1913+
hid_dbg(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
1914+
__func__, ret);
1915+
return false;
1916+
}
1917+
1918+
*status = hidpp20_map_adc_measurement_1f20(params, voltage);
1919+
return true;
1920+
}
1921+
1922+
static int hidpp20_query_adc_measurement_info_1f20(struct hidpp_device *hidpp)
1923+
{
1924+
u8 feature_type;
1925+
1926+
if (hidpp->battery.adc_measurement_feature_index == 0xff) {
1927+
int ret;
1928+
1929+
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_ADC_MEASUREMENT,
1930+
&hidpp->battery.adc_measurement_feature_index,
1931+
&feature_type);
1932+
if (ret)
1933+
return ret;
1934+
1935+
hidpp->capabilities |= HIDPP_CAPABILITY_ADC_MEASUREMENT;
1936+
}
1937+
1938+
hidpp->battery.online = hidpp20_get_adc_measurement_1f20(hidpp,
1939+
hidpp->battery.adc_measurement_feature_index,
1940+
&hidpp->battery.status,
1941+
&hidpp->battery.voltage);
1942+
hidpp->battery.capacity = hidpp20_map_adc_measurement_1f20_capacity(hidpp->hid_dev,
1943+
hidpp->battery.voltage);
1944+
hidpp_update_usb_wireless_status(hidpp);
1945+
1946+
return 0;
1947+
}
1948+
1949+
static int hidpp20_adc_measurement_event_1f20(struct hidpp_device *hidpp,
1950+
u8 *data, int size)
1951+
{
1952+
struct hidpp_report *report = (struct hidpp_report *)data;
1953+
int status, voltage;
1954+
1955+
if (report->fap.feature_index != hidpp->battery.adc_measurement_feature_index ||
1956+
report->fap.funcindex_clientid != EVENT_ADC_MEASUREMENT_STATUS_BROADCAST)
1957+
return 0;
1958+
1959+
status = hidpp20_map_adc_measurement_1f20(report->fap.params, &voltage);
1960+
1961+
hidpp->battery.online = status != POWER_SUPPLY_STATUS_UNKNOWN;
1962+
1963+
if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
1964+
hidpp->battery.status = status;
1965+
hidpp->battery.voltage = voltage;
1966+
hidpp->battery.capacity = hidpp20_map_adc_measurement_1f20_capacity(hidpp->hid_dev, voltage);
1967+
if (hidpp->battery.ps)
1968+
power_supply_changed(hidpp->battery.ps);
1969+
hidpp_update_usb_wireless_status(hidpp);
1970+
}
1971+
return 0;
1972+
}
1973+
17481974
/* -------------------------------------------------------------------------- */
17491975
/* 0x2120: Hi-resolution scrolling */
17501976
/* -------------------------------------------------------------------------- */
@@ -3663,6 +3889,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
36633889
ret = hidpp20_battery_voltage_event(hidpp, data, size);
36643890
if (ret != 0)
36653891
return ret;
3892+
ret = hidpp20_adc_measurement_event_1f20(hidpp, data, size);
3893+
if (ret != 0)
3894+
return ret;
36663895
}
36673896

36683897
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
@@ -3786,6 +4015,7 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
37864015
hidpp->battery.feature_index = 0xff;
37874016
hidpp->battery.solar_feature_index = 0xff;
37884017
hidpp->battery.voltage_feature_index = 0xff;
4018+
hidpp->battery.adc_measurement_feature_index = 0xff;
37894019

37904020
if (hidpp->protocol_major >= 2) {
37914021
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
@@ -3799,6 +4029,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
37994029
ret = hidpp20_query_battery_info_1004(hidpp);
38004030
if (ret)
38014031
ret = hidpp20_query_battery_voltage_info(hidpp);
4032+
if (ret)
4033+
ret = hidpp20_query_adc_measurement_info_1f20(hidpp);
38024034
}
38034035

38044036
if (ret)
@@ -3828,15 +4060,17 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
38284060

38294061
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
38304062
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE ||
3831-
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
4063+
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE ||
4064+
hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
38324065
battery_props[num_battery_props++] =
38334066
POWER_SUPPLY_PROP_CAPACITY;
38344067

38354068
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS)
38364069
battery_props[num_battery_props++] =
38374070
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
38384071

3839-
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
4072+
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE ||
4073+
hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
38404074
battery_props[num_battery_props++] =
38414075
POWER_SUPPLY_PROP_VOLTAGE_NOW;
38424076

@@ -4009,6 +4243,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
40094243
hidpp20_query_battery_voltage_info(hidpp);
40104244
else if (hidpp->capabilities & HIDPP_CAPABILITY_UNIFIED_BATTERY)
40114245
hidpp20_query_battery_info_1004(hidpp);
4246+
else if (hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
4247+
hidpp20_query_adc_measurement_info_1f20(hidpp);
40124248
else
40134249
hidpp20_query_battery_info_1000(hidpp);
40144250
}
@@ -4210,6 +4446,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
42104446

42114447
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
42124448
hidpp_unifying_init(hidpp);
4449+
else if (hid_is_usb(hidpp->hid_dev))
4450+
hidpp_serial_init(hidpp);
42134451

42144452
connected = hidpp_root_get_protocol_version(hidpp) == 0;
42154453
atomic_set(&hidpp->connected, connected);
@@ -4379,6 +4617,10 @@ static const struct hid_device_id hidpp_devices[] = {
43794617
{ /* Logitech G Pro Gaming Mouse over USB */
43804618
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
43814619

4620+
{ /* G935 Gaming Headset */
4621+
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0x0a87),
4622+
.driver_data = HIDPP_QUIRK_WIRELESS_STATUS },
4623+
43824624
{ /* MX5000 keyboard over Bluetooth */
43834625
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
43844626
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },

0 commit comments

Comments
 (0)