+ 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;
+};
+
+/* Bit Operations Helper */
+static u16 make_u16(u8 high, u8 low)
+{
+ return (high << 8) | low;
+}
+static u8 low_nib(u8 val)
+{
+ return val & 0xf;
+}
+static u8 high_nib(u8 val)
+{
+ return val >> 4;
+}
+static bool get_bit(u8 mask, u8 idx)
+{
+ return mask & (1 << idx);
+}
+
+/*
+ Helper methods for parsing mouse events. Some devices
+ use mouse events to report buttons.
+*/
+
+#define GENERIC_EVENT_MOUSE 0x02
+
+static void generic_parse_mouse_button(struct hidpp_report *report,
+ struct wtp_event *event) {
+ u8 *raw = (u8 *)report;
+ event->has_buttons = BUTTON_LEFT_MASK | BUTTON_RIGHT_MASK |
+ BUTTON_MIDDLE_MASK;
+ event->buttons = raw[1] & event->has_buttons;
+}
+
+/*
+ TouchPadRawXY (TPRXY) Feature
+*/
+
+#define TPRXY_FEATURE 0x6100
+#define TPRXY_CMD_GET_TOUCHPAD_INFO (0x00 & SOFTWARE_ID)
+#define TPRXY_CMD_GET_RAW_REPORT_STATE (0x10 & SOFTWARE_ID)
+#define TPRXY_CMD_SET_RAW_REPORT_STATE (0x20 & SOFTWARE_ID)
+#define TPRXY_EVENT_TOUCHPAD_RAW_TOUCH_POINTS 0x00
+
+#define TPRXY_FORMAT_RAW 0x01
+#define TPRXY_FORMAT_MOUSE_EXTENDED 0x02
+
+#define TPRXY_DEFAULT_RES 1000 /* DPI */
+
+#define TPRXY_SLOTS_PER_FRAME 2
+
+/* Initialize TouchPadRawXY feature:
+ Set touchpad into raw mode (except for T651, which allows for raw touch
+ data appended to mouse events). */
+static int tprxy_init(struct hidpp_device *hidpp_dev)
+{
+ struct wtp_data *fd = hidpp_dev->driver_data;
+ struct hidpp_report response;
+ int ret;
+ u8 params;
+
+ dbg_hid("%s\n", __func__);
+
+ if (hidpp_dev->hid_dev->product ==
+ USB_DEVICE_ID_WIRELESS_TOUCHPAD_T651) {
+ params = 0x4; /* enhanced sensitivity */
+ fd->feature.event_format = TPRXY_FORMAT_MOUSE_EXTENDED;
+ } else {
+ params = 0x5; /* enhanced sensitivity + raw */
+ fd->feature.event_format = TPRXY_FORMAT_RAW;
+ }
+
+ ret = hidpp_send_fap_command_sync(hidpp_dev, fd->feature.index,
+ TPRXY_CMD_SET_RAW_REPORT_STATE, ¶ms, 1, &response);
+
+ return -ret;
+}
+
+/* Probe TouchPadRawXY feature */
+static int tprxy_probe(struct hidpp_device *hidpp_dev,
+ struct wtp_device_info *info)
+{
+ struct wtp_data *fd = hidpp_dev->driver_data;
+ struct hidpp_report response;
+ int ret;
+ u16 res;
+ u8 *params = (u8 *)response.fap.params;
+
+ dbg_hid("%s\n", __func__);
+
+ ret = hidpp_send_fap_command_sync(hidpp_dev, fd->feature.index,
+ TPRXY_CMD_GET_TOUCHPAD_INFO, NULL, 0, &response);
+
+ if (ret)
+ return -ret;
+
+ info->abs_max_x = make_u16(params[0], params[1]);
+ info->abs_max_y = make_u16(params[2], params[3]);
+ info->abs_max_pressure = params[5];
+ info->max_contacts = params[7];
+ info->origin = params[8];
+
+ res = make_u16(params[13], params[14]);
+ if (!res)
+ res = TPRXY_DEFAULT_RES;
+ info->abs_res_x = res;
+ info->abs_res_y = res;
+ return ret;
+}
+
+/* Parse other events while using TouchPadRawXY feature:
+ Mouse events might still be received in the following cases:
+ - Touchpad with separate buttons send mouse events on click
+ - Touchpad using extended mouse events send touch data as
+ mouse events */
+static int tprxy_parse_other_event(struct wtp_data *wtp,
+ struct hidpp_report *report,
+ struct wtp_event *event) {
+ int i;
+ u8 *buf = &report->rap.params[0];
+
+ dbg_hid("%s\n", __func__);
+
+ if (report->report_id != GENERIC_EVENT_MOUSE)
+ return -1;
+
+ generic_parse_mouse_button(report, event);
+
+ if (wtp->feature.event_format != TPRXY_FORMAT_MOUSE_EXTENDED)
+ return 0;
+
+ for (i = 0; i < TPRXY_SLOTS_PER_FRAME; ++i) {
+ struct wtp_event_finger *finger = &event->fingers[i];
+ u8 *raw = buf + (5 + i * 6);
+ u8 width = low_nib(raw[5]);
+ u8 height = high_nib(raw[5]);
+
+ finger->pressure = (width * width + height * height) / 2;
+ finger->status = finger->pressure > 0;
+ finger->abs_x = make_u16(raw[2], raw[1]);
+ finger->abs_y = make_u16(raw[4], raw[3]);
+ finger->id = raw[0];
+ }
+ event->end_of_frame = !get_bit(buf[3], 7);
+ event->has_abs = true;
+
+ return 0;
+}
+
+/* Parse TouchPadRawXY events */
+static int tprxy_parse_feature_event(struct wtp_data *wtp,
+ struct hidpp_report *report,
+ struct wtp_event *event) {
+ int i;
+ u8 *buf = &report->rap.params[0];
+ u8 fingers_this_frame;
+
+ dbg_hid("%s\n", __func__);
+
+ if (wtp->feature.event_format != TPRXY_FORMAT_RAW)
+ return -1;
+
+ for (i = 0; i < TPRXY_SLOTS_PER_FRAME; ++i) {
+ u8 *raw = buf + (2 + i * 7);
+ event->fingers[i].status = raw[0];
+ event->fingers[i].abs_x = make_u16(raw[0] & 0x3f, raw[1]);
+ event->fingers[i].abs_y = make_u16(raw[2] & 0x3f, raw[3]);
+ event->fingers[i].pressure = raw[5];
+ event->fingers[i].id = high_nib(raw[6]);
+ }
+ event->buttons = get_bit(buf[8], 2);
+ event->end_of_frame = get_bit(buf[8], 0);
+
+ /* For single event frames, the end of frame flag is implied. */
+ fingers_this_frame = low_nib(buf[15]);
+ if (fingers_this_frame <= TPRXY_SLOTS_PER_FRAME)
+ event->end_of_frame = true;