Merge branch 'for-6.3/logitech' into for-linus

- HID++ fixes for scroll wheel, protocol and debug (Bastien Nocera)
- add support of Logitech G923 Xbox Edition steering wheel (Walt Holman)
This commit is contained in:
Benjamin Tissoires 2023-02-22 10:37:02 +01:00
commit a74749efb4
3 changed files with 99 additions and 61 deletions

View File

@ -9217,6 +9217,13 @@ L: linux-input@vger.kernel.org
S: Maintained S: Maintained
F: drivers/hid/hid-logitech-* F: drivers/hid/hid-logitech-*
HID++ LOGITECH DRIVERS
R: Filipe Laíns <lains@riseup.net>
R: Bastien Nocera <hadess@hadess.net>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-logitech-hidpp.c
HID PLAYSTATION DRIVER HID PLAYSTATION DRIVER
M: Roderick Colenbrander <roderick.colenbrander@sony.com> M: Roderick Colenbrander <roderick.colenbrander@sony.com>
L: linux-input@vger.kernel.org L: linux-input@vger.kernel.org

View File

@ -825,6 +825,7 @@
#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e #define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e
#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f #define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262 #define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262
#define USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL 0xc26e
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 #define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287

View File

@ -30,11 +30,7 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>"); MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
static bool disable_raw_mode;
module_param(disable_raw_mode, bool, 0644);
MODULE_PARM_DESC(disable_raw_mode,
"Disable Raw mode reporting for touchpads and keep firmware gestures.");
static bool disable_tap_to_click; static bool disable_tap_to_click;
module_param(disable_tap_to_click, bool, 0644); module_param(disable_tap_to_click, bool, 0644);
@ -71,12 +67,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
/* bits 2..20 are reserved for classes */ /* bits 2..20 are reserved for classes */
/* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */ /* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23) #define HIDPP_QUIRK_DELAYED_INIT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25) #define HIDPP_QUIRK_UNIFYING BIT(25)
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(26) #define HIDPP_QUIRK_HIDPP_WHEELS BIT(26)
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27) #define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28) #define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29)
/* These are just aliases for now */ /* These are just aliases for now */
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@ -87,8 +84,6 @@ MODULE_PARM_DESC(disable_tap_to_click,
HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL | \ HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL | \
HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL) HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL)
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
#define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0) #define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0)
#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1) #define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2) #define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
@ -225,6 +220,16 @@ struct hidpp_device {
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
/* HID++ 2.0 error codes */ /* HID++ 2.0 error codes */
#define HIDPP20_ERROR_NO_ERROR 0x00
#define HIDPP20_ERROR_UNKNOWN 0x01
#define HIDPP20_ERROR_INVALID_ARGS 0x02
#define HIDPP20_ERROR_OUT_OF_RANGE 0x03
#define HIDPP20_ERROR_HW_ERROR 0x04
#define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05
#define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06
#define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07
#define HIDPP20_ERROR_BUSY 0x08
#define HIDPP20_ERROR_UNSUPPORTED 0x09
#define HIDPP20_ERROR 0xff #define HIDPP20_ERROR 0xff
static void hidpp_connect_event(struct hidpp_device *hidpp_dev); static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
@ -279,6 +284,7 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
struct hidpp_report *response) struct hidpp_report *response)
{ {
int ret; int ret;
int max_retries = 3;
mutex_lock(&hidpp->send_mutex); mutex_lock(&hidpp->send_mutex);
@ -291,34 +297,39 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
*/ */
*response = *message; *response = *message;
ret = __hidpp_send_report(hidpp->hid_dev, message); for (; max_retries != 0; max_retries--) {
ret = __hidpp_send_report(hidpp->hid_dev, message);
if (ret) { if (ret) {
dbg_hid("__hidpp_send_report returned err: %d\n", ret); dbg_hid("__hidpp_send_report returned err: %d\n", ret);
memset(response, 0, sizeof(struct hidpp_report)); memset(response, 0, sizeof(struct hidpp_report));
goto exit; goto exit;
} }
if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
5*HZ)) { 5*HZ)) {
dbg_hid("%s:timeout waiting for response\n", __func__); dbg_hid("%s:timeout waiting for response\n", __func__);
memset(response, 0, sizeof(struct hidpp_report)); memset(response, 0, sizeof(struct hidpp_report));
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
} }
if (response->report_id == REPORT_ID_HIDPP_SHORT && if (response->report_id == REPORT_ID_HIDPP_SHORT &&
response->rap.sub_id == HIDPP_ERROR) { response->rap.sub_id == HIDPP_ERROR) {
ret = response->rap.params[1]; ret = response->rap.params[1];
dbg_hid("%s:got hidpp error %02X\n", __func__, ret); dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
goto exit; goto exit;
} }
if ((response->report_id == REPORT_ID_HIDPP_LONG || if ((response->report_id == REPORT_ID_HIDPP_LONG ||
response->report_id == REPORT_ID_HIDPP_VERY_LONG) && response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
response->fap.feature_index == HIDPP20_ERROR) { response->fap.feature_index == HIDPP20_ERROR) {
ret = response->fap.params[1]; ret = response->fap.params[1];
dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); if (ret != HIDPP20_ERROR_BUSY) {
goto exit; dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
goto exit;
}
dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
}
} }
exit: exit:
@ -334,8 +345,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
struct hidpp_report *message; struct hidpp_report *message;
int ret; int ret;
if (param_count > sizeof(message->fap.params)) if (param_count > sizeof(message->fap.params)) {
hid_dbg(hidpp->hid_dev,
"Invalid number of parameters passed to command (%d != %llu)\n",
param_count,
(unsigned long long) sizeof(message->fap.params));
return -EINVAL; return -EINVAL;
}
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
if (!message) if (!message)
@ -3436,11 +3452,17 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp)
ret = hidpp10_enable_scrolling_acceleration(hidpp); ret = hidpp10_enable_scrolling_acceleration(hidpp);
multiplier = 8; multiplier = 8;
} }
if (ret) if (ret) {
hid_dbg(hidpp->hid_dev,
"Could not enable hi-res scrolling: %d\n", ret);
return ret; return ret;
}
if (multiplier == 0) if (multiplier == 0) {
hid_dbg(hidpp->hid_dev,
"Invalid multiplier 0 from device, setting it to 1\n");
multiplier = 1; multiplier = 1;
}
hidpp->vertical_wheel_counter.wheel_multiplier = multiplier; hidpp->vertical_wheel_counter.wheel_multiplier = multiplier;
hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier); hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier);
@ -3472,14 +3494,8 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp)
hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n"); hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n");
} }
} else { } else {
struct hidpp_report response; /* We cannot detect fast scrolling support on HID++ 1.0 devices */
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) {
ret = hidpp_send_rap_command_sync(hidpp,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_REGISTER,
HIDPP_ENABLE_FAST_SCROLL,
NULL, 0, &response);
if (!ret) {
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL; hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL;
hid_dbg(hidpp->hid_dev, "Detected HID++ 1.0 fast scroll\n"); hid_dbg(hidpp->hid_dev, "Detected HID++ 1.0 fast scroll\n");
} }
@ -4002,7 +4018,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL) if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
hi_res_scroll_enable(hidpp); hi_res_scroll_enable(hidpp);
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input) if (!(hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) || hidpp->delayed_input)
/* if the input nodes are already created, we can stop now */ /* if the input nodes are already created, we can stop now */
return; return;
@ -4107,6 +4123,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
bool connected; bool connected;
unsigned int connect_mask = HID_CONNECT_DEFAULT; unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct hidpp_ff_private_data data; struct hidpp_ff_private_data data;
bool will_restart = false;
/* report_fixup needs drvdata to be set before we call hid_parse */ /* report_fixup needs drvdata to be set before we call hid_parse */
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL); hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
@ -4147,11 +4164,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp_application_equals(hdev, HID_GD_KEYBOARD)) hidpp_application_equals(hdev, HID_GD_KEYBOARD))
hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS; hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS;
if (disable_raw_mode) {
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_allocate(hdev, id); ret = wtp_allocate(hdev, id);
if (ret) if (ret)
@ -4162,6 +4174,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret; return ret;
} }
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT ||
hidpp->quirks & HIDPP_QUIRK_UNIFYING)
will_restart = true;
INIT_WORK(&hidpp->work, delayed_work_cb); INIT_WORK(&hidpp->work, delayed_work_cb);
mutex_init(&hidpp->send_mutex); mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait); init_waitqueue_head(&hidpp->wait);
@ -4176,7 +4192,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
* Plain USB connections need to actually call start and open * Plain USB connections need to actually call start and open
* on the transport driver to allow incoming data. * on the transport driver to allow incoming data.
*/ */
ret = hid_hw_start(hdev, 0); ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask);
if (ret) { if (ret) {
hid_err(hdev, "hw start failed\n"); hid_err(hdev, "hw start failed\n");
goto hid_hw_start_fail; goto hid_hw_start_fail;
@ -4213,6 +4229,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp->wireless_feature_index = 0; hidpp->wireless_feature_index = 0;
else if (ret) else if (ret)
goto hid_hw_init_fail; goto hid_hw_init_fail;
ret = 0;
} }
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) { if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
@ -4227,19 +4244,21 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp_connect_event(hidpp); hidpp_connect_event(hidpp);
/* Reset the HID node state */ if (will_restart) {
hid_device_io_stop(hdev); /* Reset the HID node state */
hid_hw_close(hdev); hid_device_io_stop(hdev);
hid_hw_stop(hdev); hid_hw_close(hdev);
hid_hw_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT; connect_mask &= ~HID_CONNECT_HIDINPUT;
/* Now export the actual inputs and hidraw nodes to the world */ /* Now export the actual inputs and hidraw nodes to the world */
ret = hid_hw_start(hdev, connect_mask); ret = hid_hw_start(hdev, connect_mask);
if (ret) { if (ret) {
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
goto hid_hw_start_fail; goto hid_hw_start_fail;
}
} }
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
@ -4297,9 +4316,15 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651), USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP }, .driver_data = HIDPP_QUIRK_CLASS_WTP },
{ /* Mouse Logitech Anywhere MX */
LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Mouse logitech M560 */ { /* Mouse logitech M560 */
LDJ_DEVICE(0x402d), LDJ_DEVICE(0x402d),
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
{ /* Mouse Logitech M705 (firmware RQM17) */
LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Mouse Logitech Performance MX */
LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */ { /* Keyboard logitech K400 */
LDJ_DEVICE(0x4024), LDJ_DEVICE(0x4024),
.driver_data = HIDPP_QUIRK_CLASS_K400 }, .driver_data = HIDPP_QUIRK_CLASS_K400 },
@ -4348,6 +4373,9 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Logitech G920 Wheel over USB */ { /* Logitech G920 Wheel over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
{ /* Logitech G923 Wheel (Xbox version) over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },
{ /* Logitech G Pro Gaming Mouse over USB */ { /* Logitech G Pro Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
@ -4367,6 +4395,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX Ergo trackball over Bluetooth */ { /* MX Ergo trackball over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) }, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) },
{ /* Signature M650 over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) },
{ /* MX Master 3 mouse over Bluetooth */ { /* MX Master 3 mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) }, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) },
{} {}