Commit 7bfd2927 authored by Simon Wood's avatar Simon Wood Committed by Jiri Kosina

HID: hid-logitech-hidpp: Add basic support for Logitech G920

This patch adds basic support for the Logitech G920 wheel when in HID
mode. This wheel 'speaks' the HID++ protocol, and therefor is driven
with hid-logitech-hidpp.

At this stage the driver only shows that it can communicate with the
wheel by outputting the name discovered over HID++.

The normal HID functions work to give input functionality using
joystick/event interface.

Note: in 'hidpp_probe()' we have to start the hardware to get packets
flowing, the same might apply in future for other devices which don't
use the unifying protocol.
Signed-off-by: default avatarSimon Wood <simon@mungewell.org>
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent a5ce8f5b
...@@ -1902,6 +1902,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1902,6 +1902,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
......
...@@ -619,6 +619,7 @@ ...@@ -619,6 +619,7 @@
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
#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_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
......
...@@ -49,11 +49,13 @@ MODULE_PARM_DESC(disable_tap_to_click, ...@@ -49,11 +49,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_CLASS_WTP BIT(0) #define HIDPP_QUIRK_CLASS_WTP BIT(0)
#define HIDPP_QUIRK_CLASS_M560 BIT(1) #define HIDPP_QUIRK_CLASS_M560 BIT(1)
#define HIDPP_QUIRK_CLASS_K400 BIT(2) #define HIDPP_QUIRK_CLASS_K400 BIT(2)
#define HIDPP_QUIRK_CLASS_G920 BIT(3)
/* bits 2..20 are reserved for classes */ /* bits 2..20 are reserved for classes */
#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
#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_NO_HIDINPUT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \ #define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
HIDPP_QUIRK_CONNECT_EVENTS) HIDPP_QUIRK_CONNECT_EVENTS)
...@@ -146,8 +148,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp_dev); ...@@ -146,8 +148,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
static int __hidpp_send_report(struct hid_device *hdev, static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_report *hidpp_report) struct hidpp_report *hidpp_report)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
int fields_count, ret; int fields_count, ret;
hidpp = hid_get_drvdata(hdev);
switch (hidpp_report->report_id) { switch (hidpp_report->report_id) {
case REPORT_ID_HIDPP_SHORT: case REPORT_ID_HIDPP_SHORT:
fields_count = HIDPP_REPORT_SHORT_LENGTH; fields_count = HIDPP_REPORT_SHORT_LENGTH;
...@@ -168,9 +173,13 @@ static int __hidpp_send_report(struct hid_device *hdev, ...@@ -168,9 +173,13 @@ static int __hidpp_send_report(struct hid_device *hdev,
*/ */
hidpp_report->device_index = 0xff; hidpp_report->device_index = 0xff;
ret = hid_hw_raw_request(hdev, hidpp_report->report_id, if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
(u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT, ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, fields_count);
HID_REQ_SET_REPORT); } else {
ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
(u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT);
}
return ret == fields_count ? 0 : -1; return ret == fields_count ? 0 : -1;
} }
...@@ -1430,10 +1439,12 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying) ...@@ -1430,10 +1439,12 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
else else
name = hidpp_get_device_name(hidpp); name = hidpp_get_device_name(hidpp);
if (!name) if (!name) {
hid_err(hdev, "unable to retrieve the name of the device"); hid_err(hdev, "unable to retrieve the name of the device");
else } else {
dbg_hid("HID++: Got name: %s\n", name);
snprintf(hdev->name, sizeof(hdev->name), "%s", name); snprintf(hdev->name, sizeof(hdev->name), "%s", name);
}
kfree(name); kfree(name);
} }
...@@ -1596,6 +1607,25 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1596,6 +1607,25 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto hid_parse_fail; goto hid_parse_fail;
} }
if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto hid_hw_start_fail;
}
ret = hid_hw_open(hdev);
if (ret < 0) {
dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
__func__, ret);
hid_hw_stop(hdev);
goto hid_hw_start_fail;
}
}
/* Allow incoming packets */ /* Allow incoming packets */
hid_device_io_start(hdev); hid_device_io_start(hdev);
...@@ -1604,8 +1634,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1604,8 +1634,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!connected) { if (!connected) {
ret = -ENODEV; ret = -ENODEV;
hid_err(hdev, "Device not connected"); hid_err(hdev, "Device not connected");
hid_device_io_stop(hdev); goto hid_hw_open_failed;
goto hid_parse_fail;
} }
hid_info(hdev, "HID++ %u.%u device connected.\n", hid_info(hdev, "HID++ %u.%u device connected.\n",
...@@ -1618,19 +1647,18 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1618,19 +1647,18 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) { if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp); ret = wtp_get_config(hidpp);
if (ret) if (ret)
goto hid_parse_fail; goto hid_hw_open_failed;
} }
/* Block incoming packets */ /* Block incoming packets */
hid_device_io_stop(hdev); hid_device_io_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) if (!(hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
connect_mask &= ~HID_CONNECT_HIDINPUT; ret = hid_hw_start(hdev, connect_mask);
if (ret) {
ret = hid_hw_start(hdev, connect_mask); hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
if (ret) { goto hid_hw_start_fail;
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); }
goto hid_hw_start_fail;
} }
if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) { if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) {
...@@ -1642,6 +1670,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1642,6 +1670,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret; return ret;
hid_hw_open_failed:
hid_device_io_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
hid_hw_start_fail: hid_hw_start_fail:
hid_parse_fail: hid_parse_fail:
cancel_work_sync(&hidpp->work); cancel_work_sync(&hidpp->work);
...@@ -1655,9 +1689,11 @@ static void hidpp_remove(struct hid_device *hdev) ...@@ -1655,9 +1689,11 @@ static void hidpp_remove(struct hid_device *hdev)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)
hid_hw_close(hdev);
hid_hw_stop(hdev);
cancel_work_sync(&hidpp->work); cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex); mutex_destroy(&hidpp->send_mutex);
hid_hw_stop(hdev);
} }
static const struct hid_device_id hidpp_devices[] = { static const struct hid_device_id hidpp_devices[] = {
...@@ -1685,6 +1721,9 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -1685,6 +1721,9 @@ static const struct hid_device_id hidpp_devices[] = {
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
{} {}
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment