2 * HID driver for Logitech Wireless Touchpad device
4 * Copyright (c) 2011 Logitech (c)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Should you need to contact me, the author, you can do so by e-mail send
23 * your message to Benjamin Tissoires <benjamin.tissoires at gmail com>
27 #include <linux/device.h>
28 #include <linux/hid.h>
29 #include <linux/module.h>
30 #include <linux/slab.h>
31 #include <linux/input/mt.h>
33 MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
34 MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
35 MODULE_DESCRIPTION("Logitech Wireless Touchpad");
36 MODULE_LICENSE("GPL");
39 #include "hid-logitech-hidpp.h"
41 #define SOFTWARE_ID 0xB
43 #define CMD_TOUCHPAD_GET_RAW_INFO 0x01
44 #define CMD_TOUCHPAD_GET_RAW_REPORT_STATE 0x11
45 #define CMD_TOUCHPAD_SET_RAW_REPORT_STATE 0x21
46 #define WTP_RAW_XY_FEAT_ID 0x6100
47 #define WTP_RAW_XY_EVENT_INDEX 0
49 #define ORIGIN_LOWER_LEFT 0x1
50 #define ORIGIN_LOWER_RIGHT 0x2
51 #define ORIGIN_UPPER_LEFT 0x3
52 #define ORIGIN_UPPER_RIGHT 0x4
54 #define ORIGIN_IS_HIGH(origin) ((origin - 1) & 2)
55 #define ORIGIN_IS_RIGHT(origin) ((origin - 1) & 1)
57 /* Converts a value in DPI to dots-per-millimeter */
58 #define DPI_TO_DPMM(dpi) (((dpi) * 5) / 127)
62 /* The WTP touchpad doesn't supply a resolution, so we hardcode it in
64 #define WTP_RES 1000 /* DPI */
66 #define CONTACT_STATUS_RELEASED 0
67 #define CONTACT_STATUS_TOUCH 1
68 #define CONTACT_STATUS_HOVER 2
69 #define CONTACT_STATUS_RESERVED 3
71 /* These two structs represent a single raw data input message */
72 struct hidpp_touchpad_raw_xy_finger {
81 struct hidpp_touchpad_raw_xy {
83 struct hidpp_touchpad_raw_xy_finger fingers[2];
84 u8 fingers_this_frame;
85 bool proximity_detection:1;
86 bool mechanical_button:1;
92 struct input_dev *input;
94 /* Properties of the device. Filled by hidpp_touchpad_get_raw_info() */
97 __u8 z_range, area_range;
99 __u16 res; /* points per inch */
101 /* Feature index of the raw data feature */
102 __u8 mt_feature_index;
104 /* For assigning tracking IDs for MT-B protocol. */
105 __u16 next_tracking_id;
106 __u16 current_slots_used; /* slots = device IDs. Bitmask. */
107 __u16 prev_slots_used; /* slots = device IDs. Bitmask. */
110 static void wtp_touch_event(struct wtp_data *fd,
111 struct hidpp_touchpad_raw_xy_finger *touch_report)
113 int slot = touch_report->finger_id - 1;
115 bool new_finger = !(fd->prev_slots_used & (1 << slot));
116 fd->current_slots_used |= 1 << slot;
118 input_mt_slot(fd->input, slot);
120 input_event(fd->input, EV_ABS, ABS_MT_TRACKING_ID,
121 fd->next_tracking_id++);
122 if (fd->next_tracking_id == 0xffff)
123 fd->next_tracking_id = 1;
125 input_mt_report_slot_state(fd->input, MT_TOOL_FINGER, 1);
126 input_event(fd->input, EV_ABS, ABS_MT_POSITION_X,
127 ORIGIN_IS_RIGHT(fd->origin) ?
128 fd->x_size - touch_report->x : touch_report->x);
129 input_event(fd->input, EV_ABS, ABS_MT_POSITION_Y,
130 ORIGIN_IS_HIGH(fd->origin) ?
131 touch_report->y : fd->y_size - touch_report->y);
132 input_event(fd->input, EV_ABS, ABS_MT_PRESSURE, touch_report->area);
135 static int wtp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
136 struct hidpp_touchpad_raw_xy *event)
138 struct wtp_data *fd = (struct wtp_data *)hidpp_dev->driver_data;
139 u8 finger_count = event->fingers_this_frame;
143 if (!hidpp_dev->initialized)
146 for (i = 0; i < 2; i++) {
147 if (event->fingers[i].contact_status != CONTACT_STATUS_RELEASED)
148 wtp_touch_event(fd, &event->fingers[i]);
151 if (event->end_of_frame || finger_count <= 2) {
152 for (i = 0; i < SLOT_COUNT; i++) {
153 __u16 slot_mask = 1 << i;
154 bool released = (fd->prev_slots_used & slot_mask) &&
155 !(fd->current_slots_used & slot_mask);
158 input_mt_slot(fd->input, i);
159 input_event(fd->input, EV_ABS, ABS_MT_TRACKING_ID, -1);
160 input_mt_report_slot_state(fd->input, MT_TOOL_FINGER, 0);
162 input_mt_report_pointer_emulation(fd->input, true);
163 input_report_key(fd->input, BTN_TOOL_FINGER,
165 input_report_key(fd->input, BTN_TOOL_DOUBLETAP,
167 input_report_key(fd->input, BTN_TOOL_TRIPLETAP,
169 input_report_key(fd->input, BTN_TOOL_QUADTAP,
171 input_report_key(fd->input, BTN_TOOL_QUINTTAP,
173 /* WTP Uses normal mouse reports for button state */
174 if (hidpp_dev->hid_dev->product !=
175 UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD)
176 input_report_key(fd->input, BTN_LEFT,
177 event->mechanical_button);
178 input_sync(fd->input);
180 fd->prev_slots_used = fd->current_slots_used;
181 fd->current_slots_used = 0;
187 static int hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_device,
188 struct hidpp_report *hidpp_report)
190 u8 *buf = &hidpp_report->rap.params[0]; /* 4 strips off the DJ header */
191 struct wtp_data *fd = (struct wtp_data *)hidpp_device->driver_data;
193 /* Parse the message into a more convenient struct */
194 struct hidpp_touchpad_raw_xy raw_xy = {
195 (buf[0] << 8) | buf[1], /* Timestamp */
197 buf[2] >> 6, /* Contact type */
198 buf[4] >> 6, /* Contact status */
199 ((buf[2] & 0x3f) << 8) | buf[3], /* X */
200 ((buf[4] & 0x3f) << 8) | buf[5], /* Y */
201 buf[6], /* Z/Force */
203 buf[8] >> 4 /* Finger ID */
205 buf[9] >> 6, /* Contact type */
206 buf[11] >> 6, /* Contact status */
207 ((buf[9] & 0x3f) << 8) | buf[10], /* X */
208 ((buf[11] & 0x3f) << 8) | buf[12], /* Y */
209 buf[13], /* Z/Force */
211 buf[15] >> 4 /* Finger ID */
213 buf[15] & 0xf, /* Fingers this frame */
214 (buf[8] & (1 << 3)) != 0, /* Proximity detection */
215 (buf[8] & (1 << 2)) != 0, /* Mechanical button */
216 (buf[8] & (1 << 1)) != 0, /* Spurious flag */
217 (buf[8] & (1 << 0)) != 0, /* End-of-frame */
220 /* Ensure we get the proper raw data report here. We do this after
221 the parsing above to avoid mixed declarations and code. */
222 if (hidpp_report->report_id != REPORT_ID_HIDPP_LONG ||
223 hidpp_report->rap.sub_id != fd->mt_feature_index ||
224 (hidpp_report->rap.reg_address >> 4) != WTP_RAW_XY_EVENT_INDEX) {
225 dbg_hid("Unhandled event type\n");
229 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",
231 raw_xy.fingers[0].contact_type,
232 raw_xy.fingers[0].contact_status,
236 raw_xy.fingers[0].area,
237 raw_xy.fingers[0].finger_id,
238 raw_xy.fingers[1].contact_type,
239 raw_xy.fingers[1].contact_status,
243 raw_xy.fingers[1].area,
244 raw_xy.fingers[1].finger_id,
245 raw_xy.fingers_this_frame,
246 raw_xy.proximity_detection,
247 raw_xy.mechanical_button,
248 raw_xy.spurious_flag,
249 raw_xy.end_of_frame);
250 return wtp_touchpad_raw_xy_event(hidpp_device, &raw_xy);
253 static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp_dev)
255 struct wtp_data *fd = hidpp_dev->driver_data;
256 struct hidpp_report response;
258 u8 *params = (u8 *)response.fap.params;
260 ret = hidpp_send_fap_command_sync(hidpp_dev, fd->mt_feature_index,
261 CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
266 fd->x_size = (params[0] << 8) | params[1];
267 fd->y_size = (params[2] << 8) | params[3];
268 fd->z_range = params[4];
269 fd->area_range = params[5];
270 fd->maxcontacts = params[7];
271 fd->origin = params[8];
272 fd->res = (params[13] << 8) | params[14];
279 static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev)
281 struct wtp_data *fd = hidpp_dev->driver_data;
282 struct hidpp_report response;
287 0x02 - 16bit Z, no area
288 0x04 - enhanced sensitivity
289 0x08 - width, height instead of area
290 0x10 - send raw + gestures (degrades smoothness)
291 remaining bits - reserved */
294 ret = hidpp_send_fap_command_sync(hidpp_dev, fd->mt_feature_index,
295 CMD_TOUCHPAD_SET_RAW_REPORT_STATE, ¶ms, 1, &response);
303 static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
304 struct hid_field *field, struct hid_usage *usage,
305 unsigned long **bit, int *max)
307 struct hidpp_device *hidpp_dev = hid_get_drvdata(hdev);
308 struct wtp_data *fd = (struct wtp_data *)hidpp_dev->driver_data;
309 struct input_dev *input = hi->input;
312 dbg_hid("%s:\n", __func__);
314 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
317 fd->input = hi->input;
319 __set_bit(BTN_TOUCH, input->keybit);
320 __set_bit(BTN_TOOL_FINGER, input->keybit);
321 __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
322 __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
323 __set_bit(BTN_TOOL_QUADTAP, input->keybit);
324 __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
326 __set_bit(EV_ABS, input->evbit);
328 input_mt_init_slots(input, SLOT_COUNT);
330 input_set_capability(input, EV_KEY, BTN_TOUCH);
332 input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
333 input_set_abs_params(input, ABS_MT_POSITION_X, 0, fd->x_size, 0, 0);
334 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, fd->y_size, 0, 0);
335 input_set_abs_params(input, ABS_X, 0, fd->x_size, 0, 0);
336 input_set_abs_params(input, ABS_Y, 0, fd->y_size, 0, 0);
338 res_mm = DPI_TO_DPMM(fd->res);
340 input_abs_set_res(input, ABS_MT_POSITION_X, res_mm);
341 input_abs_set_res(input, ABS_MT_POSITION_Y, res_mm);
342 input_abs_set_res(input, ABS_X, res_mm);
343 input_abs_set_res(input, ABS_Y, res_mm);
348 static void wtp_connect_change(struct hidpp_device *hidpp_dev, bool connected)
350 dbg_hid("%s: connected:%d\n", __func__, connected);
351 if (connected && hidpp_dev->initialized)
352 hidpp_touchpad_set_raw_report_state(hidpp_dev);
355 static int wtp_device_init(struct hidpp_device *hidpp_dev)
359 dbg_hid("%s\n", __func__);
361 ret = hidpp_touchpad_set_raw_report_state(hidpp_dev);
364 hid_err(hidpp_dev->hid_dev, "unable to set to raw report mode. "
365 "The device may not be in range.\n");
369 ret = hidpp_touchpad_get_raw_info(hidpp_dev);
373 static int wtp_probe(struct hid_device *hdev, const struct hid_device_id *id)
375 struct wtp_data *fd = NULL;
376 struct hidpp_device *hidpp_device = NULL;
378 struct hidpp_report response;
380 dbg_hid("%s\n", __func__);
382 hidpp_device = kzalloc(sizeof(struct hidpp_device), GFP_KERNEL);
384 hid_err(hdev, "cannot allocate hidpp_device\n");
386 goto hidpp_alloc_failed;
389 fd = kzalloc(sizeof(struct wtp_data), GFP_KERNEL);
391 hid_err(hdev, "cannot allocate wtp Touch data\n");
393 goto fd_alloc_failed;
395 fd->next_tracking_id = 1;
397 hidpp_device->driver_data = (void *)fd;
398 hid_set_drvdata(hdev, hidpp_device);
400 hidpp_device->connect_change = wtp_connect_change;
402 ret = hid_parse(hdev);
408 ret = hidpp_init(hidpp_device, hdev);
414 hid_device_io_start(hdev);
416 /* Get hid++ version number */
417 ret = hidpp_send_hidpp2_sync(hidpp_device, REPORT_ID_HIDPP_SHORT,
422 dbg_hid("send root cmd returned: %d", ret);
427 dbg_hid("HID++ version: %d.%d\n", response.rap.params[0],
428 response.rap.params[1]);
430 /* TODO(adlr): Consider requiring a specific/minimum HID++ version. */
432 ret = hidpp_get_hidpp2_feature_index(hidpp_device,
435 &fd->mt_feature_index);
437 dbg_hid("Get raw_xy feature idx failed: %d", ret);
442 wtp_device_init(hidpp_device);
444 hid_device_io_stop(hdev);
446 hidpp_device->raw_event = hidpp_touchpad_raw_xy_event;
448 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
457 hid_set_drvdata(hdev, NULL);
465 static void wtp_remove(struct hid_device *hdev)
467 struct hidpp_device *hidpp_dev = hid_get_drvdata(hdev);
468 struct wtp_data *fd = hidpp_dev->driver_data;
469 dbg_hid("%s\n", __func__);
471 hidpp_remove(hidpp_dev);
474 hid_set_drvdata(hdev, NULL);
477 static const struct hid_device_id wtp_devices[] = {
478 {HID_DEVICE(BUS_DJ, USB_VENDOR_ID_LOGITECH, UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD) },
479 {HID_DEVICE(BUS_DJ, USB_VENDOR_ID_LOGITECH, UNIFYING_DEVICE_ID_WIRELESS_TOUCHPAD_T650) },
482 MODULE_DEVICE_TABLE(hid, wtp_devices);
484 static struct hid_driver wtp_driver = {
486 .id_table = wtp_devices,
488 .remove = wtp_remove,
489 .input_mapping = wtp_input_mapping,
490 .raw_event = hidpp_raw_event,
493 static int __init wtp_init(void)
495 return hid_register_driver(&wtp_driver);
498 static void __exit wtp_exit(void)
500 hid_unregister_driver(&wtp_driver);
503 module_init(wtp_init);
504 module_exit(wtp_exit);