/* 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
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)
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;
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) &&
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;
(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 */
/* -------------------------------------------------------------------------- */
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;
}
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];
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];
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;
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;
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;
}
/* -------------------------------------------------------------------------- */
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(¶ms[0]);
raw_info->y_size = get_unaligned_be16(¶ms[2]);
/* Touchpad HID++ devices */
/* -------------------------------------------------------------------------- */
+#define WTP_MANUAL_RESOLUTION 39
+
struct wtp_data {
struct input_dev *input;
u16 x_size, y_size;
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;
}
{
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);
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);
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) {
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;
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;
}
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
char *name;
- u8 name_length;
if (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");
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);
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 {
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);
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;
}
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__);
hid_parse_fail:
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
+wtp_allocate_fail:
hid_set_drvdata(hdev, NULL);
return ret;
}
}
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),
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)},