/* $OpenBSD: iatp.c,v 1.1 2016/09/01 10:04:51 jcs Exp $ */ /* * Atmel maXTouch i2c touchscreen/touchpad driver * Copyright (c) 2016 joshua stein * * AT421085 datasheet: * http://www.atmel.com/images/Atmel-9626-AT42-QTouch-BSW-AT421085-Object-Protocol-Guide_Datasheet.pdf * * Uses code from libmaxtouch * Copyright 2011 Atmel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY ATMEL ''AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL ATMEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include /* #define IATP_DEBUG */ #ifdef IATP_DEBUG #define DPRINTF(x) printf x #else #define DPRINTF(x) #endif struct mxt_object { uint8_t type; uint16_t start_pos; uint8_t size_minus_one; #define MXT_SIZE(o) ((uint16_t)((o)->size_minus_one) + 1) uint8_t instances_minus_one; #define MXT_INSTANCES(o) ((uint16_t)((o)->instances_minus_one) + 1) uint8_t num_report_ids; } __packed; struct mxt_id_info { uint8_t family; uint8_t variant; uint8_t version; uint8_t build; uint8_t matrix_x_size; uint8_t matrix_y_size; uint8_t num_objects; } __packed; struct mxt_info { struct mxt_id_info id; struct mxt_object *objects; uint32_t crc; uint8_t *raw_info; uint8_t max_report_id; }; /* object types we care about (of 117 total!) */ #define MXT_GEN_MESSAGEPROCESSOR_T5 5 #define MXT_GEN_COMMANDPROCESSOR_T6 6 # define MXT_T6_STATUS_RESET (1 << 7) # define MXT_T6_STATUS_OFL (1 << 6) # define MXT_T6_STATUS_SIGERR (1 << 5) # define MXT_T6_STATUS_CAL (1 << 4) # define MXT_T6_STATUS_CFGERR (1 << 3) # define MXT_T6_STATUS_COMSERR (1 << 2) # define MXT_T6_CMD_RESET 0 # define MXT_T6_CMD_BACKUPNV 1 # define MXT_T6_CMD_CALIBRATE 2 # define MXT_T6_CMD_REPORTALL 3 # define MXT_T6_CMD_DIAGNOSTIC 5 #define MXT_GEN_POWERCONFIG_T7 7 # define MXT_T7_POWER_MODE_DEFAULT 1 # define MXT_T7_POWER_MODE_DEEP_SLEEP 2 struct mxt_t7_config { uint8_t idle; uint8_t active; uint8_t atoi_timeout; } __packed; #define MXT_SPT_GPIOPWM_T19 19 static const struct mxt_t19_button_map { const char *vendor; const char *product; const char *hid; int bit; } mxt_t19_button_map_devs[] = { /* Chromebook Pixel 2015 */ { "GOOGLE", "Samus", "ATML0000", 3 }, /* Other Google Chromebooks */ { "GOOGLE", "", "ATML0000", 5 }, { NULL } }; #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 # define MXT_T100_CTRL 0 # define MXT_T100_CFG1 1 # define MXT_T100_TCHAUX 3 # define MXT_T100_XRANGE 13 # define MXT_T100_YRANGE 24 # define MXT_T100_CFG_SWITCHXY (1 << 5) # define MXT_T100_TCHAUX_VECT (1 << 0) # define MXT_T100_TCHAUX_AMPL (1 << 1) # define MXT_T100_TCHAUX_AREA (1 << 2) # define MXT_T100_DETECT (1 << 7) # define MXT_T100_TYPE_MASK 0x70 enum t100_type { MXT_T100_TYPE_FINGER = 1, MXT_T100_TYPE_PASSIVE_STYLUS = 2, MXT_T100_TYPE_HOVERING_FINGER = 4, MXT_T100_TYPE_GLOVE = 5, MXT_T100_TYPE_LARGE_TOUCH = 6, }; #define MXT_DISTANCE_ACTIVE_TOUCH 0 #define MXT_DISTANCE_HOVERING 1 #define MXT_TOUCH_MAJOR_DEFAULT 1 struct iatp_softc { struct device sc_dev; i2c_tag_t sc_tag; i2c_addr_t sc_addr; void *sc_ih; struct device *sc_wsmousedev; char sc_hid[16]; int sc_busy; int sc_enabled; int sc_touchpad; struct tsscale sc_tsscale; uint8_t *table; size_t table_size; struct mxt_info info; uint8_t *msg_buf; uint8_t multitouch; uint8_t num_touchids; uint32_t max_x; uint32_t max_y; uint8_t button; uint16_t t5_address; uint8_t t5_msg_size; uint16_t t6_address; uint8_t t6_reportid; uint16_t t7_address; struct mxt_t7_config t7_config; uint8_t t19_reportid; int t19_button_bit; uint16_t t44_address; uint8_t t100_reportid_min; uint8_t t100_reportid_max; uint8_t t100_aux_ampl; uint8_t t100_aux_area; uint8_t t100_aux_vect; }; int iatp_match(struct device *, void *, void *); void iatp_attach(struct device *, struct device *, void *); int iatp_detach(struct device *, int); int iatp_activate(struct device *, int); int iatp_ioctl(void *, u_long, caddr_t, int, struct proc *); int iatp_enable(void *); void iatp_disable(void *); int iatp_read_reg(struct iatp_softc *, uint16_t, size_t, void *); int iatp_write_reg(struct iatp_softc *, uint16_t, size_t, void *); int iatp_init(struct iatp_softc *); int iatp_intr(void *); int iatp_proc_msg(struct iatp_softc *, uint8_t *); int iatp_t5_read_msgs(struct iatp_softc *, int); void iatp_t6_proc_msg(struct iatp_softc *, uint8_t *); int iatp_t7_set_power_mode(struct iatp_softc *, int); void iatp_t19_proc_msg(struct iatp_softc *, uint8_t *); int iatp_t44_read_count(struct iatp_softc *); void iatp_t100_proc_msg(struct iatp_softc *, uint8_t *); /* for gpio pin mapping */ extern char *hw_vendor, *hw_prod; const struct wsmouse_accessops iatp_accessops = { iatp_enable, iatp_ioctl, iatp_disable, }; struct cfattach iatp_ca = { sizeof(struct iatp_softc), iatp_match, iatp_attach, iatp_detach, iatp_activate }; struct cfdriver iatp_cd = { NULL, "iatp", DV_DULL }; int iatp_match(struct device *parent, void *match, void *aux) { struct i2c_attach_args *ia = aux; if (strcmp(ia->ia_name, "iatp") == 0) return 1; return 0; } void iatp_attach(struct device *parent, struct device *self, void *aux) { struct iatp_softc *sc = (struct iatp_softc *)self; struct i2c_attach_args *ia = aux; struct wsmousedev_attach_args wsmaa; sc->sc_tag = ia->ia_tag; sc->sc_addr = ia->ia_addr; if (ia->ia_cookie != NULL) memcpy(&sc->sc_hid, ia->ia_cookie, sizeof(sc->sc_hid)); if (!iatp_init(sc)) return; if (ia->ia_intr) { printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, IPL_TTY, iatp_intr, sc, sc->sc_dev.dv_xname); if (sc->sc_ih == NULL) { printf(", can't establish interrupt\n"); return; } } printf(": Atmel maXTouch Touch%s (%dx%d)\n", sc->sc_touchpad ? "pad" : "screen", sc->max_x, sc->max_y); wsmaa.accessops = &iatp_accessops; wsmaa.accesscookie = sc; sc->sc_wsmousedev = config_found(self, &wsmaa, wsmousedevprint); } int iatp_detach(struct device *self, int flags) { struct iatp_softc *sc = (struct iatp_softc *)self; if (sc->sc_ih != NULL) { intr_disestablish(sc->sc_ih); sc->sc_ih = NULL; } sc->sc_enabled = 0; return 0; } int iatp_activate(struct device *self, int act) { struct iatp_softc *sc = (struct iatp_softc *)self; switch (act) { case DVACT_QUIESCE: #if 0 /* XXX: causes dwiic troubles */ iatp_t7_set_power_mode(sc, MXT_T7_POWER_MODE_DEEP_SLEEP); #endif break; case DVACT_WAKEUP: sc->sc_busy = 1; iatp_init(sc); sc->sc_busy = 0; break; } config_activate_children(self, act); return 0; } int iatp_enable(void *v) { struct iatp_softc *sc = v; if (sc->sc_busy && tsleep(&sc->sc_busy, PRIBIO, "iatp", hz) != 0) { printf("%s: trying to enable but we're busy\n", sc->sc_dev.dv_xname); return 1; } sc->sc_busy = 1; DPRINTF(("%s: enabling\n", sc->sc_dev.dv_xname)); if (wsmouse_mt_init(sc->sc_wsmousedev, sc->num_touchids, 0)) { printf("%s: failed wsmouse_mt_init\n", sc->sc_dev.dv_xname); return 1; } if (sc->sc_touchpad) wsmouse_set_mode(sc->sc_wsmousedev, WSMOUSE_COMPAT); /* force a read of any pending messages so we start getting new * interrupts */ iatp_t5_read_msgs(sc, sc->info.max_report_id); sc->sc_enabled = 1; sc->sc_busy = 0; return 0; } void iatp_disable(void *v) { struct iatp_softc *sc = v; DPRINTF(("%s: disabling\n", sc->sc_dev.dv_xname)); if (sc->sc_touchpad) wsmouse_set_mode(sc->sc_wsmousedev, WSMOUSE_COMPAT); sc->sc_enabled = 0; } int iatp_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct iatp_softc *sc = v; struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; int wsmode; DPRINTF(("%s: %s: cmd %ld\n", sc->sc_dev.dv_xname, __func__, cmd)); switch (cmd) { case WSMOUSEIO_SCALIBCOORDS: sc->sc_tsscale.minx = wsmc->minx; sc->sc_tsscale.maxx = wsmc->maxx; sc->sc_tsscale.miny = wsmc->miny; sc->sc_tsscale.maxy = wsmc->maxy; sc->sc_tsscale.swapxy = wsmc->swapxy; sc->sc_tsscale.resx = wsmc->resx; sc->sc_tsscale.resy = wsmc->resy; break; case WSMOUSEIO_GCALIBCOORDS: wsmc->minx = sc->sc_tsscale.minx; wsmc->maxx = sc->sc_tsscale.maxx; wsmc->miny = sc->sc_tsscale.miny; wsmc->maxy = sc->sc_tsscale.maxy; wsmc->swapxy = sc->sc_tsscale.swapxy; wsmc->resx = sc->sc_tsscale.resx; wsmc->resy = sc->sc_tsscale.resy; break; case WSMOUSEIO_GTYPE: if (sc->sc_touchpad) *(u_int *)data = WSMOUSE_TYPE_SYNAPTICS; else *(u_int *)data = WSMOUSE_TYPE_TPANEL; break; case WSMOUSEIO_SETMODE: if (!sc->sc_touchpad) return -1; wsmode = *(u_int *)data; if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) { printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname, wsmode); return EINVAL; } wsmouse_set_mode(sc->sc_wsmousedev, wsmode); break; default: return -1; } return 0; } int iatp_init(struct iatp_softc *sc) { uint8_t reportid; int i; sc->sc_enabled = 0; /* some sane defaults */ sc->num_touchids = 10; sc->max_x = 1023; sc->max_y = 1023; sc->sc_touchpad = 0; /* * AT42QT1085 Information block: * * ID information (struct mxt_id_info) * 0 Family ID * 1 Variant ID * 2 Version * 3 Build * 4 Number of Keys * 5 1 * 6 Number of Object Table Elements * Object Table Element 1 (struct mxt_object) * 7 Object Type * 8-9 Object Start Address * 10 Size - 1 * 11 Instances - 1 * 12 Number of report IDs per instance * Object Table Element 2 (struct mxt_object) * ... * Information Block Checksum * [ Object 1 ] * ... */ /* read table header */ if (iatp_read_reg(sc, 0, sizeof(struct mxt_id_info), &sc->info.id) || !sc->info.id.num_objects) { printf("%s: failed reading main memory map\n", sc->sc_dev.dv_xname); return 0; } sc->table_size = sc->info.id.num_objects * sizeof(struct mxt_object); sc->table = malloc(sc->table_size, M_DEVBUF, M_NOWAIT | M_ZERO); /* read all table objects */ if (iatp_read_reg(sc, sizeof(struct mxt_id_info), sc->table_size, sc->table)) { printf("%s: failed reading info table of size %zu\n", sc->sc_dev.dv_xname, sc->table_size); return 0; } reportid = 1; for (i = 0; i < sc->info.id.num_objects; i++) { struct mxt_object *object = (void *)(sc->table + (sizeof(struct mxt_object) * i)); int min_id = 0, max_id = 0; if (object->num_report_ids) { min_id = reportid; reportid += (object->num_report_ids * (uint8_t)MXT_INSTANCES(object)); max_id = reportid - 1; } DPRINTF(("%s: object[%d] T%d at 0x%x, %d report ids (%d-%d)\n", sc->sc_dev.dv_xname, i, object->type, le16toh(object->start_pos), object->num_report_ids, min_id, max_id)); switch (object->type) { case MXT_GEN_MESSAGEPROCESSOR_T5: /* * 4.2 - message processor is what interrupts and * relays new messages to us */ if (sc->info.id.family == 0x80 && sc->info.id.version < 0x20) /* * from linux: "On mXT224 firmware versions * prior to V2.0 read and discard unused CRC * byte otherwise DMA reads are misaligned." */ sc->t5_msg_size = MXT_SIZE(object); else sc->t5_msg_size = MXT_SIZE(object) - 1; sc->t5_address = le16toh(object->start_pos); break; case MXT_GEN_COMMANDPROCESSOR_T6: /* * 4.3 - command processor receives commands from us * and reports command status messages */ sc->t6_address = le16toh(object->start_pos); sc->t6_reportid = min_id; break; case MXT_GEN_POWERCONFIG_T7: /* * 4.4 - power configuration, number of milliseconds * between sampling in each mode */ sc->t7_address = le16toh(object->start_pos); iatp_read_reg(sc, sc->t7_address, sizeof(sc->t7_config), &sc->t7_config); break; case MXT_SPT_GPIOPWM_T19: { /* * generic gpio pin, mapped to touchpad button(s) */ const struct mxt_t19_button_map *m; sc->t19_reportid = min_id; /* find this machine's button config */ sc->t19_button_bit = -1; if (hw_vendor == NULL || hw_prod == NULL || sc->sc_hid == NULL) break; for (m = mxt_t19_button_map_devs; m->vendor != NULL; m++) { if (strncmp(hw_vendor, m->vendor, strlen(m->vendor)) != 0 || strncmp(hw_prod, m->product, strlen(m->product)) != 0 || strncmp(sc->sc_hid, m->hid, strlen(m->hid)) != 0) continue; DPRINTF(("%s: found matching t19 " "button map device \"%s\"/\"%s\" on %s: " "bit %d\n", sc->sc_dev.dv_xname, m->vendor, m->product, m->hid, m->bit)); sc->t19_button_bit = m->bit; break; } if (sc->t19_button_bit > -1) sc->sc_touchpad = 1; break; } case MXT_SPT_MESSAGECOUNT_T44: sc->t44_address = le16toh(object->start_pos); break; case MXT_TOUCH_MULTITOUCHSCREEN_T100: { uint16_t range_x, range_y; uint8_t orient, tchaux; int aux; sc->t100_reportid_min = min_id; sc->t100_reportid_max = max_id; sc->num_touchids = object->num_report_ids - 2; sc->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; if (iatp_read_reg(sc, object->start_pos + MXT_T100_XRANGE, sizeof(range_x), &range_x) || iatp_read_reg(sc, object->start_pos + MXT_T100_YRANGE, sizeof(range_y), &range_y) || iatp_read_reg(sc, object->start_pos + MXT_T100_CFG1, 1, &orient) || iatp_read_reg(sc, object->start_pos + MXT_T100_TCHAUX, 1, &tchaux)) { printf("%s: failed reading t100 settings\n", sc->sc_dev.dv_xname); continue; } /* * orient just affects the size we read, not the x/y * values we read per-packet later. */ if (orient & MXT_T100_CFG_SWITCHXY) { sc->max_x = le16toh(range_y); sc->max_y = le16toh(range_x); } else { sc->max_x = le16toh(range_x); sc->max_y = le16toh(range_y); } aux = 6; if (tchaux & MXT_T100_TCHAUX_VECT) sc->t100_aux_vect = aux++; if (tchaux & MXT_T100_TCHAUX_AMPL) sc->t100_aux_ampl = aux++; if (tchaux & MXT_T100_TCHAUX_AREA) sc->t100_aux_area = aux++; break; } } } sc->info.max_report_id = reportid; sc->sc_tsscale.minx = 0; sc->sc_tsscale.maxx = sc->max_x; sc->sc_tsscale.miny = 0; sc->sc_tsscale.maxy = sc->max_y; sc->sc_tsscale.swapxy = 0; sc->sc_tsscale.resx = 0; sc->sc_tsscale.resy = 0; /* * iatp_t44_read_count expects t5 message processor to immediately * follow t44 message count byte */ if (sc->t44_address && (sc->t5_address != sc->t44_address + 1)) { printf("%s: t5 address (0x%x) != t44 (0x%x + 1)\n", sc->sc_dev.dv_xname, sc->t5_address, sc->t44_address); return 0; } sc->msg_buf = mallocarray(sc->info.max_report_id, sc->t5_msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); /* flush queue of any pending messages */ iatp_t5_read_msgs(sc, sc->info.max_report_id); return 1; } int iatp_read_reg(struct iatp_softc *sc, uint16_t reg, size_t len, void *val) { uint8_t cmd[2] = { reg & 0xff, (reg >> 8) & 0xff }; int ret; iic_acquire_bus(sc->sc_tag, 0); ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &cmd, sizeof(cmd), val, len, I2C_F_POLL); iic_release_bus(sc->sc_tag, 0); return ret; } int iatp_write_reg(struct iatp_softc *sc, uint16_t reg, size_t len, void *val) { int ret; uint8_t *cmd; cmd = malloc(len + 2, M_DEVBUF, M_NOWAIT | M_ZERO); cmd[0] = reg & 0xff; cmd[1] = (reg >> 8) & 0xff; memcpy(&cmd[2], val, len); iic_acquire_bus(sc->sc_tag, 0); ret = iic_exec(sc->sc_tag, I2C_OP_WRITE, sc->sc_addr, cmd, len + 2, NULL, 0, I2C_F_POLL); iic_release_bus(sc->sc_tag, 0); free(cmd, M_DEVBUF, sizeof(len + 2)); return ret; } int iatp_intr(void *arg) { struct iatp_softc *sc = arg; int count; DPRINTF(("%s: %s (busy:%d enabled:%d)\n", sc->sc_dev.dv_xname, __func__, sc->sc_busy, sc->sc_enabled)); if (sc->sc_busy) return 1; sc->sc_busy = 1; if (sc->t44_address) count = iatp_t44_read_count(sc); else count = 1; if (count) iatp_t5_read_msgs(sc, count); sc->sc_busy = 0; wakeup(&sc->sc_busy); return 1; } int iatp_proc_msg(struct iatp_softc *sc, uint8_t *msg) { uint8_t report_id = msg[0]; int i; /* process a single message that has already been read off the wire */ if (report_id == 0xff) /* * this is usually when we've intentionally over-read just to * clear any pending data to keep interrupts flowing */ return 0; DPRINTF(("%s: %s: report id %d\n", sc->sc_dev.dv_xname, __func__, report_id)); if (report_id == sc->t19_reportid) iatp_t19_proc_msg(sc, msg); else if (report_id >= sc->t100_reportid_min && report_id <= sc->t100_reportid_max) iatp_t100_proc_msg(sc, msg); else { DPRINTF(("%s: unknown message (report id %d)", sc->sc_dev.dv_xname, report_id)); for (i = 0; i < sc->t5_msg_size; i++) DPRINTF((" %02x", msg[i])); DPRINTF(("\n")); } return 1; } int iatp_t5_read_msgs(struct iatp_softc *sc, int count) { int i; if (count > sc->info.max_report_id) { DPRINTF(("%s: clamping count %d to max_report_id %d\n", sc->sc_dev.dv_xname, count, sc->info.max_report_id)); count = sc->info.max_report_id; } DPRINTF(("%s: %s: %d message(s) to read\n", sc->sc_dev.dv_xname, __func__, count)); if (iatp_read_reg(sc, sc->t5_address, sc->t5_msg_size * count, sc->msg_buf)) { printf("%s: failed reading %d\n", sc->sc_dev.dv_xname, sc->t5_msg_size * count); return 0; } for (i = 0; i < count; i++) iatp_proc_msg(sc, sc->msg_buf + (sc->t5_msg_size * i)); return 1; } void iatp_t6_proc_msg(struct iatp_softc *sc, uint8_t *msg) { uint8_t status = msg[1]; if (status & MXT_T6_STATUS_RESET) DPRINTF(("%s: completed reset\n", sc->sc_dev.dv_xname)); else DPRINTF(("%s: other status report 0x%x\n", sc->sc_dev.dv_xname, status)); } int iatp_t7_set_power_mode(struct iatp_softc *sc, int mode) { struct mxt_t7_config new_config; if (mode == MXT_T7_POWER_MODE_DEEP_SLEEP) { new_config.idle = 0; new_config.active = 0; new_config.atoi_timeout = 0; } else new_config = sc->t7_config; DPRINTF(("%s: setting power mode to %d\n", sc->sc_dev.dv_xname, mode)); if (iatp_write_reg(sc, sc->t7_address, sizeof(new_config), &new_config)) { printf("%s: failed setting power mode to %d\n", sc->sc_dev.dv_xname, mode); return 1; } return 0; } void iatp_t19_proc_msg(struct iatp_softc *sc, uint8_t *msg) { int s; if (!sc->sc_enabled) return; /* active-low switch */ sc->button = !(msg[1] & (1 << sc->t19_button_bit)); DPRINTF(("%s: button is %d\n", sc->sc_dev.dv_xname, sc->button)); s = spltty(); wsmouse_buttons(sc->sc_wsmousedev, sc->button); wsmouse_input_sync(sc->sc_wsmousedev); splx(s); } int iatp_t44_read_count(struct iatp_softc *sc) { int ret, count; /* read t44 count byte and t5 message data in one shot */ ret = iatp_read_reg(sc, sc->t44_address, 1 + sc->t5_msg_size, sc->msg_buf); if (ret) { printf("%s: failed reading t44 and t5\n", sc->sc_dev.dv_xname); return 0; } count = sc->msg_buf[0]; if (count == 0) { DPRINTF(("%s: %s: no messages\n", sc->sc_dev.dv_xname, __func__)); /* flush so we keep getting interrupts */ iatp_t5_read_msgs(sc, sc->info.max_report_id); return 0; } count--; iatp_proc_msg(sc, sc->msg_buf + 1); return count; } void iatp_t100_proc_msg(struct iatp_softc *sc, uint8_t *msg) { int id = msg[0] - sc->t100_reportid_min - 2; int s; uint8_t status, type = 0, pressure = 0; uint16_t x, y; if (id < 0 || !sc->sc_enabled) return; status = msg[1]; x = (msg[3] << 8) | msg[2]; y = (msg[5] << 8) | msg[4]; if (status & MXT_T100_DETECT) { type = (status & MXT_T100_TYPE_MASK) >> 4; if (sc->t100_aux_ampl) pressure = msg[sc->t100_aux_ampl]; if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER) pressure = 50; /* large enough for synaptics driver */ DPRINTF(("%s: type=%d x=%d y=%d finger=%d pressure=%d " "button=%d\n", sc->sc_dev.dv_xname, type, x, y, id, pressure, sc->button)); } else { DPRINTF(("%s: closing slot for finger=%d\n", sc->sc_dev.dv_xname, id)); if (sc->sc_touchpad) x = y = 0; pressure = 0; } if (sc->sc_touchpad) y = (sc->max_y - y); /* TODO: adjust to sc_tsscale? */ s = spltty(); wsmouse_mtstate(sc->sc_wsmousedev, id, x, y, pressure); /* on the touchscreen, assume any finger down is clicking */ if (!sc->sc_touchpad) wsmouse_buttons(sc->sc_wsmousedev, pressure ? 1 : 0); wsmouse_input_sync(sc->sc_wsmousedev); splx(s); }