CHROMIUM: HID: Support for Logitech T651 Touchpad

This touchpad is similar to the T650, but has a few notable
differences:

- It connects via Bluetooth as opposed to the Unifying Receiver

- The raw data reports are in a unique format appended to normal mouse
  reports. The mouse reports must be ignored. Putting this pad into
  raw data mode will work as the T650 does, but Nestor Lopez Casado of
  Logitech advises against as that mode as it's not officially
  supported.

BUG=chromium:223138
TEST=Manually tested on device. Will add base regression tests.

Change-Id: Ic264d9f6034546fecee8fc387308460e1c4b14a5
Signed-off-by: Dennis Kempin <denniskempin@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/57253
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>

[benzh: 3.14 rebase. Resolved trivial conflicts]
Signed-off-by: Ben Zhang <benzh@chromium.org>
This commit is contained in:
Andrew de los Reyes
2013-02-24 10:45:11 -08:00
committed by Ben Zhang
parent 6327d7cbd6
commit fb9ad22b3a
5 changed files with 75 additions and 13 deletions

View File

@@ -1772,6 +1772,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },

View File

@@ -592,6 +592,7 @@
#define USB_DEVICE_ID_DINOVO_MINI 0xc71f
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03
#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04
#define USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651 0xb00c
#define USB_VENDOR_ID_LUMIO 0x202e
#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006

View File

@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include "hid-ids.h"
#include "hid-logitech-hidpp.h"
MODULE_LICENSE("GPL");
@@ -74,7 +75,9 @@ static int __hidpp_send_report(struct hid_device *hdev,
sizeof(struct hidpp_report),
HID_OUTPUT_REPORT);
return (sent_bytes < 0) ? sent_bytes : 0;
/* It seems that sending via bluetooth can return -EIO even
* when the message is delivered, so we have this hack: */
return (sent_bytes < 0 && sent_bytes != -EIO) ? sent_bytes : 0;
}
static int hidpp_send_message_sync(struct hidpp_device *hidpp_dev,
@@ -154,6 +157,9 @@ int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
memset(&message, 0, sizeof(message));
message.report_id = report_id;
/* If sending to a non-DJ device, device expects 0xff. If sending to
* a DJ device, this device_index will be overwritten by the DJ code: */
message.device_index = 0xff;
message.rap.sub_id = sub_id;
message.rap.reg_address = reg_address;
memcpy(&message.rap.params, params, param_count);
@@ -174,7 +180,7 @@ int hidpp_get_hidpp2_feature_index(struct hidpp_device *hidpp_dev,
params[0] = feature_id >> 8;
params[1] = feature_id & 0xff;
ret = hidpp_send_hidpp2_sync(hidpp_dev,
REPORT_ID_HIDPP_SHORT,
REPORT_ID_HIDPP_LONG,
0,
0,
software_id,
@@ -344,8 +350,10 @@ int hidpp_raw_event(struct hid_device *hdev, struct hid_report *hid_report,
hidpp_print_raw_event("hidpp_raw_event", data, size);
if ((report->report_id != REPORT_ID_HIDPP_LONG) &&
(report->report_id != REPORT_ID_HIDPP_SHORT)) {
if (!(report->report_id == REPORT_ID_HIDPP_LONG ||
report->report_id == REPORT_ID_HIDPP_SHORT ||
(report->report_id == T651_REPORT_TYPE_MOUSE &&
hdev->product == USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651))) {
dbg_hid("hid-logitech-hidpp.c:%s: ignore report_id:%d\n",
__func__, report->report_id);
return 0;

View File

@@ -164,4 +164,6 @@ extern int hidpp_get_hidpp2_feature_index(struct hidpp_device *hidpp_dev,
#define HIDPP_TYPE_PRESENTER 0x06
#define HIDPP_TYPE_RECEIVER 0x07
#define T651_REPORT_TYPE_MOUSE 0x02
#endif

View File

@@ -105,6 +105,8 @@ struct wtp_data {
__u16 next_tracking_id;
__u16 current_slots_used; /* slots = device IDs. Bitmask. */
__u16 prev_slots_used; /* slots = device IDs. Bitmask. */
__u8 fingers_seen_this_frame;
};
static void wtp_touch_event(struct wtp_data *fd,
@@ -114,6 +116,7 @@ static void wtp_touch_event(struct wtp_data *fd,
bool new_finger = !(fd->prev_slots_used & (1 << slot));
fd->current_slots_used |= 1 << slot;
fd->fingers_seen_this_frame++;
input_mt_slot(fd->input, slot);
if (new_finger) {
@@ -148,7 +151,10 @@ static int wtp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
wtp_touch_event(fd, &event->fingers[i]);
}
if (event->end_of_frame || finger_count <= 2) {
if (event->end_of_frame || finger_count < 2 ||
(finger_count == 2 && hidpp_dev->hid_dev->product ==
UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD)) {
u8 fingers_this_frame = fd->fingers_seen_this_frame;
for (i = 0; i < SLOT_COUNT; i++) {
__u16 slot_mask = 1 << i;
bool released = (fd->prev_slots_used & slot_mask) &&
@@ -161,15 +167,15 @@ static int wtp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
}
input_mt_report_pointer_emulation(fd->input, true);
input_report_key(fd->input, BTN_TOOL_FINGER,
finger_count == 1);
fingers_this_frame == 1);
input_report_key(fd->input, BTN_TOOL_DOUBLETAP,
finger_count == 2);
fingers_this_frame == 2);
input_report_key(fd->input, BTN_TOOL_TRIPLETAP,
finger_count == 3);
fingers_this_frame == 3);
input_report_key(fd->input, BTN_TOOL_QUADTAP,
finger_count == 4);
fingers_this_frame == 4);
input_report_key(fd->input, BTN_TOOL_QUINTTAP,
finger_count == 5);
fingers_this_frame == 5);
/* WTP Uses normal mouse reports for button state */
if (hidpp_dev->hid_dev->product !=
UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD)
@@ -179,6 +185,7 @@ static int wtp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
fd->prev_slots_used = fd->current_slots_used;
fd->current_slots_used = 0;
fd->fingers_seen_this_frame = 0;
}
return 1;
}
@@ -219,13 +226,50 @@ static int hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_device,
/* Ensure we get the proper raw data report here. We do this after
the parsing above to avoid mixed declarations and code. */
if (hidpp_report->report_id != REPORT_ID_HIDPP_LONG ||
if ((hidpp_report->report_id != REPORT_ID_HIDPP_LONG ||
hidpp_report->rap.sub_id != fd->mt_feature_index ||
(hidpp_report->rap.reg_address >> 4) != WTP_RAW_XY_EVENT_INDEX) {
(hidpp_report->rap.reg_address >> 4) != WTP_RAW_XY_EVENT_INDEX) &&
!(hidpp_report->report_id == T651_REPORT_TYPE_MOUSE &&
hidpp_device->hid_dev->product == USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651)) {
dbg_hid("Unhandled event type\n");
return 0;
}
if (hidpp_report->report_id == T651_REPORT_TYPE_MOUSE &&
hidpp_device->hid_dev->product == USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651) {
/* Approximate area as (w^2 + h^2) / 2: */
u8 c1_area = ((buf[10] & 0xf) * (buf[10] & 0xf) +
(buf[10] >> 4) * (buf[10] >> 4)) / 2;
u8 c2_area = ((buf[16] & 0xf) * (buf[16] & 0xf) +
(buf[16] >> 4) * (buf[16] >> 4)) / 2;
struct hidpp_touchpad_raw_xy raw_xy_t651 = {
buf[4], /* Timestamp */
{ {
0, /* Contact type */
c1_area > 0, /* Contact status */
(buf[7] << 8) | buf[6], /* X */
(buf[9] << 8) | buf[8], /* Y */
c1_area, /* Z/Force */
c1_area, /* Area */
buf[5] /* Finger ID */
}, {
0, /* Contact type */
c2_area > 0, /* Contact status */
(buf[13] << 8) | buf[12], /* X */
(buf[15] << 8) | buf[14], /* Y */
c2_area, /* Z/Force */
c2_area, /* Area */
buf[11] /* Finger ID */
} },
(buf[5] != 0) + (buf[11] != 0), /* Fingers this frame */
0, /* Proximity */
buf[3] & 1, /* Mechanical button */
0, /* Spurious flag */
(buf[3] >> 7) == 0 /* EOF */
};
raw_xy = raw_xy_t651;
}
dbg_hid("EVT: %d {ty: %d, st: %d, (%d,%d,%d,%d) id:%d} {ty: %d, st: %d, (%d,%d,%d,%d) id:%d} cnt:%d pr:%d but:%d sf:%d eof:%d\n",
raw_xy.timestamp,
raw_xy.fingers[0].contact_type,
@@ -291,6 +335,11 @@ static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev)
remaining bits - reserved */
u8 params = 0x5;
if (hidpp_dev->hid_dev->product == USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651) {
dbg_hid("not going to raw for T651\n");
return 0;
}
ret = hidpp_send_fap_command_sync(hidpp_dev, fd->mt_feature_index,
CMD_TOUCHPAD_SET_RAW_REPORT_STATE, &params, 1, &response);
@@ -414,7 +463,7 @@ static int wtp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_device_io_start(hdev);
/* Get hid++ version number */
ret = hidpp_send_hidpp2_sync(hidpp_device, REPORT_ID_HIDPP_SHORT,
ret = hidpp_send_hidpp2_sync(hidpp_device, REPORT_ID_HIDPP_LONG,
0, 1,
SOFTWARE_ID,
NULL, 0, &response);
@@ -477,6 +526,7 @@ static void wtp_remove(struct hid_device *hdev)
static const struct hid_device_id wtp_devices[] = {
{HID_DEVICE(BUS_DJ, 0, USB_VENDOR_ID_LOGITECH, UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD) },
{HID_DEVICE(BUS_DJ, 0, USB_VENDOR_ID_LOGITECH, UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD_T650) },
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651) },
{ }
};
MODULE_DEVICE_TABLE(hid, wtp_devices);