struct hid_device *hid_dev;
struct mutex send_mutex;
void *send_receive_buf;
+ char *name; /* will never be NULL and should not be freed */
wait_queue_head_t wait;
bool answer_available;
u8 protocol_major;
};
+/* HID++ 1.0 error codes */
#define HIDPP_ERROR 0x8f
#define HIDPP_ERROR_SUCCESS 0x00
#define HIDPP_ERROR_INVALID_SUBID 0x01
#define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
+/* HID++ 2.0 error codes */
+#define HIDPP20_ERROR 0xff
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
}
if (response->report_id == REPORT_ID_HIDPP_SHORT &&
- response->fap.feature_index == HIDPP_ERROR) {
+ response->rap.sub_id == HIDPP_ERROR) {
+ ret = response->rap.params[1];
+ dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+ goto exit;
+ }
+
+ if (response->report_id == REPORT_ID_HIDPP_LONG &&
+ response->fap.feature_index == HIDPP20_ERROR) {
ret = response->fap.params[1];
- dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret);
+ dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
goto exit;
}
static inline bool hidpp_match_error(struct hidpp_report *question,
struct hidpp_report *answer)
{
- return (answer->fap.feature_index == HIDPP_ERROR) &&
+ return ((answer->rap.sub_id == HIDPP_ERROR) ||
+ (answer->fap.feature_index == HIDPP20_ERROR)) &&
(answer->fap.funcindex_clientid == question->fap.feature_index) &&
(answer->fap.params[0] == question->fap.funcindex_clientid);
}
(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;
}
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;
}
/* -------------------------------------------------------------------------- */
switch (data[0]) {
case 0x02:
+ if (size < 2) {
+ hid_err(hdev, "Received HID report of bad size (%d)",
+ size);
+ return 1;
+ }
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;
return 0;
};
-static void wtp_connect(struct hid_device *hdev, bool connected)
+static int wtp_connect(struct hid_device *hdev, bool connected)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd = hidpp->private_data;
int ret;
if (!connected)
- return;
+ return 0;
if (!wd->x_size) {
ret = wtp_get_config(hidpp);
if (ret) {
hid_err(hdev, "Can not get wtp config: %d\n", ret);
- return;
+ return ret;
}
}
- hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
+ return hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
true, true);
}
/*
* If the mutex is locked then we have a pending answer from a
- * previoulsly sent command
+ * previously sent command.
*/
if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
/*
return 1;
}
- if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
- return wtp_raw_event(hidpp->hid_dev, data, size);
-
return 0;
}
u8 *data, int size)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ int ret = 0;
+ /* Generic HID++ processing. */
switch (data[0]) {
case REPORT_ID_HIDPP_LONG:
if (size != HIDPP_REPORT_LONG_LENGTH) {
size);
return 1;
}
- return hidpp_raw_hidpp_event(hidpp, data, size);
+ ret = hidpp_raw_hidpp_event(hidpp, data, size);
+ break;
case REPORT_ID_HIDPP_SHORT:
if (size != HIDPP_REPORT_SHORT_LENGTH) {
hid_err(hdev, "received hid++ report of bad size (%d)",
size);
return 1;
}
- return hidpp_raw_hidpp_event(hidpp, data, size);
+ ret = hidpp_raw_hidpp_event(hidpp, data, size);
+ break;
}
+ /* If no report is available for further processing, skip calling
+ * raw_event of subclasses. */
+ if (ret != 0)
+ return ret;
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_raw_event(hdev, data, size);
{
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");
static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
{
struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (!input_dev)
return NULL;
input_dev->open = hidpp_input_open;
input_dev->close = hidpp_input_close;
- input_dev->name = hdev->name;
+ input_dev->name = hidpp->name;
input_dev->phys = hdev->phys;
input_dev->uniq = hdev->uniq;
input_dev->id.bustype = hdev->bus;
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);
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
+ ret = wtp_connect(hdev, connected);
+ if (ret)
+ return;
+ }
if (!connected || hidpp->delayed_input)
return;
hid_info(hdev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
+ if (!hidpp->name || hidpp->name == hdev->name) {
+ name = hidpp_get_device_name(hidpp);
+ if (!name) {
+ hid_err(hdev,
+ "unable to retrieve the name of the device");
+ return;
+ }
+
+ devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
+ kfree(name);
+ if (!devm_name)
+ return;
+
+ hidpp->name = devm_name;
+ }
+
input = hidpp_allocate_input(hdev);
if (!input) {
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
return;
}
- name = hidpp_get_device_name(hidpp, &name_length);
- if (!name) {
- hid_err(hdev, "unable to retrieve the name of the device");
- } else {
- devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
- if (devm_name)
- input->name = devm_name;
- kfree(name);
- }
-
hidpp_populate_input(hidpp, input, false);
ret = input_register_device(input);
return -ENOMEM;
hidpp->hid_dev = hdev;
+ hidpp->name = hdev->name;
hid_set_drvdata(hdev, hidpp);
hidpp->quirks = id->driver_data;
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;
}
hid_parse_fail:
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
+wtp_allocate_fail:
hid_set_drvdata(hdev, NULL);
return ret;
}