1b9b347e9SVladimir Kondratyev /*- 2cb022db8SVladimir Kondratyev * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3cb022db8SVladimir Kondratyev * 4cb022db8SVladimir Kondratyev * Copyright (c) 2014-2020 Vladimir Kondratyev <wulf@FreeBSD.org> 5b9b347e9SVladimir Kondratyev * 6b9b347e9SVladimir Kondratyev * Redistribution and use in source and binary forms, with or without 7b9b347e9SVladimir Kondratyev * modification, are permitted provided that the following conditions 8b9b347e9SVladimir Kondratyev * are met: 9b9b347e9SVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright 10b9b347e9SVladimir Kondratyev * notice, this list of conditions and the following disclaimer. 11b9b347e9SVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright 12b9b347e9SVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the 13b9b347e9SVladimir Kondratyev * documentation and/or other materials provided with the distribution. 14b9b347e9SVladimir Kondratyev * 15b9b347e9SVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16b9b347e9SVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17b9b347e9SVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18b9b347e9SVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19b9b347e9SVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20b9b347e9SVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21b9b347e9SVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22b9b347e9SVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23b9b347e9SVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24b9b347e9SVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25b9b347e9SVladimir Kondratyev * SUCH DAMAGE. 26b9b347e9SVladimir Kondratyev */ 27b9b347e9SVladimir Kondratyev 28b9b347e9SVladimir Kondratyev #include <sys/cdefs.h> 29b9b347e9SVladimir Kondratyev __FBSDID("$FreeBSD$"); 30b9b347e9SVladimir Kondratyev 31b9b347e9SVladimir Kondratyev /* 32cb022db8SVladimir Kondratyev * MS Windows 7/8/10 compatible HID Multi-touch Device driver. 33b9b347e9SVladimir Kondratyev * https://msdn.microsoft.com/en-us/library/windows/hardware/jj151569(v=vs.85).aspx 34cb022db8SVladimir Kondratyev * http://download.microsoft.com/download/7/d/d/7dd44bb7-2a7a-4505-ac1c-7227d3d96d5b/hid-over-i2c-protocol-spec-v1-0.docx 35b9b347e9SVladimir Kondratyev * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt 36b9b347e9SVladimir Kondratyev */ 37b9b347e9SVladimir Kondratyev 38*5cc21ab9SVladimir Kondratyev #include "opt_hid.h" 39*5cc21ab9SVladimir Kondratyev 40b9b347e9SVladimir Kondratyev #include <sys/param.h> 41b9b347e9SVladimir Kondratyev #include <sys/bus.h> 42b9b347e9SVladimir Kondratyev #include <sys/kernel.h> 43b9b347e9SVladimir Kondratyev #include <sys/lock.h> 44b9b347e9SVladimir Kondratyev #include <sys/malloc.h> 45b9b347e9SVladimir Kondratyev #include <sys/module.h> 46b9b347e9SVladimir Kondratyev #include <sys/mutex.h> 47b9b347e9SVladimir Kondratyev #include <sys/sysctl.h> 48b9b347e9SVladimir Kondratyev #include <sys/systm.h> 49b9b347e9SVladimir Kondratyev 50b9b347e9SVladimir Kondratyev #include <dev/evdev/evdev.h> 51b9b347e9SVladimir Kondratyev #include <dev/evdev/input.h> 52b9b347e9SVladimir Kondratyev 53cb022db8SVladimir Kondratyev #define HID_DEBUG_VAR hmt_debug 54cb022db8SVladimir Kondratyev #include <dev/hid/hid.h> 55cb022db8SVladimir Kondratyev #include <dev/hid/hidbus.h> 56cb022db8SVladimir Kondratyev #include <dev/hid/hidquirk.h> 57b9b347e9SVladimir Kondratyev 58cb022db8SVladimir Kondratyev #include <dev/hid/hconf.h> 59cb022db8SVladimir Kondratyev 60cb022db8SVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, hmt, CTLFLAG_RW, 0, 61cb022db8SVladimir Kondratyev "MSWindows 7/8/10 compatible HID Multi-touch Device"); 62cb022db8SVladimir Kondratyev #ifdef HID_DEBUG 63cb022db8SVladimir Kondratyev static int hmt_debug = 0; 64cb022db8SVladimir Kondratyev SYSCTL_INT(_hw_hid_hmt, OID_AUTO, debug, CTLFLAG_RWTUN, 65cb022db8SVladimir Kondratyev &hmt_debug, 1, "Debug level"); 66b9b347e9SVladimir Kondratyev #endif 67cb022db8SVladimir Kondratyev static bool hmt_timestamps = 0; 68cb022db8SVladimir Kondratyev SYSCTL_BOOL(_hw_hid_hmt, OID_AUTO, timestamps, CTLFLAG_RDTUN, 69cb022db8SVladimir Kondratyev &hmt_timestamps, 1, "Enable hardware timestamp reporting"); 70b9b347e9SVladimir Kondratyev 71cb022db8SVladimir Kondratyev #define HMT_BTN_MAX 8 /* Number of buttons supported */ 72b9b347e9SVladimir Kondratyev 73cb022db8SVladimir Kondratyev enum hmt_type { 74cb022db8SVladimir Kondratyev HMT_TYPE_UNKNOWN = 0, /* HID report descriptor is not probed */ 75cb022db8SVladimir Kondratyev HMT_TYPE_UNSUPPORTED, /* Repdescr does not belong to MT device */ 76cb022db8SVladimir Kondratyev HMT_TYPE_TOUCHPAD, 77cb022db8SVladimir Kondratyev HMT_TYPE_TOUCHSCREEN, 78b9b347e9SVladimir Kondratyev }; 79b9b347e9SVladimir Kondratyev 80b9b347e9SVladimir Kondratyev enum { 81cb022db8SVladimir Kondratyev HMT_TIP_SWITCH, 82cb022db8SVladimir Kondratyev #define HMT_SLOT HMT_TIP_SWITCH 83cb022db8SVladimir Kondratyev HMT_WIDTH, 84cb022db8SVladimir Kondratyev #define HMT_MAJOR HMT_WIDTH 85cb022db8SVladimir Kondratyev HMT_HEIGHT, 86cb022db8SVladimir Kondratyev #define HMT_MINOR HMT_HEIGHT 87cb022db8SVladimir Kondratyev HMT_ORIENTATION, 88cb022db8SVladimir Kondratyev HMT_X, 89cb022db8SVladimir Kondratyev HMT_Y, 90cb022db8SVladimir Kondratyev HMT_CONTACTID, 91cb022db8SVladimir Kondratyev HMT_PRESSURE, 92cb022db8SVladimir Kondratyev HMT_IN_RANGE, 93cb022db8SVladimir Kondratyev HMT_CONFIDENCE, 94cb022db8SVladimir Kondratyev HMT_TOOL_X, 95cb022db8SVladimir Kondratyev HMT_TOOL_Y, 96cb022db8SVladimir Kondratyev HMT_N_USAGES, 97b9b347e9SVladimir Kondratyev }; 98b9b347e9SVladimir Kondratyev 99cb022db8SVladimir Kondratyev #define HMT_NO_CODE (ABS_MAX + 10) 100cb022db8SVladimir Kondratyev #define HMT_NO_USAGE -1 101b9b347e9SVladimir Kondratyev 102cb022db8SVladimir Kondratyev struct hmt_hid_map_item { 103b9b347e9SVladimir Kondratyev char name[5]; 104b9b347e9SVladimir Kondratyev int32_t usage; /* HID usage */ 105b9b347e9SVladimir Kondratyev uint32_t code; /* Evdev event code */ 106b9b347e9SVladimir Kondratyev bool required; /* Required for MT Digitizers */ 107b9b347e9SVladimir Kondratyev }; 108b9b347e9SVladimir Kondratyev 109cb022db8SVladimir Kondratyev static const struct hmt_hid_map_item hmt_hid_map[HMT_N_USAGES] = { 110cb022db8SVladimir Kondratyev [HMT_TIP_SWITCH] = { /* HMT_SLOT */ 111b9b347e9SVladimir Kondratyev .name = "TIP", 112b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH), 113b9b347e9SVladimir Kondratyev .code = ABS_MT_SLOT, 114b9b347e9SVladimir Kondratyev .required = true, 115b9b347e9SVladimir Kondratyev }, 116cb022db8SVladimir Kondratyev [HMT_WIDTH] = { /* HMT_MAJOR */ 117b9b347e9SVladimir Kondratyev .name = "WDTH", 118b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH), 119b9b347e9SVladimir Kondratyev .code = ABS_MT_TOUCH_MAJOR, 120b9b347e9SVladimir Kondratyev .required = false, 121b9b347e9SVladimir Kondratyev }, 122cb022db8SVladimir Kondratyev [HMT_HEIGHT] = { /* HMT_MINOR */ 123b9b347e9SVladimir Kondratyev .name = "HGHT", 124b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT), 125b9b347e9SVladimir Kondratyev .code = ABS_MT_TOUCH_MINOR, 126b9b347e9SVladimir Kondratyev .required = false, 127b9b347e9SVladimir Kondratyev }, 128cb022db8SVladimir Kondratyev [HMT_ORIENTATION] = { 129b9b347e9SVladimir Kondratyev .name = "ORIE", 130cb022db8SVladimir Kondratyev .usage = HMT_NO_USAGE, 131b9b347e9SVladimir Kondratyev .code = ABS_MT_ORIENTATION, 132b9b347e9SVladimir Kondratyev .required = false, 133b9b347e9SVladimir Kondratyev }, 134cb022db8SVladimir Kondratyev [HMT_X] = { 135b9b347e9SVladimir Kondratyev .name = "X", 136b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 137b9b347e9SVladimir Kondratyev .code = ABS_MT_POSITION_X, 138b9b347e9SVladimir Kondratyev .required = true, 139b9b347e9SVladimir Kondratyev }, 140cb022db8SVladimir Kondratyev [HMT_Y] = { 141b9b347e9SVladimir Kondratyev .name = "Y", 142b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 143b9b347e9SVladimir Kondratyev .code = ABS_MT_POSITION_Y, 144b9b347e9SVladimir Kondratyev .required = true, 145b9b347e9SVladimir Kondratyev }, 146cb022db8SVladimir Kondratyev [HMT_CONTACTID] = { 147b9b347e9SVladimir Kondratyev .name = "C_ID", 148b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID), 149b9b347e9SVladimir Kondratyev .code = ABS_MT_TRACKING_ID, 150b9b347e9SVladimir Kondratyev .required = true, 151b9b347e9SVladimir Kondratyev }, 152cb022db8SVladimir Kondratyev [HMT_PRESSURE] = { 153b9b347e9SVladimir Kondratyev .name = "PRES", 154b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_PRESSURE), 155b9b347e9SVladimir Kondratyev .code = ABS_MT_PRESSURE, 156b9b347e9SVladimir Kondratyev .required = false, 157b9b347e9SVladimir Kondratyev }, 158cb022db8SVladimir Kondratyev [HMT_IN_RANGE] = { 159b9b347e9SVladimir Kondratyev .name = "RANG", 160b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_IN_RANGE), 161b9b347e9SVladimir Kondratyev .code = ABS_MT_DISTANCE, 162b9b347e9SVladimir Kondratyev .required = false, 163b9b347e9SVladimir Kondratyev }, 164cb022db8SVladimir Kondratyev [HMT_CONFIDENCE] = { 165b9b347e9SVladimir Kondratyev .name = "CONF", 166b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE), 167cb022db8SVladimir Kondratyev .code = HMT_NO_CODE, 168b9b347e9SVladimir Kondratyev .required = false, 169b9b347e9SVladimir Kondratyev }, 170cb022db8SVladimir Kondratyev [HMT_TOOL_X] = { /* Shares HID usage with HMT_X */ 171b9b347e9SVladimir Kondratyev .name = "TL_X", 172b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 173b9b347e9SVladimir Kondratyev .code = ABS_MT_TOOL_X, 174b9b347e9SVladimir Kondratyev .required = false, 175b9b347e9SVladimir Kondratyev }, 176cb022db8SVladimir Kondratyev [HMT_TOOL_Y] = { /* Shares HID usage with HMT_Y */ 177b9b347e9SVladimir Kondratyev .name = "TL_Y", 178b9b347e9SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 179b9b347e9SVladimir Kondratyev .code = ABS_MT_TOOL_Y, 180b9b347e9SVladimir Kondratyev .required = false, 181b9b347e9SVladimir Kondratyev }, 182b9b347e9SVladimir Kondratyev }; 183b9b347e9SVladimir Kondratyev 184cb022db8SVladimir Kondratyev struct hmt_softc { 185b9b347e9SVladimir Kondratyev device_t dev; 186cb022db8SVladimir Kondratyev enum hmt_type type; 187b9b347e9SVladimir Kondratyev 188cb022db8SVladimir Kondratyev struct hid_absinfo ai[HMT_N_USAGES]; 189cb022db8SVladimir Kondratyev struct hid_location locs[MAX_MT_SLOTS][HMT_N_USAGES]; 190b9b347e9SVladimir Kondratyev struct hid_location cont_count_loc; 191cb022db8SVladimir Kondratyev struct hid_location btn_loc[HMT_BTN_MAX]; 192b9b347e9SVladimir Kondratyev struct hid_location int_btn_loc; 193b9b347e9SVladimir Kondratyev struct hid_location scan_time_loc; 194b9b347e9SVladimir Kondratyev int32_t scan_time_max; 195b9b347e9SVladimir Kondratyev int32_t scan_time; 196b9b347e9SVladimir Kondratyev int32_t timestamp; 197b9b347e9SVladimir Kondratyev bool touch; 198b9b347e9SVladimir Kondratyev bool prev_touch; 199b9b347e9SVladimir Kondratyev 200b9b347e9SVladimir Kondratyev struct evdev_dev *evdev; 201b9b347e9SVladimir Kondratyev 202cb022db8SVladimir Kondratyev uint32_t slot_data[HMT_N_USAGES]; 203cb022db8SVladimir Kondratyev uint8_t caps[howmany(HMT_N_USAGES, 8)]; 204cb022db8SVladimir Kondratyev uint8_t buttons[howmany(HMT_BTN_MAX, 8)]; 205b9b347e9SVladimir Kondratyev uint32_t nconts_per_report; 206b9b347e9SVladimir Kondratyev uint32_t nconts_todo; 207b9b347e9SVladimir Kondratyev uint8_t report_id; 208b9b347e9SVladimir Kondratyev uint32_t max_button; 209b9b347e9SVladimir Kondratyev bool has_int_button; 210b9b347e9SVladimir Kondratyev bool is_clickpad; 211b9b347e9SVladimir Kondratyev bool do_timestamps; 212*5cc21ab9SVladimir Kondratyev #ifdef IICHID_SAMPLING 213cb022db8SVladimir Kondratyev bool iichid_sampling; 214*5cc21ab9SVladimir Kondratyev #endif 215b9b347e9SVladimir Kondratyev 216b9b347e9SVladimir Kondratyev struct hid_location cont_max_loc; 217b9b347e9SVladimir Kondratyev uint32_t cont_max_rlen; 218b9b347e9SVladimir Kondratyev uint8_t cont_max_rid; 219b9b347e9SVladimir Kondratyev struct hid_location btn_type_loc; 220b9b347e9SVladimir Kondratyev uint32_t btn_type_rlen; 221b9b347e9SVladimir Kondratyev uint8_t btn_type_rid; 222b9b347e9SVladimir Kondratyev uint32_t thqa_cert_rlen; 223b9b347e9SVladimir Kondratyev uint8_t thqa_cert_rid; 224b9b347e9SVladimir Kondratyev }; 225b9b347e9SVladimir Kondratyev 226cb022db8SVladimir Kondratyev #define HMT_FOREACH_USAGE(caps, usage) \ 227cb022db8SVladimir Kondratyev for ((usage) = 0; (usage) < HMT_N_USAGES; ++(usage)) \ 228b9b347e9SVladimir Kondratyev if (isset((caps), (usage))) 229b9b347e9SVladimir Kondratyev 230cb022db8SVladimir Kondratyev static enum hmt_type hmt_hid_parse(struct hmt_softc *, const void *, 231cb022db8SVladimir Kondratyev hid_size_t, uint32_t, uint8_t); 232cb022db8SVladimir Kondratyev static int hmt_set_input_mode(struct hmt_softc *, enum hconf_input_mode); 233b9b347e9SVladimir Kondratyev 234cb022db8SVladimir Kondratyev static hid_intr_t hmt_intr; 235b9b347e9SVladimir Kondratyev 236cb022db8SVladimir Kondratyev static device_probe_t hmt_probe; 237cb022db8SVladimir Kondratyev static device_attach_t hmt_attach; 238cb022db8SVladimir Kondratyev static device_detach_t hmt_detach; 239b9b347e9SVladimir Kondratyev 240cb022db8SVladimir Kondratyev static evdev_open_t hmt_ev_open; 241cb022db8SVladimir Kondratyev static evdev_close_t hmt_ev_close; 242b9b347e9SVladimir Kondratyev 243cb022db8SVladimir Kondratyev static const struct evdev_methods hmt_evdev_methods = { 244cb022db8SVladimir Kondratyev .ev_open = &hmt_ev_open, 245cb022db8SVladimir Kondratyev .ev_close = &hmt_ev_close, 246b9b347e9SVladimir Kondratyev }; 247b9b347e9SVladimir Kondratyev 248cb022db8SVladimir Kondratyev static const struct hid_device_id hmt_devs[] = { 249cb022db8SVladimir Kondratyev { HID_TLC(HUP_DIGITIZERS, HUD_TOUCHSCREEN) }, 250cb022db8SVladimir Kondratyev { HID_TLC(HUP_DIGITIZERS, HUD_TOUCHPAD) }, 251b9b347e9SVladimir Kondratyev }; 252b9b347e9SVladimir Kondratyev 253b9b347e9SVladimir Kondratyev static int 254cb022db8SVladimir Kondratyev hmt_ev_close(struct evdev_dev *evdev) 255b9b347e9SVladimir Kondratyev { 256cb022db8SVladimir Kondratyev return (hidbus_intr_stop(evdev_get_softc(evdev))); 257cb022db8SVladimir Kondratyev } 258cb022db8SVladimir Kondratyev 259cb022db8SVladimir Kondratyev static int 260cb022db8SVladimir Kondratyev hmt_ev_open(struct evdev_dev *evdev) 261cb022db8SVladimir Kondratyev { 262cb022db8SVladimir Kondratyev return (hidbus_intr_start(evdev_get_softc(evdev))); 263cb022db8SVladimir Kondratyev } 264cb022db8SVladimir Kondratyev 265cb022db8SVladimir Kondratyev static int 266cb022db8SVladimir Kondratyev hmt_probe(device_t dev) 267cb022db8SVladimir Kondratyev { 268cb022db8SVladimir Kondratyev struct hmt_softc *sc = device_get_softc(dev); 269b9b347e9SVladimir Kondratyev void *d_ptr; 270cb022db8SVladimir Kondratyev hid_size_t d_len; 271b9b347e9SVladimir Kondratyev int err; 272b9b347e9SVladimir Kondratyev 273cb022db8SVladimir Kondratyev err = HIDBUS_LOOKUP_DRIVER_INFO(dev, hmt_devs); 274cb022db8SVladimir Kondratyev if (err != 0) 275cb022db8SVladimir Kondratyev return (err); 276cb022db8SVladimir Kondratyev 277cb022db8SVladimir Kondratyev err = hid_get_report_descr(dev, &d_ptr, &d_len); 278cb022db8SVladimir Kondratyev if (err != 0) { 279cb022db8SVladimir Kondratyev device_printf(dev, "could not retrieve report descriptor from " 280cb022db8SVladimir Kondratyev "device: %d\n", err); 281b9b347e9SVladimir Kondratyev return (ENXIO); 282b9b347e9SVladimir Kondratyev } 283b9b347e9SVladimir Kondratyev 284cb022db8SVladimir Kondratyev /* Check if report descriptor belongs to a HID multitouch device */ 285cb022db8SVladimir Kondratyev if (sc->type == HMT_TYPE_UNKNOWN) 286cb022db8SVladimir Kondratyev sc->type = hmt_hid_parse(sc, d_ptr, d_len, 287cb022db8SVladimir Kondratyev hidbus_get_usage(dev), hidbus_get_index(dev)); 288cb022db8SVladimir Kondratyev if (sc->type == HMT_TYPE_UNSUPPORTED) 289cb022db8SVladimir Kondratyev return (ENXIO); 290cb022db8SVladimir Kondratyev 291cb022db8SVladimir Kondratyev hidbus_set_desc(dev, 292cb022db8SVladimir Kondratyev sc->type == HMT_TYPE_TOUCHPAD ? "TouchPad" : "TouchScreen"); 293cb022db8SVladimir Kondratyev 294cb022db8SVladimir Kondratyev return (BUS_PROBE_DEFAULT); 295b9b347e9SVladimir Kondratyev } 296b9b347e9SVladimir Kondratyev 297b9b347e9SVladimir Kondratyev static int 298cb022db8SVladimir Kondratyev hmt_attach(device_t dev) 299b9b347e9SVladimir Kondratyev { 300cb022db8SVladimir Kondratyev struct hmt_softc *sc = device_get_softc(dev); 301cb022db8SVladimir Kondratyev const struct hid_device_info *hw = hid_get_device_info(dev); 302cb022db8SVladimir Kondratyev void *d_ptr; 303cb022db8SVladimir Kondratyev uint8_t *fbuf = NULL; 304cb022db8SVladimir Kondratyev hid_size_t d_len, fsize; 305b9b347e9SVladimir Kondratyev uint32_t cont_count_max; 306b9b347e9SVladimir Kondratyev int nbuttons, btn; 307b9b347e9SVladimir Kondratyev size_t i; 308b9b347e9SVladimir Kondratyev int err; 309b9b347e9SVladimir Kondratyev 310cb022db8SVladimir Kondratyev err = hid_get_report_descr(dev, &d_ptr, &d_len); 311cb022db8SVladimir Kondratyev if (err != 0) { 312cb022db8SVladimir Kondratyev device_printf(dev, "could not retrieve report descriptor from " 313cb022db8SVladimir Kondratyev "device: %d\n", err); 314cb022db8SVladimir Kondratyev return (ENXIO); 315cb022db8SVladimir Kondratyev } 316cb022db8SVladimir Kondratyev 317b9b347e9SVladimir Kondratyev sc->dev = dev; 318b9b347e9SVladimir Kondratyev 319cb022db8SVladimir Kondratyev fsize = hid_report_size_max(d_ptr, d_len, hid_feature, NULL); 320cb022db8SVladimir Kondratyev if (fsize != 0) 321cb022db8SVladimir Kondratyev fbuf = malloc(fsize, M_TEMP, M_WAITOK | M_ZERO); 322cb022db8SVladimir Kondratyev 323b9b347e9SVladimir Kondratyev /* Fetch and parse "Contact count maximum" feature report */ 324cb022db8SVladimir Kondratyev if (sc->cont_max_rlen > 1) { 325cb022db8SVladimir Kondratyev err = hid_get_report(dev, fbuf, sc->cont_max_rlen, NULL, 326cb022db8SVladimir Kondratyev HID_FEATURE_REPORT, sc->cont_max_rid); 327cb022db8SVladimir Kondratyev if (err == 0) { 328cb022db8SVladimir Kondratyev cont_count_max = hid_get_udata(fbuf + 1, 329b9b347e9SVladimir Kondratyev sc->cont_max_rlen - 1, &sc->cont_max_loc); 330b9b347e9SVladimir Kondratyev /* 331b9b347e9SVladimir Kondratyev * Feature report is a primary source of 332b9b347e9SVladimir Kondratyev * 'Contact Count Maximum' 333b9b347e9SVladimir Kondratyev */ 334b9b347e9SVladimir Kondratyev if (cont_count_max > 0) 335cb022db8SVladimir Kondratyev sc->ai[HMT_SLOT].max = cont_count_max - 1; 336b9b347e9SVladimir Kondratyev } else 337cb022db8SVladimir Kondratyev DPRINTF("hid_get_report error=%d\n", err); 338b9b347e9SVladimir Kondratyev } else 339cb022db8SVladimir Kondratyev DPRINTF("Feature report %hhu size invalid: %u\n", 340b9b347e9SVladimir Kondratyev sc->cont_max_rid, sc->cont_max_rlen); 341b9b347e9SVladimir Kondratyev 342b9b347e9SVladimir Kondratyev /* Fetch and parse "Button type" feature report */ 343cb022db8SVladimir Kondratyev if (sc->btn_type_rlen > 1 && sc->btn_type_rid != sc->cont_max_rid) { 344cb022db8SVladimir Kondratyev bzero(fbuf, fsize); 345cb022db8SVladimir Kondratyev err = hid_get_report(dev, fbuf, sc->btn_type_rlen, NULL, 346cb022db8SVladimir Kondratyev HID_FEATURE_REPORT, sc->btn_type_rid); 347b9b347e9SVladimir Kondratyev } 348b9b347e9SVladimir Kondratyev if (sc->btn_type_rlen > 1) { 349b9b347e9SVladimir Kondratyev if (err == 0) 350cb022db8SVladimir Kondratyev sc->is_clickpad = hid_get_udata(fbuf + 1, 351b9b347e9SVladimir Kondratyev sc->btn_type_rlen - 1, &sc->btn_type_loc) == 0; 352b9b347e9SVladimir Kondratyev else 353cb022db8SVladimir Kondratyev DPRINTF("hid_get_report error=%d\n", err); 354b9b347e9SVladimir Kondratyev } 355b9b347e9SVladimir Kondratyev 356b9b347e9SVladimir Kondratyev /* Fetch THQA certificate to enable some devices like WaveShare */ 357cb022db8SVladimir Kondratyev if (sc->thqa_cert_rlen > 1 && sc->thqa_cert_rid != sc->cont_max_rid) 358cb022db8SVladimir Kondratyev (void)hid_get_report(dev, fbuf, sc->thqa_cert_rlen, NULL, 359cb022db8SVladimir Kondratyev HID_FEATURE_REPORT, sc->thqa_cert_rid); 360cb022db8SVladimir Kondratyev 361cb022db8SVladimir Kondratyev free(fbuf, M_TEMP); 362b9b347e9SVladimir Kondratyev 363b9b347e9SVladimir Kondratyev /* Switch touchpad in to absolute multitouch mode */ 364cb022db8SVladimir Kondratyev if (sc->type == HMT_TYPE_TOUCHPAD) { 365cb022db8SVladimir Kondratyev err = hmt_set_input_mode(sc, HCONF_INPUT_MODE_MT_TOUCHPAD); 366b9b347e9SVladimir Kondratyev if (err != 0) 367b9b347e9SVladimir Kondratyev DPRINTF("Failed to set input mode: %d\n", err); 368b9b347e9SVladimir Kondratyev } 369b9b347e9SVladimir Kondratyev 370b9b347e9SVladimir Kondratyev /* Cap contact count maximum to MAX_MT_SLOTS */ 371cb022db8SVladimir Kondratyev if (sc->ai[HMT_SLOT].max >= MAX_MT_SLOTS) { 372b9b347e9SVladimir Kondratyev DPRINTF("Hardware reported %d contacts while only %d is " 373cb022db8SVladimir Kondratyev "supported\n", (int)sc->ai[HMT_SLOT].max+1, MAX_MT_SLOTS); 374cb022db8SVladimir Kondratyev sc->ai[HMT_SLOT].max = MAX_MT_SLOTS - 1; 375b9b347e9SVladimir Kondratyev } 376b9b347e9SVladimir Kondratyev 377cb022db8SVladimir Kondratyev if (hid_test_quirk(hw, HQ_MT_TIMESTAMP) || hmt_timestamps) 378b9b347e9SVladimir Kondratyev sc->do_timestamps = true; 379*5cc21ab9SVladimir Kondratyev #ifdef IICHID_SAMPLING 380cb022db8SVladimir Kondratyev if (hid_test_quirk(hw, HQ_IICHID_SAMPLING)) 381cb022db8SVladimir Kondratyev sc->iichid_sampling = true; 382*5cc21ab9SVladimir Kondratyev #endif 383b9b347e9SVladimir Kondratyev 384cb022db8SVladimir Kondratyev hidbus_set_intr(dev, hmt_intr, sc); 385b9b347e9SVladimir Kondratyev 386b9b347e9SVladimir Kondratyev sc->evdev = evdev_alloc(); 387b9b347e9SVladimir Kondratyev evdev_set_name(sc->evdev, device_get_desc(dev)); 388b9b347e9SVladimir Kondratyev evdev_set_phys(sc->evdev, device_get_nameunit(dev)); 389cb022db8SVladimir Kondratyev evdev_set_id(sc->evdev, hw->idBus, hw->idVendor, hw->idProduct, 390cb022db8SVladimir Kondratyev hw->idVersion); 391cb022db8SVladimir Kondratyev evdev_set_serial(sc->evdev, hw->serial); 392cb022db8SVladimir Kondratyev evdev_set_methods(sc->evdev, dev, &hmt_evdev_methods); 393b9b347e9SVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT); 394cb022db8SVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */ 395b9b347e9SVladimir Kondratyev switch (sc->type) { 396cb022db8SVladimir Kondratyev case HMT_TYPE_TOUCHSCREEN: 397b9b347e9SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT); 398b9b347e9SVladimir Kondratyev break; 399cb022db8SVladimir Kondratyev case HMT_TYPE_TOUCHPAD: 400b9b347e9SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_POINTER); 401b9b347e9SVladimir Kondratyev if (sc->is_clickpad) 402b9b347e9SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD); 403b9b347e9SVladimir Kondratyev break; 404b9b347e9SVladimir Kondratyev default: 405cb022db8SVladimir Kondratyev KASSERT(0, ("hmt_attach: unsupported touch device type")); 406b9b347e9SVladimir Kondratyev } 407b9b347e9SVladimir Kondratyev evdev_support_event(sc->evdev, EV_SYN); 408b9b347e9SVladimir Kondratyev evdev_support_event(sc->evdev, EV_ABS); 409b9b347e9SVladimir Kondratyev if (sc->do_timestamps) { 410b9b347e9SVladimir Kondratyev evdev_support_event(sc->evdev, EV_MSC); 411b9b347e9SVladimir Kondratyev evdev_support_msc(sc->evdev, MSC_TIMESTAMP); 412b9b347e9SVladimir Kondratyev } 413*5cc21ab9SVladimir Kondratyev #ifdef IICHID_SAMPLING 414cb022db8SVladimir Kondratyev if (sc->iichid_sampling) 415cb022db8SVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_AUTOREL); 416*5cc21ab9SVladimir Kondratyev #endif 417b9b347e9SVladimir Kondratyev nbuttons = 0; 418b9b347e9SVladimir Kondratyev if (sc->max_button != 0 || sc->has_int_button) { 419b9b347e9SVladimir Kondratyev evdev_support_event(sc->evdev, EV_KEY); 420b9b347e9SVladimir Kondratyev if (sc->has_int_button) 421b9b347e9SVladimir Kondratyev evdev_support_key(sc->evdev, BTN_LEFT); 422b9b347e9SVladimir Kondratyev for (btn = 0; btn < sc->max_button; ++btn) { 423b9b347e9SVladimir Kondratyev if (isset(sc->buttons, btn)) { 424b9b347e9SVladimir Kondratyev evdev_support_key(sc->evdev, BTN_MOUSE + btn); 425b9b347e9SVladimir Kondratyev nbuttons++; 426b9b347e9SVladimir Kondratyev } 427b9b347e9SVladimir Kondratyev } 428b9b347e9SVladimir Kondratyev } 429cb022db8SVladimir Kondratyev HMT_FOREACH_USAGE(sc->caps, i) { 430cb022db8SVladimir Kondratyev if (hmt_hid_map[i].code != HMT_NO_CODE) 431cb022db8SVladimir Kondratyev evdev_support_abs(sc->evdev, hmt_hid_map[i].code, 432b9b347e9SVladimir Kondratyev sc->ai[i].min, sc->ai[i].max, 0, 0, sc->ai[i].res); 433b9b347e9SVladimir Kondratyev } 434b9b347e9SVladimir Kondratyev 435cb022db8SVladimir Kondratyev err = evdev_register(sc->evdev); 436cb022db8SVladimir Kondratyev if (err) { 437cb022db8SVladimir Kondratyev hmt_detach(dev); 438b9b347e9SVladimir Kondratyev return (ENXIO); 439b9b347e9SVladimir Kondratyev } 440b9b347e9SVladimir Kondratyev 441cb022db8SVladimir Kondratyev /* Announce information about the touch device */ 442cb022db8SVladimir Kondratyev device_printf(sc->dev, "Multitouch %s with %d external button%s%s\n", 443cb022db8SVladimir Kondratyev sc->type == HMT_TYPE_TOUCHSCREEN ? "touchscreen" : "touchpad", 444cb022db8SVladimir Kondratyev nbuttons, nbuttons != 1 ? "s" : "", 445cb022db8SVladimir Kondratyev sc->is_clickpad ? ", click-pad" : ""); 446cb022db8SVladimir Kondratyev device_printf(sc->dev, 447cb022db8SVladimir Kondratyev "%d contacts with [%s%s%s%s%s] properties. Report range [%d:%d] - [%d:%d]\n", 448cb022db8SVladimir Kondratyev (int)sc->ai[HMT_SLOT].max + 1, 449cb022db8SVladimir Kondratyev isset(sc->caps, HMT_IN_RANGE) ? "R" : "", 450cb022db8SVladimir Kondratyev isset(sc->caps, HMT_CONFIDENCE) ? "C" : "", 451cb022db8SVladimir Kondratyev isset(sc->caps, HMT_WIDTH) ? "W" : "", 452cb022db8SVladimir Kondratyev isset(sc->caps, HMT_HEIGHT) ? "H" : "", 453cb022db8SVladimir Kondratyev isset(sc->caps, HMT_PRESSURE) ? "P" : "", 454cb022db8SVladimir Kondratyev (int)sc->ai[HMT_X].min, (int)sc->ai[HMT_Y].min, 455cb022db8SVladimir Kondratyev (int)sc->ai[HMT_X].max, (int)sc->ai[HMT_Y].max); 456cb022db8SVladimir Kondratyev 457cb022db8SVladimir Kondratyev return (0); 458cb022db8SVladimir Kondratyev } 459cb022db8SVladimir Kondratyev 460b9b347e9SVladimir Kondratyev static int 461cb022db8SVladimir Kondratyev hmt_detach(device_t dev) 462b9b347e9SVladimir Kondratyev { 463cb022db8SVladimir Kondratyev struct hmt_softc *sc = device_get_softc(dev); 464b9b347e9SVladimir Kondratyev 465b9b347e9SVladimir Kondratyev evdev_free(sc->evdev); 466cb022db8SVladimir Kondratyev 467b9b347e9SVladimir Kondratyev return (0); 468b9b347e9SVladimir Kondratyev } 469b9b347e9SVladimir Kondratyev 470b9b347e9SVladimir Kondratyev static void 471cb022db8SVladimir Kondratyev hmt_intr(void *context, void *buf, hid_size_t len) 472b9b347e9SVladimir Kondratyev { 473cb022db8SVladimir Kondratyev struct hmt_softc *sc = context; 474b9b347e9SVladimir Kondratyev size_t usage; 475b9b347e9SVladimir Kondratyev uint32_t *slot_data = sc->slot_data; 476b9b347e9SVladimir Kondratyev uint32_t cont, btn; 477b9b347e9SVladimir Kondratyev uint32_t cont_count; 478b9b347e9SVladimir Kondratyev uint32_t width; 479b9b347e9SVladimir Kondratyev uint32_t height; 480b9b347e9SVladimir Kondratyev uint32_t int_btn = 0; 481b9b347e9SVladimir Kondratyev uint32_t left_btn = 0; 482b9b347e9SVladimir Kondratyev int32_t slot; 483b9b347e9SVladimir Kondratyev uint32_t scan_time; 484b9b347e9SVladimir Kondratyev int32_t delta; 485cb022db8SVladimir Kondratyev uint8_t id; 486cb022db8SVladimir Kondratyev 487*5cc21ab9SVladimir Kondratyev #ifdef IICHID_SAMPLING 488cb022db8SVladimir Kondratyev /* 489cb022db8SVladimir Kondratyev * Special packet of zero length is generated by iichid driver running 490cb022db8SVladimir Kondratyev * in polling mode at the start of inactivity period to workaround 491cb022db8SVladimir Kondratyev * "stuck touch" problem caused by miss of finger release events. 492cb022db8SVladimir Kondratyev * This snippet is to be removed after GPIO interrupt support is added. 493cb022db8SVladimir Kondratyev */ 494cb022db8SVladimir Kondratyev if (sc->iichid_sampling && len == 0) { 495cb022db8SVladimir Kondratyev sc->prev_touch = false; 496cb022db8SVladimir Kondratyev sc->timestamp = 0; 497cb022db8SVladimir Kondratyev for (slot = 0; slot <= sc->ai[HMT_SLOT].max; slot++) { 498cb022db8SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_SLOT, slot); 499cb022db8SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_TRACKING_ID, -1); 500cb022db8SVladimir Kondratyev } 501cb022db8SVladimir Kondratyev evdev_sync(sc->evdev); 502cb022db8SVladimir Kondratyev return; 503cb022db8SVladimir Kondratyev } 504*5cc21ab9SVladimir Kondratyev #endif 505cb022db8SVladimir Kondratyev 506cb022db8SVladimir Kondratyev /* Ignore irrelevant reports */ 507cb022db8SVladimir Kondratyev id = sc->report_id != 0 ? *(uint8_t *)buf : 0; 508cb022db8SVladimir Kondratyev if (sc->report_id != id) { 509cb022db8SVladimir Kondratyev DPRINTF("Skip report with unexpected ID: %hhu\n", id); 510cb022db8SVladimir Kondratyev return; 511cb022db8SVladimir Kondratyev } 512cb022db8SVladimir Kondratyev 513cb022db8SVladimir Kondratyev /* Strip leading "report ID" byte */ 514cb022db8SVladimir Kondratyev if (sc->report_id != 0) { 515cb022db8SVladimir Kondratyev len--; 516cb022db8SVladimir Kondratyev buf = (uint8_t *)buf + 1; 517cb022db8SVladimir Kondratyev } 518b9b347e9SVladimir Kondratyev 519b9b347e9SVladimir Kondratyev /* 520b9b347e9SVladimir Kondratyev * "In Parallel mode, devices report all contact information in a 521b9b347e9SVladimir Kondratyev * single packet. Each physical contact is represented by a logical 522b9b347e9SVladimir Kondratyev * collection that is embedded in the top-level collection." 523b9b347e9SVladimir Kondratyev * 524b9b347e9SVladimir Kondratyev * Since additional contacts that were not present will still be in the 525b9b347e9SVladimir Kondratyev * report with contactid=0 but contactids are zero-based, find 526b9b347e9SVladimir Kondratyev * contactcount first. 527b9b347e9SVladimir Kondratyev */ 528b9b347e9SVladimir Kondratyev cont_count = hid_get_udata(buf, len, &sc->cont_count_loc); 529b9b347e9SVladimir Kondratyev /* 530b9b347e9SVladimir Kondratyev * "In Hybrid mode, the number of contacts that can be reported in one 531b9b347e9SVladimir Kondratyev * report is less than the maximum number of contacts that the device 532b9b347e9SVladimir Kondratyev * supports. For example, a device that supports a maximum of 533b9b347e9SVladimir Kondratyev * 4 concurrent physical contacts, can set up its top-level collection 534b9b347e9SVladimir Kondratyev * to deliver a maximum of two contacts in one report. If four contact 535b9b347e9SVladimir Kondratyev * points are present, the device can break these up into two serial 536b9b347e9SVladimir Kondratyev * reports that deliver two contacts each. 537b9b347e9SVladimir Kondratyev * 538b9b347e9SVladimir Kondratyev * "When a device delivers data in this manner, the Contact Count usage 539b9b347e9SVladimir Kondratyev * value in the first report should reflect the total number of 540b9b347e9SVladimir Kondratyev * contacts that are being delivered in the hybrid reports. The other 541b9b347e9SVladimir Kondratyev * serial reports should have a contact count of zero (0)." 542b9b347e9SVladimir Kondratyev */ 543b9b347e9SVladimir Kondratyev if (cont_count != 0) 544b9b347e9SVladimir Kondratyev sc->nconts_todo = cont_count; 545b9b347e9SVladimir Kondratyev 546cb022db8SVladimir Kondratyev #ifdef HID_DEBUG 547b9b347e9SVladimir Kondratyev DPRINTFN(6, "cont_count:%2u", (unsigned)cont_count); 548cb022db8SVladimir Kondratyev if (hmt_debug >= 6) { 549cb022db8SVladimir Kondratyev HMT_FOREACH_USAGE(sc->caps, usage) { 550cb022db8SVladimir Kondratyev if (hmt_hid_map[usage].usage != HMT_NO_USAGE) 551cb022db8SVladimir Kondratyev printf(" %-4s", hmt_hid_map[usage].name); 552b9b347e9SVladimir Kondratyev } 553b9b347e9SVladimir Kondratyev printf("\n"); 554b9b347e9SVladimir Kondratyev } 555b9b347e9SVladimir Kondratyev #endif 556b9b347e9SVladimir Kondratyev 557b9b347e9SVladimir Kondratyev /* Find the number of contacts reported in current report */ 558b9b347e9SVladimir Kondratyev cont_count = MIN(sc->nconts_todo, sc->nconts_per_report); 559b9b347e9SVladimir Kondratyev 560b9b347e9SVladimir Kondratyev /* Use protocol Type B for reporting events */ 561b9b347e9SVladimir Kondratyev for (cont = 0; cont < cont_count; cont++) { 562b9b347e9SVladimir Kondratyev bzero(slot_data, sizeof(sc->slot_data)); 563cb022db8SVladimir Kondratyev HMT_FOREACH_USAGE(sc->caps, usage) { 564b9b347e9SVladimir Kondratyev if (sc->locs[cont][usage].size > 0) 565b9b347e9SVladimir Kondratyev slot_data[usage] = hid_get_udata( 566b9b347e9SVladimir Kondratyev buf, len, &sc->locs[cont][usage]); 567b9b347e9SVladimir Kondratyev } 568b9b347e9SVladimir Kondratyev 569b9b347e9SVladimir Kondratyev slot = evdev_get_mt_slot_by_tracking_id(sc->evdev, 570cb022db8SVladimir Kondratyev slot_data[HMT_CONTACTID]); 571b9b347e9SVladimir Kondratyev 572cb022db8SVladimir Kondratyev #ifdef HID_DEBUG 573b9b347e9SVladimir Kondratyev DPRINTFN(6, "cont%01x: data = ", cont); 574cb022db8SVladimir Kondratyev if (hmt_debug >= 6) { 575cb022db8SVladimir Kondratyev HMT_FOREACH_USAGE(sc->caps, usage) { 576cb022db8SVladimir Kondratyev if (hmt_hid_map[usage].usage != HMT_NO_USAGE) 577b9b347e9SVladimir Kondratyev printf("%04x ", slot_data[usage]); 578b9b347e9SVladimir Kondratyev } 579b9b347e9SVladimir Kondratyev printf("slot = %d\n", (int)slot); 580b9b347e9SVladimir Kondratyev } 581b9b347e9SVladimir Kondratyev #endif 582b9b347e9SVladimir Kondratyev 583b9b347e9SVladimir Kondratyev if (slot == -1) { 584b9b347e9SVladimir Kondratyev DPRINTF("Slot overflow for contact_id %u\n", 585cb022db8SVladimir Kondratyev (unsigned)slot_data[HMT_CONTACTID]); 586b9b347e9SVladimir Kondratyev continue; 587b9b347e9SVladimir Kondratyev } 588b9b347e9SVladimir Kondratyev 589cb022db8SVladimir Kondratyev if (slot_data[HMT_TIP_SWITCH] != 0 && 590cb022db8SVladimir Kondratyev !(isset(sc->caps, HMT_CONFIDENCE) && 591cb022db8SVladimir Kondratyev slot_data[HMT_CONFIDENCE] == 0)) { 592b9b347e9SVladimir Kondratyev /* This finger is in proximity of the sensor */ 593b9b347e9SVladimir Kondratyev sc->touch = true; 594cb022db8SVladimir Kondratyev slot_data[HMT_SLOT] = slot; 595cb022db8SVladimir Kondratyev slot_data[HMT_IN_RANGE] = !slot_data[HMT_IN_RANGE]; 596b9b347e9SVladimir Kondratyev /* Divided by two to match visual scale of touch */ 597cb022db8SVladimir Kondratyev width = slot_data[HMT_WIDTH] >> 1; 598cb022db8SVladimir Kondratyev height = slot_data[HMT_HEIGHT] >> 1; 599cb022db8SVladimir Kondratyev slot_data[HMT_ORIENTATION] = width > height; 600cb022db8SVladimir Kondratyev slot_data[HMT_MAJOR] = MAX(width, height); 601cb022db8SVladimir Kondratyev slot_data[HMT_MINOR] = MIN(width, height); 602b9b347e9SVladimir Kondratyev 603cb022db8SVladimir Kondratyev HMT_FOREACH_USAGE(sc->caps, usage) 604cb022db8SVladimir Kondratyev if (hmt_hid_map[usage].code != HMT_NO_CODE) 605b9b347e9SVladimir Kondratyev evdev_push_abs(sc->evdev, 606cb022db8SVladimir Kondratyev hmt_hid_map[usage].code, 607b9b347e9SVladimir Kondratyev slot_data[usage]); 608b9b347e9SVladimir Kondratyev } else { 609b9b347e9SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_SLOT, slot); 610b9b347e9SVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_TRACKING_ID, -1); 611b9b347e9SVladimir Kondratyev } 612b9b347e9SVladimir Kondratyev } 613b9b347e9SVladimir Kondratyev 614b9b347e9SVladimir Kondratyev sc->nconts_todo -= cont_count; 615b9b347e9SVladimir Kondratyev if (sc->do_timestamps && sc->nconts_todo == 0) { 616b9b347e9SVladimir Kondratyev /* HUD_SCAN_TIME is measured in 100us, convert to us. */ 617b9b347e9SVladimir Kondratyev scan_time = hid_get_udata(buf, len, &sc->scan_time_loc); 618b9b347e9SVladimir Kondratyev if (sc->prev_touch) { 619b9b347e9SVladimir Kondratyev delta = scan_time - sc->scan_time; 620b9b347e9SVladimir Kondratyev if (delta < 0) 621b9b347e9SVladimir Kondratyev delta += sc->scan_time_max; 622b9b347e9SVladimir Kondratyev } else 623b9b347e9SVladimir Kondratyev delta = 0; 624b9b347e9SVladimir Kondratyev sc->scan_time = scan_time; 625b9b347e9SVladimir Kondratyev sc->timestamp += delta * 100; 626b9b347e9SVladimir Kondratyev evdev_push_msc(sc->evdev, MSC_TIMESTAMP, sc->timestamp); 627b9b347e9SVladimir Kondratyev sc->prev_touch = sc->touch; 628b9b347e9SVladimir Kondratyev sc->touch = false; 629b9b347e9SVladimir Kondratyev if (!sc->prev_touch) 630b9b347e9SVladimir Kondratyev sc->timestamp = 0; 631b9b347e9SVladimir Kondratyev } 632b9b347e9SVladimir Kondratyev if (sc->nconts_todo == 0) { 633b9b347e9SVladimir Kondratyev /* Report both the click and external left btns as BTN_LEFT */ 634b9b347e9SVladimir Kondratyev if (sc->has_int_button) 635b9b347e9SVladimir Kondratyev int_btn = hid_get_data(buf, len, &sc->int_btn_loc); 636b9b347e9SVladimir Kondratyev if (isset(sc->buttons, 0)) 637b9b347e9SVladimir Kondratyev left_btn = hid_get_data(buf, len, &sc->btn_loc[0]); 638b9b347e9SVladimir Kondratyev if (sc->has_int_button || isset(sc->buttons, 0)) 639b9b347e9SVladimir Kondratyev evdev_push_key(sc->evdev, BTN_LEFT, 640b9b347e9SVladimir Kondratyev (int_btn != 0) | (left_btn != 0)); 641b9b347e9SVladimir Kondratyev for (btn = 1; btn < sc->max_button; ++btn) { 642b9b347e9SVladimir Kondratyev if (isset(sc->buttons, btn)) 643b9b347e9SVladimir Kondratyev evdev_push_key(sc->evdev, BTN_MOUSE + btn, 644b9b347e9SVladimir Kondratyev hid_get_data(buf, 645b9b347e9SVladimir Kondratyev len, 646b9b347e9SVladimir Kondratyev &sc->btn_loc[btn]) != 0); 647b9b347e9SVladimir Kondratyev } 648b9b347e9SVladimir Kondratyev evdev_sync(sc->evdev); 649b9b347e9SVladimir Kondratyev } 650b9b347e9SVladimir Kondratyev } 651b9b347e9SVladimir Kondratyev 652cb022db8SVladimir Kondratyev static enum hmt_type 653cb022db8SVladimir Kondratyev hmt_hid_parse(struct hmt_softc *sc, const void *d_ptr, hid_size_t d_len, 654cb022db8SVladimir Kondratyev uint32_t tlc_usage, uint8_t tlc_index) 655b9b347e9SVladimir Kondratyev { 656cb022db8SVladimir Kondratyev struct hid_absinfo ai; 657b9b347e9SVladimir Kondratyev struct hid_item hi; 658b9b347e9SVladimir Kondratyev struct hid_data *hd; 659cb022db8SVladimir Kondratyev uint32_t flags; 660b9b347e9SVladimir Kondratyev size_t i; 661b9b347e9SVladimir Kondratyev size_t cont = 0; 662cb022db8SVladimir Kondratyev enum hmt_type type; 663b9b347e9SVladimir Kondratyev uint32_t left_btn, btn; 664b9b347e9SVladimir Kondratyev int32_t cont_count_max = 0; 665b9b347e9SVladimir Kondratyev uint8_t report_id = 0; 666b9b347e9SVladimir Kondratyev bool finger_coll = false; 667b9b347e9SVladimir Kondratyev bool cont_count_found = false; 668b9b347e9SVladimir Kondratyev bool scan_time_found = false; 669b9b347e9SVladimir Kondratyev bool has_int_button = false; 670b9b347e9SVladimir Kondratyev 671cb022db8SVladimir Kondratyev #define HMT_HI_ABSOLUTE(hi) \ 672b9b347e9SVladimir Kondratyev (((hi).flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE) 673b9b347e9SVladimir Kondratyev #define HUMS_THQA_CERT 0xC5 674b9b347e9SVladimir Kondratyev 675cb022db8SVladimir Kondratyev /* 676cb022db8SVladimir Kondratyev * Get left button usage taking in account MS Precision Touchpad specs. 677cb022db8SVladimir Kondratyev * For Windows PTP report descriptor assigns buttons in following way: 678cb022db8SVladimir Kondratyev * Button 1 - Indicates Button State for touchpad button integrated 679cb022db8SVladimir Kondratyev * with digitizer. 680cb022db8SVladimir Kondratyev * Button 2 - Indicates Button State for external button for primary 681cb022db8SVladimir Kondratyev * (default left) clicking. 682cb022db8SVladimir Kondratyev * Button 3 - Indicates Button State for external button for secondary 683cb022db8SVladimir Kondratyev * (default right) clicking. 684cb022db8SVladimir Kondratyev * If a device only supports external buttons, it must still use 685cb022db8SVladimir Kondratyev * Button 2 and Button 3 to reference the external buttons. 686cb022db8SVladimir Kondratyev */ 687cb022db8SVladimir Kondratyev switch (tlc_usage) { 688cb022db8SVladimir Kondratyev case HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN): 689cb022db8SVladimir Kondratyev type = HMT_TYPE_TOUCHSCREEN; 690b9b347e9SVladimir Kondratyev left_btn = 1; 691b9b347e9SVladimir Kondratyev break; 692cb022db8SVladimir Kondratyev case HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD): 693cb022db8SVladimir Kondratyev type = HMT_TYPE_TOUCHPAD; 694b9b347e9SVladimir Kondratyev left_btn = 2; 695b9b347e9SVladimir Kondratyev break; 696b9b347e9SVladimir Kondratyev default: 697cb022db8SVladimir Kondratyev return (HMT_TYPE_UNSUPPORTED); 698b9b347e9SVladimir Kondratyev } 699b9b347e9SVladimir Kondratyev 700cb022db8SVladimir Kondratyev /* Parse features for mandatory maximum contact count usage */ 701cb022db8SVladimir Kondratyev if (!hidbus_locate(d_ptr, d_len, 702cb022db8SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX), hid_feature, 703cb022db8SVladimir Kondratyev tlc_index, 0, &sc->cont_max_loc, &flags, &sc->cont_max_rid, &ai) || 704cb022db8SVladimir Kondratyev (flags & (HIO_VARIABLE | HIO_RELATIVE)) != HIO_VARIABLE) 705cb022db8SVladimir Kondratyev return (HMT_TYPE_UNSUPPORTED); 706b9b347e9SVladimir Kondratyev 707cb022db8SVladimir Kondratyev cont_count_max = ai.max; 708cb022db8SVladimir Kondratyev 709cb022db8SVladimir Kondratyev /* Parse features for button type usage */ 710cb022db8SVladimir Kondratyev if (hidbus_locate(d_ptr, d_len, 711cb022db8SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_BUTTON_TYPE), hid_feature, 712cb022db8SVladimir Kondratyev tlc_index, 0, &sc->btn_type_loc, &flags, &sc->btn_type_rid, NULL) 713cb022db8SVladimir Kondratyev && (flags & (HIO_VARIABLE | HIO_RELATIVE)) != HIO_VARIABLE) 714cb022db8SVladimir Kondratyev sc->btn_type_rid = 0; 715cb022db8SVladimir Kondratyev 716cb022db8SVladimir Kondratyev /* Parse features for THQA certificate report ID */ 717cb022db8SVladimir Kondratyev hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_MICROSOFT, HUMS_THQA_CERT), 718cb022db8SVladimir Kondratyev hid_feature, tlc_index, 0, NULL, NULL, &sc->thqa_cert_rid, NULL); 719b9b347e9SVladimir Kondratyev 720b9b347e9SVladimir Kondratyev /* Parse input for other parameters */ 721b9b347e9SVladimir Kondratyev hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); 722cb022db8SVladimir Kondratyev HIDBUS_FOREACH_ITEM(hd, &hi, tlc_index) { 723b9b347e9SVladimir Kondratyev switch (hi.kind) { 724b9b347e9SVladimir Kondratyev case hid_collection: 725cb022db8SVladimir Kondratyev if (hi.collevel == 2 && 726b9b347e9SVladimir Kondratyev hi.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_FINGER)) 727b9b347e9SVladimir Kondratyev finger_coll = true; 728b9b347e9SVladimir Kondratyev break; 729b9b347e9SVladimir Kondratyev case hid_endcollection: 730b9b347e9SVladimir Kondratyev if (hi.collevel == 1 && finger_coll) { 731b9b347e9SVladimir Kondratyev finger_coll = false; 732b9b347e9SVladimir Kondratyev cont++; 733cb022db8SVladimir Kondratyev } 734b9b347e9SVladimir Kondratyev break; 735b9b347e9SVladimir Kondratyev case hid_input: 736b9b347e9SVladimir Kondratyev /* 737cb022db8SVladimir Kondratyev * Ensure that all usages belong to the same report 738b9b347e9SVladimir Kondratyev */ 739cb022db8SVladimir Kondratyev if (HMT_HI_ABSOLUTE(hi) && 740b9b347e9SVladimir Kondratyev (report_id == 0 || report_id == hi.report_ID)) 741b9b347e9SVladimir Kondratyev report_id = hi.report_ID; 742b9b347e9SVladimir Kondratyev else 743b9b347e9SVladimir Kondratyev break; 744b9b347e9SVladimir Kondratyev 745b9b347e9SVladimir Kondratyev if (hi.collevel == 1 && left_btn == 2 && 746b9b347e9SVladimir Kondratyev hi.usage == HID_USAGE2(HUP_BUTTON, 1)) { 747b9b347e9SVladimir Kondratyev has_int_button = true; 748b9b347e9SVladimir Kondratyev sc->int_btn_loc = hi.loc; 749b9b347e9SVladimir Kondratyev break; 750b9b347e9SVladimir Kondratyev } 751b9b347e9SVladimir Kondratyev if (hi.collevel == 1 && 752b9b347e9SVladimir Kondratyev hi.usage >= HID_USAGE2(HUP_BUTTON, left_btn) && 753cb022db8SVladimir Kondratyev hi.usage <= HID_USAGE2(HUP_BUTTON, HMT_BTN_MAX)) { 754b9b347e9SVladimir Kondratyev btn = (hi.usage & 0xFFFF) - left_btn; 755b9b347e9SVladimir Kondratyev setbit(sc->buttons, btn); 756b9b347e9SVladimir Kondratyev sc->btn_loc[btn] = hi.loc; 757b9b347e9SVladimir Kondratyev if (btn >= sc->max_button) 758b9b347e9SVladimir Kondratyev sc->max_button = btn + 1; 759b9b347e9SVladimir Kondratyev break; 760b9b347e9SVladimir Kondratyev } 761b9b347e9SVladimir Kondratyev if (hi.collevel == 1 && hi.usage == 762b9b347e9SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT)) { 763b9b347e9SVladimir Kondratyev cont_count_found = true; 764b9b347e9SVladimir Kondratyev sc->cont_count_loc = hi.loc; 765b9b347e9SVladimir Kondratyev break; 766b9b347e9SVladimir Kondratyev } 767b9b347e9SVladimir Kondratyev /* Scan time is required but clobbered by evdev */ 768b9b347e9SVladimir Kondratyev if (hi.collevel == 1 && hi.usage == 769b9b347e9SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_SCAN_TIME)) { 770b9b347e9SVladimir Kondratyev scan_time_found = true; 771b9b347e9SVladimir Kondratyev sc->scan_time_loc = hi.loc; 772b9b347e9SVladimir Kondratyev sc->scan_time_max = hi.logical_maximum; 773b9b347e9SVladimir Kondratyev break; 774b9b347e9SVladimir Kondratyev } 775b9b347e9SVladimir Kondratyev 776b9b347e9SVladimir Kondratyev if (!finger_coll || hi.collevel != 2) 777b9b347e9SVladimir Kondratyev break; 778b9b347e9SVladimir Kondratyev if (cont >= MAX_MT_SLOTS) { 779b9b347e9SVladimir Kondratyev DPRINTF("Finger %zu ignored\n", cont); 780b9b347e9SVladimir Kondratyev break; 781b9b347e9SVladimir Kondratyev } 782b9b347e9SVladimir Kondratyev 783cb022db8SVladimir Kondratyev for (i = 0; i < HMT_N_USAGES; i++) { 784cb022db8SVladimir Kondratyev if (hi.usage == hmt_hid_map[i].usage) { 785b9b347e9SVladimir Kondratyev /* 786b9b347e9SVladimir Kondratyev * HUG_X usage is an array mapped to 787b9b347e9SVladimir Kondratyev * both ABS_MT_POSITION and ABS_MT_TOOL 788b9b347e9SVladimir Kondratyev * events. So don`t stop search if we 789b9b347e9SVladimir Kondratyev * already have HUG_X mapping done. 790b9b347e9SVladimir Kondratyev */ 791b9b347e9SVladimir Kondratyev if (sc->locs[cont][i].size) 792b9b347e9SVladimir Kondratyev continue; 793b9b347e9SVladimir Kondratyev sc->locs[cont][i] = hi.loc; 794b9b347e9SVladimir Kondratyev /* 795b9b347e9SVladimir Kondratyev * Hid parser returns valid logical and 796b9b347e9SVladimir Kondratyev * physical sizes for first finger only 797b9b347e9SVladimir Kondratyev * at least on ElanTS 0x04f3:0x0012. 798b9b347e9SVladimir Kondratyev */ 799b9b347e9SVladimir Kondratyev if (cont > 0) 800b9b347e9SVladimir Kondratyev break; 801b9b347e9SVladimir Kondratyev setbit(sc->caps, i); 802cb022db8SVladimir Kondratyev sc->ai[i] = (struct hid_absinfo) { 803b9b347e9SVladimir Kondratyev .max = hi.logical_maximum, 804b9b347e9SVladimir Kondratyev .min = hi.logical_minimum, 805b9b347e9SVladimir Kondratyev .res = hid_item_resolution(&hi), 806b9b347e9SVladimir Kondratyev }; 807b9b347e9SVladimir Kondratyev break; 808b9b347e9SVladimir Kondratyev } 809b9b347e9SVladimir Kondratyev } 810b9b347e9SVladimir Kondratyev break; 811b9b347e9SVladimir Kondratyev default: 812b9b347e9SVladimir Kondratyev break; 813b9b347e9SVladimir Kondratyev } 814b9b347e9SVladimir Kondratyev } 815b9b347e9SVladimir Kondratyev hid_end_parse(hd); 816b9b347e9SVladimir Kondratyev 817b9b347e9SVladimir Kondratyev /* Check for required HID Usages */ 818b9b347e9SVladimir Kondratyev if (!cont_count_found || !scan_time_found || cont == 0) 819cb022db8SVladimir Kondratyev return (HMT_TYPE_UNSUPPORTED); 820cb022db8SVladimir Kondratyev for (i = 0; i < HMT_N_USAGES; i++) { 821cb022db8SVladimir Kondratyev if (hmt_hid_map[i].required && isclr(sc->caps, i)) 822cb022db8SVladimir Kondratyev return (HMT_TYPE_UNSUPPORTED); 823b9b347e9SVladimir Kondratyev } 824b9b347e9SVladimir Kondratyev 825b9b347e9SVladimir Kondratyev /* Touchpads must have at least one button */ 826cb022db8SVladimir Kondratyev if (type == HMT_TYPE_TOUCHPAD && !sc->max_button && !has_int_button) 827cb022db8SVladimir Kondratyev return (HMT_TYPE_UNSUPPORTED); 828b9b347e9SVladimir Kondratyev 829b9b347e9SVladimir Kondratyev /* 830b9b347e9SVladimir Kondratyev * According to specifications 'Contact Count Maximum' should be read 831b9b347e9SVladimir Kondratyev * from Feature Report rather than from HID descriptor. Set sane 832b9b347e9SVladimir Kondratyev * default value now to handle the case of 'Get Report' request failure 833b9b347e9SVladimir Kondratyev */ 834b9b347e9SVladimir Kondratyev if (cont_count_max < 1) 835b9b347e9SVladimir Kondratyev cont_count_max = cont; 836b9b347e9SVladimir Kondratyev 837b9b347e9SVladimir Kondratyev /* Set number of MT protocol type B slots */ 838cb022db8SVladimir Kondratyev sc->ai[HMT_SLOT] = (struct hid_absinfo) { 839b9b347e9SVladimir Kondratyev .min = 0, 840b9b347e9SVladimir Kondratyev .max = cont_count_max - 1, 841b9b347e9SVladimir Kondratyev .res = 0, 842b9b347e9SVladimir Kondratyev }; 843b9b347e9SVladimir Kondratyev 844b9b347e9SVladimir Kondratyev /* Report touch orientation if both width and height are supported */ 845cb022db8SVladimir Kondratyev if (isset(sc->caps, HMT_WIDTH) && isset(sc->caps, HMT_HEIGHT)) { 846cb022db8SVladimir Kondratyev setbit(sc->caps, HMT_ORIENTATION); 847cb022db8SVladimir Kondratyev sc->ai[HMT_ORIENTATION].max = 1; 848b9b347e9SVladimir Kondratyev } 849b9b347e9SVladimir Kondratyev 850b9b347e9SVladimir Kondratyev sc->cont_max_rlen = hid_report_size(d_ptr, d_len, hid_feature, 851b9b347e9SVladimir Kondratyev sc->cont_max_rid); 852b9b347e9SVladimir Kondratyev if (sc->btn_type_rid > 0) 853b9b347e9SVladimir Kondratyev sc->btn_type_rlen = hid_report_size(d_ptr, d_len, 854b9b347e9SVladimir Kondratyev hid_feature, sc->btn_type_rid); 855b9b347e9SVladimir Kondratyev if (sc->thqa_cert_rid > 0) 856b9b347e9SVladimir Kondratyev sc->thqa_cert_rlen = hid_report_size(d_ptr, d_len, 857b9b347e9SVladimir Kondratyev hid_feature, sc->thqa_cert_rid); 858b9b347e9SVladimir Kondratyev 859b9b347e9SVladimir Kondratyev sc->report_id = report_id; 860b9b347e9SVladimir Kondratyev sc->nconts_per_report = cont; 861b9b347e9SVladimir Kondratyev sc->has_int_button = has_int_button; 862b9b347e9SVladimir Kondratyev 863b9b347e9SVladimir Kondratyev return (type); 864b9b347e9SVladimir Kondratyev } 865b9b347e9SVladimir Kondratyev 866b9b347e9SVladimir Kondratyev static int 867cb022db8SVladimir Kondratyev hmt_set_input_mode(struct hmt_softc *sc, enum hconf_input_mode mode) 868b9b347e9SVladimir Kondratyev { 869cb022db8SVladimir Kondratyev devclass_t hconf_devclass; 870cb022db8SVladimir Kondratyev device_t hconf; 871b9b347e9SVladimir Kondratyev int err; 872b9b347e9SVladimir Kondratyev 873cb022db8SVladimir Kondratyev GIANT_REQUIRED; 874b9b347e9SVladimir Kondratyev 875cb022db8SVladimir Kondratyev /* Find touchpad's configuration TLC */ 876cb022db8SVladimir Kondratyev hconf = hidbus_find_child(device_get_parent(sc->dev), 877cb022db8SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG)); 878cb022db8SVladimir Kondratyev if (hconf == NULL) 879cb022db8SVladimir Kondratyev return (ENXIO); 880b9b347e9SVladimir Kondratyev 881cb022db8SVladimir Kondratyev /* Ensure that hconf driver is attached to configuration TLC */ 882cb022db8SVladimir Kondratyev if (device_is_alive(hconf) == 0) 883cb022db8SVladimir Kondratyev device_probe_and_attach(hconf); 884cb022db8SVladimir Kondratyev if (device_is_attached(hconf) == 0) 885cb022db8SVladimir Kondratyev return (ENXIO); 886cb022db8SVladimir Kondratyev hconf_devclass = devclass_find("hconf"); 887cb022db8SVladimir Kondratyev if (device_get_devclass(hconf) != hconf_devclass) 888cb022db8SVladimir Kondratyev return (ENXIO); 889cb022db8SVladimir Kondratyev 890cb022db8SVladimir Kondratyev /* hconf_set_input_mode can drop the Giant while sleeping */ 891cb022db8SVladimir Kondratyev device_busy(hconf); 892cb022db8SVladimir Kondratyev err = hconf_set_input_mode(hconf, mode); 893cb022db8SVladimir Kondratyev device_unbusy(hconf); 894b9b347e9SVladimir Kondratyev 895b9b347e9SVladimir Kondratyev return (err); 896b9b347e9SVladimir Kondratyev } 897b9b347e9SVladimir Kondratyev 898cb022db8SVladimir Kondratyev static devclass_t hmt_devclass; 899b9b347e9SVladimir Kondratyev 900cb022db8SVladimir Kondratyev static device_method_t hmt_methods[] = { 901cb022db8SVladimir Kondratyev DEVMETHOD(device_probe, hmt_probe), 902cb022db8SVladimir Kondratyev DEVMETHOD(device_attach, hmt_attach), 903cb022db8SVladimir Kondratyev DEVMETHOD(device_detach, hmt_detach), 904b9b347e9SVladimir Kondratyev 905b9b347e9SVladimir Kondratyev DEVMETHOD_END 906b9b347e9SVladimir Kondratyev }; 907b9b347e9SVladimir Kondratyev 908cb022db8SVladimir Kondratyev static driver_t hmt_driver = { 909cb022db8SVladimir Kondratyev .name = "hmt", 910cb022db8SVladimir Kondratyev .methods = hmt_methods, 911cb022db8SVladimir Kondratyev .size = sizeof(struct hmt_softc), 912b9b347e9SVladimir Kondratyev }; 913b9b347e9SVladimir Kondratyev 914cb022db8SVladimir Kondratyev DRIVER_MODULE(hmt, hidbus, hmt_driver, hmt_devclass, NULL, 0); 915cb022db8SVladimir Kondratyev MODULE_DEPEND(hmt, hidbus, 1, 1, 1); 916cb022db8SVladimir Kondratyev MODULE_DEPEND(hmt, hid, 1, 1, 1); 917cb022db8SVladimir Kondratyev MODULE_DEPEND(hmt, hconf, 1, 1, 1); 918cb022db8SVladimir Kondratyev MODULE_DEPEND(hmt, evdev, 1, 1, 1); 919cb022db8SVladimir Kondratyev MODULE_VERSION(hmt, 1); 920cb022db8SVladimir Kondratyev HID_PNP_INFO(hmt_devs); 921