Merge tag 'devicetree-for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/robh...
[linux-2.6-block.git] / drivers / hid / hid-logitech-hidpp.c
index 129b01be488d2ff88678c8913a0ec467e47d2634..fd6d8f1d9b8f61992a69ce651dd379d121c2da49 100644 (file)
@@ -69,12 +69,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS       BIT(22)
 #define HIDPP_QUIRK_DELAYED_INIT               BIT(23)
 #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS       BIT(24)
-#define HIDPP_QUIRK_UNIFYING                   BIT(25)
-#define HIDPP_QUIRK_HIDPP_WHEELS               BIT(26)
-#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS     BIT(27)
-#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
-#define HIDPP_QUIRK_HI_RES_SCROLL_1P0          BIT(29)
-#define HIDPP_QUIRK_WIRELESS_STATUS            BIT(30)
+#define HIDPP_QUIRK_HIDPP_WHEELS               BIT(25)
+#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS     BIT(26)
+#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(27)
+#define HIDPP_QUIRK_HI_RES_SCROLL_1P0          BIT(28)
+#define HIDPP_QUIRK_WIRELESS_STATUS            BIT(29)
 
 /* These are just aliases for now */
 #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -194,7 +193,6 @@ struct hidpp_device {
 
        struct work_struct work;
        struct kfifo delayed_work_fifo;
-       atomic_t connected;
        struct input_dev *delayed_input;
 
        unsigned long quirks;
@@ -228,15 +226,13 @@ struct hidpp_device {
 #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_NOT_ALLOWED              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
 
-static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
-
 static int __hidpp_send_report(struct hid_device *hdev,
                                struct hidpp_report *hidpp_report)
 {
@@ -275,21 +271,22 @@ static int __hidpp_send_report(struct hid_device *hdev,
 }
 
 /*
- * hidpp_send_message_sync() returns 0 in case of success, and something else
- * in case of a failure.
- * - If ' something else' is positive, that means that an error has been raised
- *   by the protocol itself.
- * - If ' something else' is negative, that means that we had a classic error
- *   (-ENOMEM, -EPIPE, etc...)
+ * Effectively send the message to the device, waiting for its answer.
+ *
+ * Must be called with hidpp->send_mutex locked
+ *
+ * Same return protocol than hidpp_send_message_sync():
+ * - success on 0
+ * - negative error means transport error
+ * - positive value means protocol error
  */
-static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp,
        struct hidpp_report *message,
        struct hidpp_report *response)
 {
-       int ret = -1;
-       int max_retries = 3;
+       int ret;
 
-       mutex_lock(&hidpp->send_mutex);
+       __must_hold(&hidpp->send_mutex);
 
        hidpp->send_receive_buf = response;
        hidpp->answer_available = false;
@@ -300,47 +297,74 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
         */
        *response = *message;
 
-       for (; max_retries != 0 && ret; max_retries--) {
-               ret = __hidpp_send_report(hidpp->hid_dev, message);
+       ret = __hidpp_send_report(hidpp->hid_dev, message);
+       if (ret) {
+               dbg_hid("__hidpp_send_report returned err: %d\n", ret);
+               memset(response, 0, sizeof(struct hidpp_report));
+               return ret;
+       }
 
-               if (ret) {
-                       dbg_hid("__hidpp_send_report returned err: %d\n", ret);
-                       memset(response, 0, sizeof(struct hidpp_report));
-                       break;
-               }
+       if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
+                               5*HZ)) {
+               dbg_hid("%s:timeout waiting for response\n", __func__);
+               memset(response, 0, sizeof(struct hidpp_report));
+               return -ETIMEDOUT;
+       }
 
-               if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
-                                       5*HZ)) {
-                       dbg_hid("%s:timeout waiting for response\n", __func__);
-                       memset(response, 0, sizeof(struct hidpp_report));
-                       ret = -ETIMEDOUT;
-                       break;
-               }
+       if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+           response->rap.sub_id == HIDPP_ERROR) {
+               ret = response->rap.params[1];
+               dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+               return ret;
+       }
+
+       if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+            response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+           response->fap.feature_index == HIDPP20_ERROR) {
+               ret = response->fap.params[1];
+               dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * hidpp_send_message_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
+static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+       struct hidpp_report *message,
+       struct hidpp_report *response)
+{
+       int ret;
+       int max_retries = 3;
 
-               if (response->report_id == REPORT_ID_HIDPP_SHORT &&
-                   response->rap.sub_id == HIDPP_ERROR) {
-                       ret = response->rap.params[1];
-                       dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+       mutex_lock(&hidpp->send_mutex);
+
+       do {
+               ret = __do_hidpp_send_message_sync(hidpp, message, response);
+               if (ret != HIDPP20_ERROR_BUSY)
                        break;
-               }
 
-               if ((response->report_id == REPORT_ID_HIDPP_LONG ||
-                    response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
-                   response->fap.feature_index == HIDPP20_ERROR) {
-                       ret = response->fap.params[1];
-                       if (ret != HIDPP20_ERROR_BUSY) {
-                               dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
-                               break;
-                       }
-                       dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
-               }
-       }
+               dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
+       } while (--max_retries);
 
        mutex_unlock(&hidpp->send_mutex);
        return ret;
 
 }
 
+/*
+ * hidpp_send_fap_command_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
 static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
        u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
        struct hidpp_report *response)
@@ -373,6 +397,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
        return ret;
 }
 
+/*
+ * hidpp_send_rap_command_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
 static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
        u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
        struct hidpp_report *response)
@@ -415,13 +446,6 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
        return ret;
 }
 
-static void delayed_work_cb(struct work_struct *work)
-{
-       struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
-                                                       work);
-       hidpp_connect_event(hidpp);
-}
-
 static inline bool hidpp_match_answer(struct hidpp_report *question,
                struct hidpp_report *answer)
 {
@@ -1800,15 +1824,14 @@ static int hidpp_battery_get_property(struct power_supply *psy,
 /* -------------------------------------------------------------------------- */
 #define HIDPP_PAGE_WIRELESS_DEVICE_STATUS                      0x1d4b
 
-static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
+static int hidpp_get_wireless_feature_index(struct hidpp_device *hidpp, u8 *feature_index)
 {
        u8 feature_type;
        int ret;
 
        ret = hidpp_root_get_feature(hidpp,
                                     HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
-                                    &hidpp->wireless_feature_index,
-                                    &feature_type);
+                                    feature_index, &feature_type);
 
        return ret;
 }
@@ -3107,7 +3130,7 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
        return 0;
 };
 
-static int wtp_connect(struct hid_device *hdev, bool connected)
+static int wtp_connect(struct hid_device *hdev)
 {
        struct hidpp_device *hidpp = hid_get_drvdata(hdev);
        struct wtp_data *wd = hidpp->private_data;
@@ -3169,7 +3192,7 @@ static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
 #define M560_SUB_ID                    0x0a
 #define M560_BUTTON_MODE_REGISTER      0x35
 
-static int m560_send_config_command(struct hid_device *hdev, bool connected)
+static int m560_send_config_command(struct hid_device *hdev)
 {
        struct hidpp_report response;
        struct hidpp_device *hidpp_dev;
@@ -3364,7 +3387,7 @@ static int k400_allocate(struct hid_device *hdev)
        return 0;
 };
 
-static int k400_connect(struct hid_device *hdev, bool connected)
+static int k400_connect(struct hid_device *hdev)
 {
        struct hidpp_device *hidpp = hid_get_drvdata(hdev);
 
@@ -3859,8 +3882,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
        }
 
        if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
-               atomic_set(&hidpp->connected,
-                               !(report->rap.params[0] & (1 << 6)));
                if (schedule_work(&hidpp->work) == 0)
                        dbg_hid("%s: connect event already queued\n", __func__);
                return 1;
@@ -4096,24 +4117,22 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
        return ret;
 }
 
-static void hidpp_overwrite_name(struct hid_device *hdev)
+/* Get name + serial for USB and Bluetooth HID++ devices */
+static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
 {
-       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct hid_device *hdev = hidpp->hid_dev;
        char *name;
 
-       if (hidpp->protocol_major < 2)
-               return;
+       /* Bluetooth devices already have their serialnr set */
+       if (hid_is_usb(hdev))
+               hidpp_serial_init(hidpp);
 
        name = hidpp_get_device_name(hidpp);
-
-       if (!name) {
-               hid_err(hdev, "unable to retrieve the name of the device");
-       } else {
+       if (name) {
                dbg_hid("HID++: Got name: %s\n", name);
                snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+               kfree(name);
        }
-
-       kfree(name);
 }
 
 static int hidpp_input_open(struct input_dev *dev)
@@ -4154,15 +4173,18 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
        return input_dev;
 }
 
-static void hidpp_connect_event(struct hidpp_device *hidpp)
+static void hidpp_connect_event(struct work_struct *work)
 {
+       struct hidpp_device *hidpp = container_of(work, struct hidpp_device, work);
        struct hid_device *hdev = hidpp->hid_dev;
-       int ret = 0;
-       bool connected = atomic_read(&hidpp->connected);
        struct input_dev *input;
        char *name, *devm_name;
+       int ret;
 
-       if (!connected) {
+       /* Get device version to check if it is connected */
+       ret = hidpp_root_get_protocol_version(hidpp);
+       if (ret) {
+               hid_info(hidpp->hid_dev, "Disconnected\n");
                if (hidpp->battery.ps) {
                        hidpp->battery.online = false;
                        hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
@@ -4173,15 +4195,15 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
        }
 
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
-               ret = wtp_connect(hdev, connected);
+               ret = wtp_connect(hdev);
                if (ret)
                        return;
        } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
-               ret = m560_send_config_command(hdev, connected);
+               ret = m560_send_config_command(hdev);
                if (ret)
                        return;
        } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
-               ret = k400_connect(hdev, connected);
+               ret = k400_connect(hdev);
                if (ret)
                        return;
        }
@@ -4204,14 +4226,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
                        return;
        }
 
-       /* the device is already connected, we can ask for its name and
-        * protocol */
-       if (!hidpp->protocol_major) {
-               ret = hidpp_root_get_protocol_version(hidpp);
-               if (ret) {
-                       hid_err(hdev, "Can not get the protocol version.\n");
-                       return;
-               }
+       if (hidpp->protocol_major >= 2) {
+               u8 feature_index;
+
+               if (!hidpp_get_wireless_feature_index(hidpp, &feature_index))
+                       hidpp->wireless_feature_index = feature_index;
        }
 
        if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
@@ -4356,10 +4375,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        struct hidpp_device *hidpp;
        int ret;
-       bool connected;
        unsigned int connect_mask = HID_CONNECT_DEFAULT;
-       struct hidpp_ff_private_data data;
-       bool will_restart = false;
 
        /* report_fixup needs drvdata to be set before we call hid_parse */
        hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
@@ -4388,9 +4404,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
        }
 
-       if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
-               hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
-
        if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
            hidpp_application_equals(hdev, HID_GD_MOUSE))
                hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
@@ -4410,11 +4423,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        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, hidpp_connect_event);
        mutex_init(&hidpp->send_mutex);
        init_waitqueue_head(&hidpp->wait);
 
@@ -4425,10 +4434,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                         hdev->name);
 
        /*
-        * Plain USB connections need to actually call start and open
-        * on the transport driver to allow incoming data.
+        * First call hid_hw_start(hdev, 0) to allow IO without connecting any
+        * hid subdrivers (hid-input, hidraw). This allows retrieving the dev's
+        * name and serial number and store these in hdev->name and hdev->uniq,
+        * before the hid-input and hidraw drivers expose these to userspace.
         */
-       ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask);
+       ret = hid_hw_start(hdev, 0);
        if (ret) {
                hid_err(hdev, "hw start failed\n");
                goto hid_hw_start_fail;
@@ -4444,69 +4455,46 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
        /* Allow incoming packets */
        hid_device_io_start(hdev);
 
-       if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
+       /* Get name + serial, store in hdev->name + hdev->uniq */
+       if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
                hidpp_unifying_init(hidpp);
-       else if (hid_is_usb(hidpp->hid_dev))
-               hidpp_serial_init(hidpp);
-
-       connected = hidpp_root_get_protocol_version(hidpp) == 0;
-       atomic_set(&hidpp->connected, connected);
-       if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
-               if (!connected) {
-                       ret = -ENODEV;
-                       hid_err(hdev, "Device not connected");
-                       goto hid_hw_init_fail;
-               }
-
-               hidpp_overwrite_name(hdev);
-       }
+       else
+               hidpp_non_unifying_init(hidpp);
 
-       if (connected && hidpp->protocol_major >= 2) {
-               ret = hidpp_set_wireless_feature_index(hidpp);
-               if (ret == -ENOENT)
-                       hidpp->wireless_feature_index = 0;
-               else if (ret)
-                       goto hid_hw_init_fail;
-               ret = 0;
-       }
+       if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
+               connect_mask &= ~HID_CONNECT_HIDINPUT;
 
-       if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
-               ret = wtp_get_config(hidpp);
-               if (ret)
-                       goto hid_hw_init_fail;
-       } else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
-               ret = g920_get_config(hidpp, &data);
-               if (ret)
-                       goto hid_hw_init_fail;
+       /* Now export the actual inputs and hidraw nodes to the world */
+       hid_device_io_stop(hdev);
+       ret = hid_connect(hdev, connect_mask);
+       if (ret) {
+               hid_err(hdev, "%s:hid_connect returned error %d\n", __func__, ret);
+               goto hid_hw_init_fail;
        }
 
-       hidpp_connect_event(hidpp);
-
-       if (will_restart) {
-               /* Reset the HID node state */
-               hid_device_io_stop(hdev);
-               hid_hw_close(hdev);
-               hid_hw_stop(hdev);
+       /* Check for connected devices now that incoming packets will not be disabled again */
+       hid_device_io_start(hdev);
+       schedule_work(&hidpp->work);
+       flush_work(&hidpp->work);
 
-               if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
-                       connect_mask &= ~HID_CONNECT_HIDINPUT;
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+               struct hidpp_ff_private_data data;
 
-               /* Now export the actual inputs and hidraw nodes to the world */
-               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;
-               }
-       }
+               ret = g920_get_config(hidpp, &data);
+               if (!ret)
+                       ret = hidpp_ff_init(hidpp, &data);
 
-       if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
-               ret = hidpp_ff_init(hidpp, &data);
                if (ret)
                        hid_warn(hidpp->hid_dev,
                     "Unable to initialize force feedback support, errno %d\n",
                                 ret);
        }
 
+       /*
+        * This relies on logi_dj_ll_close() being a no-op so that DJ connection
+        * events will still be received.
+        */
+       hid_hw_close(hdev);
        return ret;
 
 hid_hw_init_fail:
@@ -4620,6 +4608,8 @@ static const struct hid_device_id hidpp_devices[] = {
                .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },
        { /* Logitech G Pro Gaming Mouse over USB */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
+       { /* Logitech G Pro X Superlight Gaming Mouse over USB */
+         HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC094) },
 
        { /* G935 Gaming Headset */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0x0a87),
@@ -4640,6 +4630,8 @@ static const struct hid_device_id hidpp_devices[] = {
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
        { /* MX Master mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb012) },
+       { /* M720 Triathlon mouse over Bluetooth */
+         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb015) },
        { /* MX Ergo trackball over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) },
@@ -4647,6 +4639,8 @@ static const struct hid_device_id hidpp_devices[] = {
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) },
        { /* MX Master 3 mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) },
+       { /* MX Anywhere 3 mouse over Bluetooth */
+         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb025) },
        { /* MX Master 3S mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb034) },
        {}