HID: logitech-hidpp: prefix the name with "Logitech"
[firefly-linux-kernel-4.4.55.git] / drivers / hid / hid-logitech-hidpp.c
index f9a4ec0ff76d4631f174cf5be6b08b8ee54d9950..a93cefe0e522e66fe670a28a269f13da9ff25d5d 100644 (file)
@@ -38,6 +38,8 @@ MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
 
 /* bits 1..20 are reserved for classes */
 #define HIDPP_QUIRK_DELAYED_INIT               BIT(21)
+#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS       BIT(22)
+#define HIDPP_QUIRK_MULTI_INPUT                        BIT(23)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -149,6 +151,14 @@ static int __hidpp_send_report(struct hid_device *hdev,
        return ret == fields_count ? 0 : -1;
 }
 
+/**
+ * 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...)
+ */
 static int hidpp_send_message_sync(struct hidpp_device *hidpp,
        struct hidpp_report *message,
        struct hidpp_report *response)
@@ -198,13 +208,15 @@ 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)
 {
-       struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report),
-                       GFP_KERNEL);
+       struct hidpp_report *message;
        int ret;
 
        if (param_count > sizeof(message->fap.params))
                return -EINVAL;
 
+       message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
+       if (!message)
+               return -ENOMEM;
        message->report_id = REPORT_ID_HIDPP_LONG;
        message->fap.feature_index = feat_index;
        message->fap.funcindex_clientid = funcindex_clientid;
@@ -219,8 +231,7 @@ 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)
 {
-       struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report),
-                       GFP_KERNEL);
+       struct hidpp_report *message;
        int ret;
 
        if ((report_id != REPORT_ID_HIDPP_SHORT) &&
@@ -230,6 +241,9 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
        if (param_count > sizeof(message->rap.params))
                return -EINVAL;
 
+       message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
+       if (!message)
+               return -ENOMEM;
        message->report_id = report_id;
        message->rap.sub_id = sub_id;
        message->rap.reg_address = reg_address;
@@ -268,6 +282,33 @@ static inline bool hidpp_report_is_connect_event(struct hidpp_report *report)
                (report->rap.sub_id == 0x41);
 }
 
+/**
+ * hidpp_prefix_name() prefixes the current given name with "Logitech ".
+ */
+static void hidpp_prefix_name(char **name, int name_length)
+{
+#define PREFIX_LENGTH 9 /* "Logitech " */
+
+       int new_length;
+       char *new_name;
+
+       if (name_length > PREFIX_LENGTH &&
+           strncmp(*name, "Logitech ", PREFIX_LENGTH) == 0)
+               /* The prefix has is already in the name */
+               return;
+
+       new_length = PREFIX_LENGTH + name_length;
+       new_name = kzalloc(new_length, GFP_KERNEL);
+       if (!new_name)
+               return;
+
+       snprintf(new_name, new_length, "Logitech %s", *name);
+
+       kfree(*name);
+
+       *name = new_name;
+}
+
 /* -------------------------------------------------------------------------- */
 /* HIDP++ 1.0 commands                                                        */
 /* -------------------------------------------------------------------------- */
@@ -299,11 +340,18 @@ static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
 
        len = response.rap.params[1];
 
+       if (2 + len > sizeof(response.rap.params))
+               return NULL;
+
        name = kzalloc(len + 1, GFP_KERNEL);
        if (!name)
                return NULL;
 
        memcpy(name, &response.rap.params[2], len);
+
+       /* include the terminating '\0' */
+       hidpp_prefix_name(&name, len + 1);
+
        return name;
 }
 
@@ -347,14 +395,23 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
                        CMD_ROOT_GET_PROTOCOL_VERSION,
                        NULL, 0, &response);
 
-       if (ret == 1) {
+       if (ret == HIDPP_ERROR_INVALID_SUBID) {
                hidpp->protocol_major = 1;
                hidpp->protocol_minor = 0;
                return 0;
        }
 
+       /* the device might not be connected */
+       if (ret == HIDPP_ERROR_RESOURCE_ERROR)
+               return -EIO;
+
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
        if (ret)
-               return -ret;
+               return ret;
 
        hidpp->protocol_major = response.fap.params[0];
        hidpp->protocol_minor = response.fap.params[1];
@@ -392,8 +449,13 @@ static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp,
        ret = hidpp_send_fap_command_sync(hidpp, feature_index,
                CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response);
 
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
        if (ret)
-               return -ret;
+               return ret;
 
        *nameLength = response.fap.params[0];
 
@@ -411,8 +473,13 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
                CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1,
                &response);
 
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
        if (ret)
-               return -ret;
+               return ret;
 
        if (response.report_id == REPORT_ID_HIDPP_LONG)
                count = HIDPP_REPORT_LONG_LENGTH - 4;
@@ -428,7 +495,7 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
        return count;
 }
 
-static char *hidpp_get_device_name(struct hidpp_device *hidpp, u8 *name_length)
+static char *hidpp_get_device_name(struct hidpp_device *hidpp)
 {
        u8 feature_type;
        u8 feature_index;
@@ -440,28 +507,32 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp, u8 *name_length)
        ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE,
                &feature_index, &feature_type);
        if (ret)
-               goto out_err;
+               return NULL;
 
        ret = hidpp_devicenametype_get_count(hidpp, feature_index,
                &__name_length);
        if (ret)
-               goto out_err;
+               return NULL;
 
        name = kzalloc(__name_length + 1, GFP_KERNEL);
        if (!name)
-               goto out_err;
+               return NULL;
 
-       *name_length = __name_length + 1;
-       while (index < __name_length)
-               index += hidpp_devicenametype_get_device_name(hidpp,
+       while (index < __name_length) {
+               ret = hidpp_devicenametype_get_device_name(hidpp,
                        feature_index, index, name + index,
                        __name_length - index);
+               if (ret <= 0) {
+                       kfree(name);
+                       return NULL;
+               }
+               index += ret;
+       }
 
-       return name;
+       /* include the terminating '\0' */
+       hidpp_prefix_name(&name, __name_length + 1);
 
-out_err:
-       *name_length = 0;
-       return NULL;
+       return name;
 }
 
 /* -------------------------------------------------------------------------- */
@@ -518,8 +589,13 @@ static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
        ret = hidpp_send_fap_command_sync(hidpp, feature_index,
                CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
 
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
        if (ret)
-               return -ret;
+               return ret;
 
        raw_info->x_size = get_unaligned_be16(&params[0]);
        raw_info->y_size = get_unaligned_be16(&params[2]);
@@ -596,6 +672,8 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
 /* Touchpad HID++ devices                                                     */
 /* -------------------------------------------------------------------------- */
 
+#define WTP_MANUAL_RESOLUTION                          39
+
 struct wtp_data {
        struct input_dev *input;
        u16 x_size, y_size;
@@ -611,6 +689,12 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+       if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
+           (field->application == HID_GD_KEYBOARD))
+               return 0;
+
        return -1;
 }
 
@@ -619,6 +703,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
 {
        struct wtp_data *wd = hidpp->private_data;
 
+       if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
+               /* this is the generic hid-input call */
+               return;
+
        __set_bit(EV_ABS, input_dev->evbit);
        __set_bit(EV_KEY, input_dev->evbit);
        __clear_bit(EV_REL, input_dev->evbit);
@@ -634,7 +722,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
 
        input_set_capability(input_dev, EV_KEY, BTN_LEFT);
 
-       __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+       if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)
+               input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+       else
+               __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
 
        input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
                INPUT_MT_DROP_UNUSED);
@@ -676,7 +767,8 @@ static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
        for (i = 0; i < 2; i++)
                wtp_touch_event(wd, &(raw->fingers[i]));
 
-       if (raw->end_of_frame)
+       if (raw->end_of_frame &&
+           !(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
                input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
 
        if (raw->end_of_frame || raw->finger_count <= 2) {
@@ -736,10 +828,25 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
 
        switch (data[0]) {
        case 0x02:
-               if (size < 21)
+               if (size < 2) {
+                       hid_err(hdev, "Received HID report of bad size (%d)",
+                               size);
                        return 1;
-               return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+               }
+               if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
+                       input_event(wd->input, EV_KEY, BTN_LEFT,
+                                       !!(data[1] & 0x01));
+                       input_event(wd->input, EV_KEY, BTN_RIGHT,
+                                       !!(data[1] & 0x02));
+                       input_sync(wd->input);
+                       return 0;
+               } else {
+                       if (size < 21)
+                               return 1;
+                       return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+               }
        case REPORT_ID_HIDPP_LONG:
+               /* size is already checked in hidpp_raw_event. */
                if ((report->fap.feature_index != wd->mt_feature_index) ||
                    (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
                        return 1;
@@ -775,6 +882,8 @@ static int wtp_get_config(struct hidpp_device *hidpp)
        wd->maxcontacts = raw_info.maxcontacts;
        wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
        wd->resolution = raw_info.res;
+       if (!wd->resolution)
+               wd->resolution = WTP_MANUAL_RESOLUTION;
 
        return 0;
 }
@@ -925,7 +1034,6 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
 {
        struct hidpp_device *hidpp = hid_get_drvdata(hdev);
        char *name;
-       u8 name_length;
 
        if (use_unifying)
                /*
@@ -935,7 +1043,7 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
                 */
                name = hidpp_get_unifying_name(hidpp);
        else
-               name = hidpp_get_device_name(hidpp, &name_length);
+               name = hidpp_get_device_name(hidpp);
 
        if (!name)
                hid_err(hdev, "unable to retrieve the name of the device");
@@ -989,7 +1097,6 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
        bool connected = atomic_read(&hidpp->connected);
        struct input_dev *input;
        char *name, *devm_name;
-       u8 name_length;
 
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
                wtp_connect(hdev, connected);
@@ -1016,7 +1123,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
                return;
        }
 
-       name = hidpp_get_device_name(hidpp, &name_length);
+       name = hidpp_get_device_name(hidpp);
        if (!name) {
                hid_err(hdev, "unable to retrieve the name of the device");
        } else {
@@ -1055,7 +1162,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
                ret = wtp_allocate(hdev, id);
                if (ret)
-                       return ret;
+                       goto wtp_allocate_fail;
        }
 
        INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1075,6 +1182,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
                if (!connected) {
                        hid_err(hdev, "Device not connected");
+                       hid_device_io_stop(hdev);
                        goto hid_parse_fail;
                }
 
@@ -1097,6 +1205,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
                connect_mask &= ~HID_CONNECT_HIDINPUT;
 
+       /* Re-enable hidinput for multi-input devices */
+       if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
+               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__);
@@ -1116,6 +1228,7 @@ hid_hw_start_fail:
 hid_parse_fail:
        cancel_work_sync(&hidpp->work);
        mutex_destroy(&hidpp->send_mutex);
+wtp_allocate_fail:
        hid_set_drvdata(hdev, NULL);
        return ret;
 }
@@ -1130,6 +1243,11 @@ static void hidpp_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id hidpp_devices[] = {
+       { /* wireless touchpad */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4011),
+         .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
+                        HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
        { /* wireless touchpad T650 */
          HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
                USB_VENDOR_ID_LOGITECH, 0x4101),
@@ -1138,6 +1256,11 @@ static const struct hid_device_id hidpp_devices[] = {
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_T651),
          .driver_data = HIDPP_QUIRK_CLASS_WTP },
+       { /* Keyboard TK820 */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4102),
+         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
+                        HIDPP_QUIRK_CLASS_WTP },
 
        { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
                USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},