summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew de los Reyes <adlr@chromium.org>2013-02-24 10:45:11 -0800
committerPeter Wu <lekensteyn@gmail.com>2013-09-04 22:28:56 +0200
commite51c28ecec74c7b6dbdaf300c0858608f0ccabd5 (patch)
tree27697cc4601dd77a1bac3077c8541d5aa915c5e3
parent3d4b99fca24772ce8c3f22de6b9c648eff6d8283 (diff)
downloadlinux-e51c28ecec74c7b6dbdaf300c0858608f0ccabd5.tar.gz
CHROMEOS: 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. Signed-off-by: Andrew de los Reyes <adlr@chromium.org> Change-Id: I9c6e3359f08508c54c47e79bec1bbc0121d0b06c Reviewed-on: https://gerrit.chromium.org/gerrit/46683 Reviewed-by: Yufeng Shen <miletus@chromium.org>
-rw-r--r--drivers/hid/hid-core.c3
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-logitech-hidpp.c16
-rw-r--r--drivers/hid/hid-logitech-hidpp.h2
-rw-r--r--drivers/hid/hid-logitech-wtp.c68
5 files changed, 77 insertions, 13 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 36668d1aca8f..e7e69f2f9a81 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1639,6 +1639,9 @@ 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_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
{ 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) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index cad227b443a1..fcdc427551cb 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -569,6 +569,7 @@
#define USB_DEVICE_ID_DINOVO_EDGE 0xc714
#define USB_DEVICE_ID_DINOVO_MINI 0xc71f
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03
+#define USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651 0xb00c
#define USB_VENDOR_ID_LUMIO 0x202e
#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index cbab03e2dce3..586f0f1e3eeb 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -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,
@@ -334,8 +340,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;
diff --git a/drivers/hid/hid-logitech-hidpp.h b/drivers/hid/hid-logitech-hidpp.h
index 66e8ce669fbf..ce127bd6a8df 100644
--- a/drivers/hid/hid-logitech-hidpp.h
+++ b/drivers/hid/hid-logitech-hidpp.h
@@ -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
diff --git a/drivers/hid/hid-logitech-wtp.c b/drivers/hid/hid-logitech-wtp.c
index 79e3aae5edb0..3b12ab490641 100644
--- a/drivers/hid/hid-logitech-wtp.c
+++ b/drivers/hid/hid-logitech-wtp.c
@@ -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, USB_VENDOR_ID_LOGITECH, UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD) },
{HID_DEVICE(BUS_DJ, 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);