HID: hid-logitech-hidpp: Add basic support for Logitech G920
[cascardo/linux.git] / drivers / hid / hid-logitech-hidpp.c
index 0f53dc8..98b8f09 100644 (file)
@@ -49,11 +49,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_CLASS_WTP                  BIT(0)
 #define HIDPP_QUIRK_CLASS_M560                 BIT(1)
 #define HIDPP_QUIRK_CLASS_K400                 BIT(2)
+#define HIDPP_QUIRK_CLASS_G920                 BIT(3)
 
 /* bits 2..20 are reserved for classes */
 #define HIDPP_QUIRK_CONNECT_EVENTS             BIT(21)
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS       BIT(22)
 #define HIDPP_QUIRK_NO_HIDINPUT                        BIT(23)
+#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS       BIT(24)
 
 #define HIDPP_QUIRK_DELAYED_INIT               (HIDPP_QUIRK_NO_HIDINPUT | \
                                                 HIDPP_QUIRK_CONNECT_EVENTS)
@@ -146,8 +148,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
 static int __hidpp_send_report(struct hid_device *hdev,
                                struct hidpp_report *hidpp_report)
 {
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
        int fields_count, ret;
 
+       hidpp = hid_get_drvdata(hdev);
+
        switch (hidpp_report->report_id) {
        case REPORT_ID_HIDPP_SHORT:
                fields_count = HIDPP_REPORT_SHORT_LENGTH;
@@ -168,9 +173,13 @@ static int __hidpp_send_report(struct hid_device *hdev,
         */
        hidpp_report->device_index = 0xff;
 
-       ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
-               (u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
-               HID_REQ_SET_REPORT);
+       if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
+               ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, fields_count);
+       } 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;
 }
@@ -1430,10 +1439,12 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
        else
                name = hidpp_get_device_name(hidpp);
 
-       if (!name)
+       if (!name) {
                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);
+       }
 
        kfree(name);
 }
@@ -1596,6 +1607,25 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                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 */
        hid_device_io_start(hdev);
 
@@ -1604,8 +1634,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                if (!connected) {
                        ret = -ENODEV;
                        hid_err(hdev, "Device not connected");
-                       hid_device_io_stop(hdev);
-                       goto hid_parse_fail;
+                       goto hid_hw_open_failed;
                }
 
                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)
        if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
                ret = wtp_get_config(hidpp);
                if (ret)
-                       goto hid_parse_fail;
+                       goto hid_hw_open_failed;
        }
 
        /* Block incoming packets */
        hid_device_io_stop(hdev);
 
-       if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
-               connect_mask &= ~HID_CONNECT_HIDINPUT;
-
-       ret = hid_hw_start(hdev, connect_mask);
-       if (ret) {
-               hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
-               goto hid_hw_start_fail;
+       if (!(hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
+               ret = hid_hw_start(hdev, connect_mask);
+               if (ret) {
+                       hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+                       goto hid_hw_start_fail;
+               }
        }
 
        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)
 
        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_parse_fail:
        cancel_work_sync(&hidpp->work);
@@ -1655,9 +1689,11 @@ static void hidpp_remove(struct hid_device *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);
        mutex_destroy(&hidpp->send_mutex);
-       hid_hw_stop(hdev);
 }
 
 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,
                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},
        {}
 };