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-08-26 15:45:29 +0200
commit45762564c360cbebd84fdfc9fb1f30b7424a3787 (patch)
treecd0058f7bf8ab19f8735523e18c7d594251aacf9
parent1c762c5f61c58a0091c6f38467dd2274fe9b27ee (diff)
downloadlinux-45762564c360cbebd84fdfc9fb1f30b7424a3787.tar.gz
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>
-rw-r--r--drivers/hid/hid-core.c1
-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, 75 insertions, 13 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index e46b63ba9022..0acea72aee47 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1641,6 +1641,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) },
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 286285546dfe..bf662a4ca7e6 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,
@@ -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;
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 8d623ee108dc..dfe352720eff 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, 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);