1*cf372defSderaadt /* $OpenBSD: pms.c,v 1.99 2024/08/18 15:09:49 deraadt Exp $ */ 24026eccfSkettenis /* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */ 34026eccfSkettenis 44026eccfSkettenis /*- 54026eccfSkettenis * Copyright (c) 1994 Charles M. Hannum. 64026eccfSkettenis * Copyright (c) 1992, 1993 Erik Forsberg. 74026eccfSkettenis * All rights reserved. 84026eccfSkettenis * 94026eccfSkettenis * Redistribution and use in source and binary forms, with or without 104026eccfSkettenis * modification, are permitted provided that the following conditions 114026eccfSkettenis * are met: 124026eccfSkettenis * 1. Redistributions of source code must retain the above copyright 134026eccfSkettenis * notice, this list of conditions and the following disclaimer. 144026eccfSkettenis * 154026eccfSkettenis * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 164026eccfSkettenis * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 174026eccfSkettenis * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 184026eccfSkettenis * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 194026eccfSkettenis * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 204026eccfSkettenis * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 214026eccfSkettenis * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 224026eccfSkettenis * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 234026eccfSkettenis * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 244026eccfSkettenis * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 254026eccfSkettenis */ 264026eccfSkettenis 274026eccfSkettenis #include <sys/param.h> 284026eccfSkettenis #include <sys/systm.h> 290649de23Smpi #include <sys/rwlock.h> 304026eccfSkettenis #include <sys/device.h> 314026eccfSkettenis #include <sys/ioctl.h> 32aaab154dSshadchin #include <sys/malloc.h> 330fcbbc5fSanton #include <sys/task.h> 340fcbbc5fSanton #include <sys/timeout.h> 354026eccfSkettenis 364026eccfSkettenis #include <machine/bus.h> 374026eccfSkettenis 384026eccfSkettenis #include <dev/ic/pckbcvar.h> 394026eccfSkettenis 404026eccfSkettenis #include <dev/pckbc/pmsreg.h> 414026eccfSkettenis 424026eccfSkettenis #include <dev/wscons/wsconsio.h> 434026eccfSkettenis #include <dev/wscons/wsmousevar.h> 444026eccfSkettenis 45ac6c6540Sjsg #if defined(__i386__) || defined(__amd64__) 46ac6c6540Sjsg #include "acpi.h" 47ac6c6540Sjsg #endif 48ac6c6540Sjsg 49ac6c6540Sjsg #if !defined(SMALL_KERNEL) && NACPI > 0 50ac6c6540Sjsg extern int mouse_has_softbtn; 51ac6c6540Sjsg #else 52ac6c6540Sjsg int mouse_has_softbtn; 53ac6c6540Sjsg #endif 54ac6c6540Sjsg 557327fb1eSmpi #ifdef DEBUG 567327fb1eSmpi #define DPRINTF(x...) do { printf(x); } while (0); 577327fb1eSmpi #else 587327fb1eSmpi #define DPRINTF(x...) 597327fb1eSmpi #endif 607327fb1eSmpi 6155fddf67Skrw #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 6255fddf67Skrw 63aaab154dSshadchin #define WSMOUSE_BUTTON(x) (1 << ((x) - 1)) 64aaab154dSshadchin 65f7577b15Sshadchin struct pms_softc; 66f7577b15Sshadchin 67f7577b15Sshadchin struct pms_protocol { 68f7577b15Sshadchin int type; 69f7577b15Sshadchin #define PMS_STANDARD 0 70f7577b15Sshadchin #define PMS_INTELLI 1 71aaab154dSshadchin #define PMS_SYNAPTICS 2 727327fb1eSmpi #define PMS_ALPS 3 7372ec3668Sstsp #define PMS_ELANTECH_V1 4 7472ec3668Sstsp #define PMS_ELANTECH_V2 5 7572ec3668Sstsp #define PMS_ELANTECH_V3 6 767f1c67caSjcs #define PMS_ELANTECH_V4 7 77f7577b15Sshadchin u_int packetsize; 78f7577b15Sshadchin int (*enable)(struct pms_softc *); 79f7577b15Sshadchin int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *); 80f7577b15Sshadchin int (*sync)(struct pms_softc *, int); 81f7577b15Sshadchin void (*proc)(struct pms_softc *); 82f7577b15Sshadchin void (*disable)(struct pms_softc *); 83f7577b15Sshadchin }; 84f7577b15Sshadchin 85aaab154dSshadchin struct synaptics_softc { 86aaab154dSshadchin int identify; 87dc9b7ba2Sjsg int capabilities, ext_capabilities, ext2_capabilities; 88aaab154dSshadchin int model, ext_model; 89dc9b7ba2Sjsg int modes; 90aaab154dSshadchin 91aaab154dSshadchin int mode; 92aaab154dSshadchin 9343a3c8cdSshadchin int mask; 9443a3c8cdSshadchin #define SYNAPTICS_MASK_NEWABS_STRICT 0xc8 9543a3c8cdSshadchin #define SYNAPTICS_MASK_NEWABS_RELAXED 0xc0 9643a3c8cdSshadchin #define SYNAPTICS_VALID_NEWABS_FIRST 0x80 9743a3c8cdSshadchin #define SYNAPTICS_VALID_NEWABS_NEXT 0xc0 9843a3c8cdSshadchin 99dc9b7ba2Sjsg u_int sec_buttons; 100dc727cfdSbru 101dc727cfdSbru #define SYNAPTICS_PRESSURE_HI 30 102dc727cfdSbru #define SYNAPTICS_PRESSURE_LO 25 103dc727cfdSbru #define SYNAPTICS_PRESSURE SYNAPTICS_PRESSURE_HI 104aaab154dSshadchin #define SYNAPTICS_SCALE 4 105dc727cfdSbru #define SYNAPTICS_MAX_FINGERS 3 106aaab154dSshadchin }; 107aaab154dSshadchin 1087327fb1eSmpi struct alps_softc { 1097327fb1eSmpi int model; 110b2dcd1e6Sshadchin #define ALPS_GLIDEPOINT (1 << 1) 111b2dcd1e6Sshadchin #define ALPS_DUALPOINT (1 << 2) 112b2dcd1e6Sshadchin #define ALPS_PASSTHROUGH (1 << 3) 113b2dcd1e6Sshadchin #define ALPS_INTERLEAVED (1 << 4) 114b2dcd1e6Sshadchin 115dd24e85bSmpi int mask; 1167327fb1eSmpi int version; 1177327fb1eSmpi 1182445272cSbru u_int gesture; 1197327fb1eSmpi 120b2dcd1e6Sshadchin u_int sec_buttons; /* trackpoint */ 121b2dcd1e6Sshadchin 1227327fb1eSmpi int old_x, old_y; 1237327fb1eSmpi #define ALPS_PRESSURE 40 1247327fb1eSmpi }; 1257327fb1eSmpi 12672ec3668Sstsp struct elantech_softc { 12772ec3668Sstsp int flags; 12872ec3668Sstsp #define ELANTECH_F_REPORTS_PRESSURE 0x01 12972ec3668Sstsp #define ELANTECH_F_HAS_ROCKER 0x02 13072ec3668Sstsp #define ELANTECH_F_2FINGER_PACKET 0x04 13172ec3668Sstsp #define ELANTECH_F_HW_V1_OLD 0x08 13240f7e3e4Sstsp #define ELANTECH_F_CRC_ENABLED 0x10 1338e9e9c10Sbru #define ELANTECH_F_TRACKPOINT 0x20 1347b88a336Sstsp int fw_version; 13572ec3668Sstsp 1365ad8c6a9Sbru u_int mt_slots; 1373c37ce5cSmpi 1387f1c67caSjcs int width; 13972ec3668Sstsp 14072ec3668Sstsp u_char parity[256]; 14172ec3668Sstsp u_char p1, p2, p3; 14272ec3668Sstsp 1434101186eSbru int max_x, max_y; 14472ec3668Sstsp int old_x, old_y; 1456e9b9891Sbru int initial_pkt; 14672ec3668Sstsp }; 1479665f110Sbru #define ELANTECH_IS_CLICKPAD(sc) (((sc)->elantech->fw_version & 0x1000) != 0) 14872ec3668Sstsp 1494026eccfSkettenis struct pms_softc { /* driver status information */ 1504026eccfSkettenis struct device sc_dev; 1514026eccfSkettenis 1524026eccfSkettenis pckbc_tag_t sc_kbctag; 1534026eccfSkettenis 15489d55430Sderaadt int sc_state; 15589d55430Sderaadt #define PMS_STATE_DISABLED 0 15689d55430Sderaadt #define PMS_STATE_ENABLED 1 15789d55430Sderaadt #define PMS_STATE_SUSPENDED 2 1588cd42f84Smiod 1590649de23Smpi struct rwlock sc_state_lock; 1600649de23Smpi 161aaab154dSshadchin int sc_dev_enable; 162aaab154dSshadchin #define PMS_DEV_IGNORE 0x00 163aaab154dSshadchin #define PMS_DEV_PRIMARY 0x01 164aaab154dSshadchin #define PMS_DEV_SECONDARY 0x02 165aaab154dSshadchin 1660fcbbc5fSanton struct task sc_rsttask; 1670fcbbc5fSanton struct timeout sc_rsttimo; 1680fcbbc5fSanton int sc_rststate; 1690fcbbc5fSanton #define PMS_RST_COMMENCE 0x01 1700fcbbc5fSanton #define PMS_RST_ANNOUNCED 0x02 1710fcbbc5fSanton 17275d69285Skrw int poll; 1734026eccfSkettenis int inputstate; 174f7577b15Sshadchin 175f7577b15Sshadchin const struct pms_protocol *protocol; 176aaab154dSshadchin struct synaptics_softc *synaptics; 1777327fb1eSmpi struct alps_softc *alps; 17872ec3668Sstsp struct elantech_softc *elantech; 179f7577b15Sshadchin 180f7577b15Sshadchin u_char packet[8]; 1814026eccfSkettenis 1824026eccfSkettenis struct device *sc_wsmousedev; 183f96d6147Sshadchin struct device *sc_sec_wsmousedev; 1844026eccfSkettenis }; 1854026eccfSkettenis 186f7577b15Sshadchin static const u_int butmap[8] = { 187f7577b15Sshadchin 0, 188aaab154dSshadchin WSMOUSE_BUTTON(1), 189aaab154dSshadchin WSMOUSE_BUTTON(3), 190aaab154dSshadchin WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3), 191aaab154dSshadchin WSMOUSE_BUTTON(2), 192aaab154dSshadchin WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2), 193aaab154dSshadchin WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3), 194aaab154dSshadchin WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3) 195f7577b15Sshadchin }; 196f7577b15Sshadchin 1977327fb1eSmpi static const struct alps_model { 1987327fb1eSmpi int version; 199dd24e85bSmpi int mask; 2007327fb1eSmpi int model; 2017327fb1eSmpi } alps_models[] = { 202dd24e85bSmpi { 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 203dd24e85bSmpi { 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 204dd24e85bSmpi { 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 205dd24e85bSmpi { 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 206b2dcd1e6Sshadchin { 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED }, 207dd24e85bSmpi { 0x5321, 0xf8, ALPS_GLIDEPOINT }, 208dd24e85bSmpi { 0x5322, 0xf8, ALPS_GLIDEPOINT }, 209dd24e85bSmpi { 0x603b, 0xf8, ALPS_GLIDEPOINT }, 210b2dcd1e6Sshadchin { 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED }, 211dd24e85bSmpi { 0x6321, 0xf8, ALPS_GLIDEPOINT }, 212dd24e85bSmpi { 0x6322, 0xf8, ALPS_GLIDEPOINT }, 213dd24e85bSmpi { 0x6323, 0xf8, ALPS_GLIDEPOINT }, 214dd24e85bSmpi { 0x6324, 0x8f, ALPS_GLIDEPOINT }, 215dd24e85bSmpi { 0x6325, 0xef, ALPS_GLIDEPOINT }, 216dd24e85bSmpi { 0x6326, 0xf8, ALPS_GLIDEPOINT }, 217419d60bbSmpi { 0x7301, 0xf8, ALPS_DUALPOINT }, 218dd24e85bSmpi { 0x7321, 0xf8, ALPS_GLIDEPOINT }, 219dd24e85bSmpi { 0x7322, 0xf8, ALPS_GLIDEPOINT }, 220dd24e85bSmpi { 0x7325, 0xcf, ALPS_GLIDEPOINT }, 2217327fb1eSmpi #if 0 2222d383e6dSmpi /* 2232d383e6dSmpi * This model has a clitpad sending almost compatible PS2 2242d383e6dSmpi * packets but not compatible enough to be used with the 2252d383e6dSmpi * ALPS protocol. 2262d383e6dSmpi */ 2272d383e6dSmpi { 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 2282d383e6dSmpi 229dd24e85bSmpi { 0x7326, 0, 0 }, /* XXX Uses unknown v3 protocol */ 230529d4085Sbru 231529d4085Sbru { 0x7331, 0x8f, ALPS_DUALPOINT }, /* not supported */ 2327327fb1eSmpi #endif 2337327fb1eSmpi }; 2347327fb1eSmpi 235dc727cfdSbru static struct wsmouse_param synaptics_params[] = { 236dc727cfdSbru { WSMOUSECFG_PRESSURE_LO, SYNAPTICS_PRESSURE_LO }, 237dc727cfdSbru { WSMOUSECFG_PRESSURE_HI, SYNAPTICS_PRESSURE_HI } 238dc727cfdSbru }; 239dc727cfdSbru 240995fbf01Sbru static struct wsmouse_param alps_params[] = { 241995fbf01Sbru { WSMOUSECFG_SMOOTHING, 3 } 242995fbf01Sbru }; 243995fbf01Sbru 2444026eccfSkettenis int pmsprobe(struct device *, void *, void *); 2454026eccfSkettenis void pmsattach(struct device *, struct device *, void *); 24689d55430Sderaadt int pmsactivate(struct device *, int); 2474026eccfSkettenis 24888955b28Skrw void pmsinput(void *, int); 2494026eccfSkettenis 250aaab154dSshadchin int pms_change_state(struct pms_softc *, int, int); 251f96d6147Sshadchin 2524026eccfSkettenis int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); 25389d55430Sderaadt int pms_enable(void *); 2544026eccfSkettenis void pms_disable(void *); 2554026eccfSkettenis 256f96d6147Sshadchin int pms_sec_ioctl(void *, u_long, caddr_t, int, struct proc *); 257f96d6147Sshadchin int pms_sec_enable(void *); 258f96d6147Sshadchin void pms_sec_disable(void *); 259f96d6147Sshadchin 26075d69285Skrw int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int); 261aaab154dSshadchin int pms_spec_cmd(struct pms_softc *, int); 26255fddf67Skrw int pms_get_devid(struct pms_softc *, u_char *); 26355fddf67Skrw int pms_get_status(struct pms_softc *, u_char *); 26455fddf67Skrw int pms_set_rate(struct pms_softc *, int); 26555fddf67Skrw int pms_set_resolution(struct pms_softc *, int); 26655fddf67Skrw int pms_set_scaling(struct pms_softc *, int); 26755fddf67Skrw int pms_reset(struct pms_softc *); 26855fddf67Skrw int pms_dev_enable(struct pms_softc *); 26955fddf67Skrw int pms_dev_disable(struct pms_softc *); 270f6323c95Smpi void pms_protocol_lookup(struct pms_softc *); 2710fcbbc5fSanton void pms_reset_detect(struct pms_softc *, int); 2720fcbbc5fSanton void pms_reset_task(void *); 2730fcbbc5fSanton void pms_reset_timo(void *); 27475d69285Skrw 275f7577b15Sshadchin int pms_enable_intelli(struct pms_softc *); 276f7577b15Sshadchin 277f7577b15Sshadchin int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *); 278f7577b15Sshadchin int pms_sync_mouse(struct pms_softc *, int); 279f7577b15Sshadchin void pms_proc_mouse(struct pms_softc *); 2808cd42f84Smiod 281aaab154dSshadchin int pms_enable_synaptics(struct pms_softc *); 282aaab154dSshadchin int pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *); 283aaab154dSshadchin int pms_sync_synaptics(struct pms_softc *, int); 284aaab154dSshadchin void pms_proc_synaptics(struct pms_softc *); 285aaab154dSshadchin void pms_disable_synaptics(struct pms_softc *); 286aaab154dSshadchin 2877327fb1eSmpi int pms_enable_alps(struct pms_softc *); 2887327fb1eSmpi int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *); 2897327fb1eSmpi int pms_sync_alps(struct pms_softc *, int); 2907327fb1eSmpi void pms_proc_alps(struct pms_softc *); 2917327fb1eSmpi 29272ec3668Sstsp int pms_enable_elantech_v1(struct pms_softc *); 29372ec3668Sstsp int pms_enable_elantech_v2(struct pms_softc *); 29472ec3668Sstsp int pms_enable_elantech_v3(struct pms_softc *); 2957f1c67caSjcs int pms_enable_elantech_v4(struct pms_softc *); 29672ec3668Sstsp int pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int, 29772ec3668Sstsp struct proc *); 29872ec3668Sstsp int pms_sync_elantech_v1(struct pms_softc *, int); 29972ec3668Sstsp int pms_sync_elantech_v2(struct pms_softc *, int); 30072ec3668Sstsp int pms_sync_elantech_v3(struct pms_softc *, int); 3017f1c67caSjcs int pms_sync_elantech_v4(struct pms_softc *, int); 30272ec3668Sstsp void pms_proc_elantech_v1(struct pms_softc *); 30372ec3668Sstsp void pms_proc_elantech_v2(struct pms_softc *); 30472ec3668Sstsp void pms_proc_elantech_v3(struct pms_softc *); 3057f1c67caSjcs void pms_proc_elantech_v4(struct pms_softc *); 30672ec3668Sstsp 307748e7d13Sstsp int synaptics_knock(struct pms_softc *); 3080329de75Smglocker int synaptics_set_mode(struct pms_softc *, int, int); 309aaab154dSshadchin int synaptics_query(struct pms_softc *, int, int *); 310aaab154dSshadchin int synaptics_get_hwinfo(struct pms_softc *); 311f96d6147Sshadchin void synaptics_sec_proc(struct pms_softc *); 312aaab154dSshadchin 313b2dcd1e6Sshadchin int alps_sec_proc(struct pms_softc *); 3147327fb1eSmpi int alps_get_hwinfo(struct pms_softc *); 3157327fb1eSmpi 31672ec3668Sstsp int elantech_knock(struct pms_softc *); 31772ec3668Sstsp int elantech_get_hwinfo_v1(struct pms_softc *); 31872ec3668Sstsp int elantech_get_hwinfo_v2(struct pms_softc *); 31972ec3668Sstsp int elantech_get_hwinfo_v3(struct pms_softc *); 3207f1c67caSjcs int elantech_get_hwinfo_v4(struct pms_softc *); 32172ec3668Sstsp int elantech_ps2_cmd(struct pms_softc *, u_char); 32272ec3668Sstsp int elantech_set_absolute_mode_v1(struct pms_softc *); 32372ec3668Sstsp int elantech_set_absolute_mode_v2(struct pms_softc *); 32472ec3668Sstsp int elantech_set_absolute_mode_v3(struct pms_softc *); 3257f1c67caSjcs int elantech_set_absolute_mode_v4(struct pms_softc *); 32672ec3668Sstsp 327471aeecfSnaddy const struct cfattach pms_ca = { 32888955b28Skrw sizeof(struct pms_softc), pmsprobe, pmsattach, NULL, 32988955b28Skrw pmsactivate 33088955b28Skrw }; 33188955b28Skrw 33288955b28Skrw struct cfdriver pms_cd = { 33388955b28Skrw NULL, "pms", DV_DULL 33488955b28Skrw }; 33588955b28Skrw 3364026eccfSkettenis const struct wsmouse_accessops pms_accessops = { 3374026eccfSkettenis pms_enable, 3384026eccfSkettenis pms_ioctl, 3394026eccfSkettenis pms_disable, 3404026eccfSkettenis }; 3414026eccfSkettenis 342f96d6147Sshadchin const struct wsmouse_accessops pms_sec_accessops = { 343f96d6147Sshadchin pms_sec_enable, 344f96d6147Sshadchin pms_sec_ioctl, 345f96d6147Sshadchin pms_sec_disable, 346aaab154dSshadchin }; 347aaab154dSshadchin 348aaab154dSshadchin const struct pms_protocol pms_protocols[] = { 349f7577b15Sshadchin /* Generic PS/2 mouse */ 350f7577b15Sshadchin { 351f7577b15Sshadchin PMS_STANDARD, 3, 352f7577b15Sshadchin NULL, 353f7577b15Sshadchin pms_ioctl_mouse, 354f7577b15Sshadchin pms_sync_mouse, 355f7577b15Sshadchin pms_proc_mouse, 356f7577b15Sshadchin NULL 357f7577b15Sshadchin }, 358aaab154dSshadchin /* Synaptics touchpad */ 359aaab154dSshadchin { 360aaab154dSshadchin PMS_SYNAPTICS, 6, 361aaab154dSshadchin pms_enable_synaptics, 362aaab154dSshadchin pms_ioctl_synaptics, 363aaab154dSshadchin pms_sync_synaptics, 364aaab154dSshadchin pms_proc_synaptics, 365aaab154dSshadchin pms_disable_synaptics 3667327fb1eSmpi }, 3677327fb1eSmpi /* ALPS touchpad */ 3687327fb1eSmpi { 3697327fb1eSmpi PMS_ALPS, 6, 3707327fb1eSmpi pms_enable_alps, 3717327fb1eSmpi pms_ioctl_alps, 3727327fb1eSmpi pms_sync_alps, 3737327fb1eSmpi pms_proc_alps, 3747327fb1eSmpi NULL 3757327fb1eSmpi }, 37672ec3668Sstsp /* Elantech touchpad (hardware version 1) */ 37772ec3668Sstsp { 37872ec3668Sstsp PMS_ELANTECH_V1, 4, 37972ec3668Sstsp pms_enable_elantech_v1, 38072ec3668Sstsp pms_ioctl_elantech, 38172ec3668Sstsp pms_sync_elantech_v1, 38272ec3668Sstsp pms_proc_elantech_v1, 38372ec3668Sstsp NULL 38472ec3668Sstsp }, 38572ec3668Sstsp /* Elantech touchpad (hardware version 2) */ 38672ec3668Sstsp { 38772ec3668Sstsp PMS_ELANTECH_V2, 6, 38872ec3668Sstsp pms_enable_elantech_v2, 38972ec3668Sstsp pms_ioctl_elantech, 39072ec3668Sstsp pms_sync_elantech_v2, 39172ec3668Sstsp pms_proc_elantech_v2, 39272ec3668Sstsp NULL 39372ec3668Sstsp }, 39472ec3668Sstsp /* Elantech touchpad (hardware version 3) */ 39572ec3668Sstsp { 39672ec3668Sstsp PMS_ELANTECH_V3, 6, 39772ec3668Sstsp pms_enable_elantech_v3, 39872ec3668Sstsp pms_ioctl_elantech, 39972ec3668Sstsp pms_sync_elantech_v3, 40072ec3668Sstsp pms_proc_elantech_v3, 40172ec3668Sstsp NULL 40272ec3668Sstsp }, 4037f1c67caSjcs /* Elantech touchpad (hardware version 4) */ 4047f1c67caSjcs { 4057f1c67caSjcs PMS_ELANTECH_V4, 6, 4067f1c67caSjcs pms_enable_elantech_v4, 4077f1c67caSjcs pms_ioctl_elantech, 4087f1c67caSjcs pms_sync_elantech_v4, 4097f1c67caSjcs pms_proc_elantech_v4, 4107f1c67caSjcs NULL 4117f1c67caSjcs }, 4121a2228f9Smpi /* Microsoft IntelliMouse */ 4131a2228f9Smpi { 4141a2228f9Smpi PMS_INTELLI, 4, 4151a2228f9Smpi pms_enable_intelli, 4161a2228f9Smpi pms_ioctl_mouse, 4171a2228f9Smpi pms_sync_mouse, 4181a2228f9Smpi pms_proc_mouse, 4191a2228f9Smpi NULL 4201a2228f9Smpi }, 421f7577b15Sshadchin }; 422f7577b15Sshadchin 4234026eccfSkettenis int 42475d69285Skrw pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen) 42575d69285Skrw { 42675d69285Skrw if (sc->poll) { 427ac6d5436Stobias return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 42875d69285Skrw cmd, len, resplen, resp, 1); 42975d69285Skrw } else { 430ac6d5436Stobias return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 43175d69285Skrw cmd, len, resplen, 1, resp); 43275d69285Skrw } 43375d69285Skrw } 43475d69285Skrw 43575d69285Skrw int 436aaab154dSshadchin pms_spec_cmd(struct pms_softc *sc, int cmd) 437aaab154dSshadchin { 438aaab154dSshadchin if (pms_set_scaling(sc, 1) || 439aaab154dSshadchin pms_set_resolution(sc, (cmd >> 6) & 0x03) || 440aaab154dSshadchin pms_set_resolution(sc, (cmd >> 4) & 0x03) || 441aaab154dSshadchin pms_set_resolution(sc, (cmd >> 2) & 0x03) || 442aaab154dSshadchin pms_set_resolution(sc, (cmd >> 0) & 0x03)) 443aaab154dSshadchin return (-1); 444aaab154dSshadchin return (0); 445aaab154dSshadchin } 446aaab154dSshadchin 447aaab154dSshadchin int 44855fddf67Skrw pms_get_devid(struct pms_softc *sc, u_char *resp) 4498cd42f84Smiod { 45055fddf67Skrw u_char cmd[1]; 4518cd42f84Smiod 4528cd42f84Smiod cmd[0] = PMS_SEND_DEV_ID; 45355fddf67Skrw return (pms_cmd(sc, cmd, 1, resp, 1)); 45455fddf67Skrw } 45555fddf67Skrw 45655fddf67Skrw int 45755fddf67Skrw pms_get_status(struct pms_softc *sc, u_char *resp) 45855fddf67Skrw { 45955fddf67Skrw u_char cmd[1]; 46055fddf67Skrw 46155fddf67Skrw cmd[0] = PMS_SEND_DEV_STATUS; 46255fddf67Skrw return (pms_cmd(sc, cmd, 1, resp, 3)); 46355fddf67Skrw } 46455fddf67Skrw 46555fddf67Skrw int 46655fddf67Skrw pms_set_rate(struct pms_softc *sc, int value) 46755fddf67Skrw { 46855fddf67Skrw u_char cmd[2]; 46955fddf67Skrw 47055fddf67Skrw cmd[0] = PMS_SET_SAMPLE; 47155fddf67Skrw cmd[1] = value; 47255fddf67Skrw return (pms_cmd(sc, cmd, 2, NULL, 0)); 47355fddf67Skrw } 47455fddf67Skrw 47555fddf67Skrw int 47655fddf67Skrw pms_set_resolution(struct pms_softc *sc, int value) 47755fddf67Skrw { 47855fddf67Skrw u_char cmd[2]; 47955fddf67Skrw 48055fddf67Skrw cmd[0] = PMS_SET_RES; 48155fddf67Skrw cmd[1] = value; 48255fddf67Skrw return (pms_cmd(sc, cmd, 2, NULL, 0)); 48355fddf67Skrw } 48455fddf67Skrw 48555fddf67Skrw int 48655fddf67Skrw pms_set_scaling(struct pms_softc *sc, int scale) 48755fddf67Skrw { 48855fddf67Skrw u_char cmd[1]; 48955fddf67Skrw 49055fddf67Skrw switch (scale) { 49155fddf67Skrw case 1: 49255fddf67Skrw default: 49355fddf67Skrw cmd[0] = PMS_SET_SCALE11; 49455fddf67Skrw break; 49555fddf67Skrw case 2: 49655fddf67Skrw cmd[0] = PMS_SET_SCALE21; 49755fddf67Skrw break; 49855fddf67Skrw } 49955fddf67Skrw return (pms_cmd(sc, cmd, 1, NULL, 0)); 50055fddf67Skrw } 50155fddf67Skrw 50255fddf67Skrw int 50355fddf67Skrw pms_reset(struct pms_softc *sc) 50455fddf67Skrw { 50555fddf67Skrw u_char cmd[1], resp[2]; 50655fddf67Skrw int res; 50755fddf67Skrw 50855fddf67Skrw cmd[0] = PMS_RESET; 50955fddf67Skrw res = pms_cmd(sc, cmd, 1, resp, 2); 51055fddf67Skrw #ifdef DEBUG 51155fddf67Skrw if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) 51255fddf67Skrw printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n", 51355fddf67Skrw DEVNAME(sc), res, resp[0], resp[1]); 51455fddf67Skrw #endif 51555fddf67Skrw return (res); 51655fddf67Skrw } 51755fddf67Skrw 51855fddf67Skrw int 51955fddf67Skrw pms_dev_enable(struct pms_softc *sc) 52055fddf67Skrw { 52155fddf67Skrw u_char cmd[1]; 52255fddf67Skrw int res; 52355fddf67Skrw 52455fddf67Skrw cmd[0] = PMS_DEV_ENABLE; 52555fddf67Skrw res = pms_cmd(sc, cmd, 1, NULL, 0); 52655fddf67Skrw if (res) 52755fddf67Skrw printf("%s: enable error\n", DEVNAME(sc)); 52855fddf67Skrw return (res); 52955fddf67Skrw } 53055fddf67Skrw 53155fddf67Skrw int 53255fddf67Skrw pms_dev_disable(struct pms_softc *sc) 53355fddf67Skrw { 53455fddf67Skrw u_char cmd[1]; 53555fddf67Skrw int res; 53655fddf67Skrw 53755fddf67Skrw cmd[0] = PMS_DEV_DISABLE; 53855fddf67Skrw res = pms_cmd(sc, cmd, 1, NULL, 0); 53955fddf67Skrw if (res) 54055fddf67Skrw printf("%s: disable error\n", DEVNAME(sc)); 54155fddf67Skrw return (res); 54255fddf67Skrw } 54355fddf67Skrw 544f6323c95Smpi void 545f6323c95Smpi pms_protocol_lookup(struct pms_softc *sc) 546f6323c95Smpi { 547f6323c95Smpi int i; 548f6323c95Smpi 549f6323c95Smpi sc->protocol = &pms_protocols[0]; 550f6323c95Smpi for (i = 1; i < nitems(pms_protocols); i++) { 551f6323c95Smpi pms_reset(sc); 552f6323c95Smpi if (pms_protocols[i].enable(sc)) { 553f6323c95Smpi sc->protocol = &pms_protocols[i]; 554f6323c95Smpi break; 555f6323c95Smpi } 556f6323c95Smpi } 557f6323c95Smpi 558f6323c95Smpi DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type); 559f6323c95Smpi } 560f6323c95Smpi 5610fcbbc5fSanton /* 5620fcbbc5fSanton * Detect reset announcement ([0xaa, 0x0]). 5630fcbbc5fSanton * The sequence will be sent as input on rare occasions when the touchpad was 5640fcbbc5fSanton * reset due to a power failure. 5650fcbbc5fSanton */ 5660fcbbc5fSanton void 5670fcbbc5fSanton pms_reset_detect(struct pms_softc *sc, int data) 5680fcbbc5fSanton { 5690fcbbc5fSanton switch (sc->sc_rststate) { 5700fcbbc5fSanton case PMS_RST_COMMENCE: 5710fcbbc5fSanton if (data == 0x0) { 5720fcbbc5fSanton sc->sc_rststate = PMS_RST_ANNOUNCED; 5730fcbbc5fSanton timeout_add_msec(&sc->sc_rsttimo, 100); 5740fcbbc5fSanton } else if (data != PMS_RSTDONE) { 5750fcbbc5fSanton sc->sc_rststate = 0; 5760fcbbc5fSanton } 5770fcbbc5fSanton break; 5780fcbbc5fSanton default: 5790fcbbc5fSanton if (data == PMS_RSTDONE) 5800fcbbc5fSanton sc->sc_rststate = PMS_RST_COMMENCE; 5810fcbbc5fSanton else 5820fcbbc5fSanton sc->sc_rststate = 0; 5830fcbbc5fSanton } 5840fcbbc5fSanton } 5850fcbbc5fSanton 5860fcbbc5fSanton void 5870fcbbc5fSanton pms_reset_timo(void *v) 5880fcbbc5fSanton { 5890fcbbc5fSanton struct pms_softc *sc = v; 5900fcbbc5fSanton int s = spltty(); 5910fcbbc5fSanton 5920fcbbc5fSanton /* 5930fcbbc5fSanton * Do nothing if the reset was a false positive or if the device already 5940fcbbc5fSanton * is disabled. 5950fcbbc5fSanton */ 5960fcbbc5fSanton if (sc->sc_rststate == PMS_RST_ANNOUNCED && 5970fcbbc5fSanton sc->sc_state != PMS_STATE_DISABLED) 5980fcbbc5fSanton task_add(systq, &sc->sc_rsttask); 5990fcbbc5fSanton 6000fcbbc5fSanton splx(s); 6010fcbbc5fSanton } 6020fcbbc5fSanton 6030fcbbc5fSanton void 6040fcbbc5fSanton pms_reset_task(void *v) 6050fcbbc5fSanton { 6060fcbbc5fSanton struct pms_softc *sc = v; 6070fcbbc5fSanton int s = spltty(); 6080fcbbc5fSanton 6090fcbbc5fSanton #ifdef DIAGNOSTIC 6100fcbbc5fSanton printf("%s: device reset (state = %d)\n", DEVNAME(sc), sc->sc_rststate); 6110fcbbc5fSanton #endif 6120fcbbc5fSanton 6130fcbbc5fSanton rw_enter_write(&sc->sc_state_lock); 6140fcbbc5fSanton 6150fcbbc5fSanton if (sc->sc_sec_wsmousedev != NULL) 6160fcbbc5fSanton pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY); 6170fcbbc5fSanton pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY); 6180fcbbc5fSanton 6190fcbbc5fSanton pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY); 6200fcbbc5fSanton if (sc->sc_sec_wsmousedev != NULL) 6210fcbbc5fSanton pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY); 6220fcbbc5fSanton 6230fcbbc5fSanton rw_exit_write(&sc->sc_state_lock); 6240fcbbc5fSanton splx(s); 6250fcbbc5fSanton } 6260fcbbc5fSanton 62755fddf67Skrw int 628f7577b15Sshadchin pms_enable_intelli(struct pms_softc *sc) 62955fddf67Skrw { 63055fddf67Skrw u_char resp; 63155fddf67Skrw 63262890a12Sshadchin /* the special sequence to enable the third button and the roller */ 63362890a12Sshadchin if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) || 63462890a12Sshadchin pms_set_rate(sc, PMS_INTELLI_MAGIC2) || 63562890a12Sshadchin pms_set_rate(sc, PMS_INTELLI_MAGIC3) || 63655fddf67Skrw pms_get_devid(sc, &resp) || 63762890a12Sshadchin resp != PMS_INTELLI_ID) 6388cd42f84Smiod return (0); 6398cd42f84Smiod 6408cd42f84Smiod return (1); 6418cd42f84Smiod } 6428cd42f84Smiod 6438cd42f84Smiod int 644f7577b15Sshadchin pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 645f7577b15Sshadchin struct proc *p) 646f7577b15Sshadchin { 647f7577b15Sshadchin int i; 648f7577b15Sshadchin 649f7577b15Sshadchin switch (cmd) { 650f7577b15Sshadchin case WSMOUSEIO_GTYPE: 651f7577b15Sshadchin *(u_int *)data = WSMOUSE_TYPE_PS2; 652f7577b15Sshadchin break; 653f7577b15Sshadchin case WSMOUSEIO_SRES: 654f7577b15Sshadchin i = ((int) *(u_int *)data - 12) / 25; 655f7577b15Sshadchin /* valid values are {0,1,2,3} */ 656f7577b15Sshadchin if (i < 0) 657f7577b15Sshadchin i = 0; 658f7577b15Sshadchin if (i > 3) 659f7577b15Sshadchin i = 3; 660f7577b15Sshadchin 661f7577b15Sshadchin if (pms_set_resolution(sc, i)) 662f7577b15Sshadchin printf("%s: SET_RES command error\n", DEVNAME(sc)); 663f7577b15Sshadchin break; 664f7577b15Sshadchin default: 665f7577b15Sshadchin return (-1); 666f7577b15Sshadchin } 667f7577b15Sshadchin return (0); 668f7577b15Sshadchin } 669f7577b15Sshadchin 670f7577b15Sshadchin int 671f7577b15Sshadchin pms_sync_mouse(struct pms_softc *sc, int data) 672f7577b15Sshadchin { 673f7577b15Sshadchin if (sc->inputstate != 0) 674f7577b15Sshadchin return (0); 675f7577b15Sshadchin 676f7577b15Sshadchin switch (sc->protocol->type) { 677f7577b15Sshadchin case PMS_STANDARD: 678f7577b15Sshadchin if ((data & 0xc0) != 0) 679f7577b15Sshadchin return (-1); 680f7577b15Sshadchin break; 681f7577b15Sshadchin case PMS_INTELLI: 682f7577b15Sshadchin if ((data & 0x08) != 0x08) 683f7577b15Sshadchin return (-1); 684f7577b15Sshadchin break; 685f7577b15Sshadchin } 686f7577b15Sshadchin 687f7577b15Sshadchin return (0); 688f7577b15Sshadchin } 689f7577b15Sshadchin 690f7577b15Sshadchin void 691f7577b15Sshadchin pms_proc_mouse(struct pms_softc *sc) 692f7577b15Sshadchin { 693f7577b15Sshadchin u_int buttons; 694f7577b15Sshadchin int dx, dy, dz; 695f7577b15Sshadchin 696f7577b15Sshadchin buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK]; 697f7577b15Sshadchin dx = (sc->packet[0] & PMS_PS2_XNEG) ? 698f7577b15Sshadchin (int)sc->packet[1] - 256 : sc->packet[1]; 699f7577b15Sshadchin dy = (sc->packet[0] & PMS_PS2_YNEG) ? 700f7577b15Sshadchin (int)sc->packet[2] - 256 : sc->packet[2]; 701f7577b15Sshadchin 7028f1a9338Smpi if (sc->protocol->type == PMS_INTELLI) 703f7577b15Sshadchin dz = (signed char)sc->packet[3]; 7048f1a9338Smpi else 7058f1a9338Smpi dz = 0; 706f7577b15Sshadchin 707ddac655dSbru WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, dz, 0); 708f7577b15Sshadchin } 709f7577b15Sshadchin 710f7577b15Sshadchin int 711b43e75e7Skrw pmsprobe(struct device *parent, void *match, void *aux) 7124026eccfSkettenis { 7134026eccfSkettenis struct pckbc_attach_args *pa = aux; 7144026eccfSkettenis u_char cmd[1], resp[2]; 7154026eccfSkettenis int res; 7164026eccfSkettenis 717ac6d5436Stobias if (pa->pa_slot != PCKBC_AUX_SLOT) 7184026eccfSkettenis return (0); 7194026eccfSkettenis 7204026eccfSkettenis /* Flush any garbage. */ 7214026eccfSkettenis pckbc_flush(pa->pa_tag, pa->pa_slot); 7224026eccfSkettenis 7234026eccfSkettenis /* reset the device */ 7244026eccfSkettenis cmd[0] = PMS_RESET; 7254026eccfSkettenis res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 72653a48ac9Skrw if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 7274026eccfSkettenis #ifdef DEBUG 72853a48ac9Skrw printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n", 72953a48ac9Skrw res, resp[0], resp[1]); 7304026eccfSkettenis #endif 7314026eccfSkettenis return (0); 7324026eccfSkettenis } 7334026eccfSkettenis 734be2abd7eSderaadt return (1); 7354026eccfSkettenis } 7364026eccfSkettenis 7374026eccfSkettenis void 738b43e75e7Skrw pmsattach(struct device *parent, struct device *self, void *aux) 7394026eccfSkettenis { 7404026eccfSkettenis struct pms_softc *sc = (void *)self; 7414026eccfSkettenis struct pckbc_attach_args *pa = aux; 7424026eccfSkettenis struct wsmousedev_attach_args a; 7434026eccfSkettenis 7444026eccfSkettenis sc->sc_kbctag = pa->pa_tag; 7454026eccfSkettenis 746ac6d5436Stobias pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT, 74755fddf67Skrw pmsinput, sc, DEVNAME(sc)); 7484026eccfSkettenis 749627a0107Smpi printf("\n"); 750627a0107Smpi 7514026eccfSkettenis a.accessops = &pms_accessops; 7524026eccfSkettenis a.accesscookie = sc; 7534026eccfSkettenis 7540649de23Smpi rw_init(&sc->sc_state_lock, "pmsst"); 7550649de23Smpi 7564026eccfSkettenis /* 7574026eccfSkettenis * Attach the wsmouse, saving a handle to it. 7584026eccfSkettenis * Note that we don't need to check this pointer against NULL 7594026eccfSkettenis * here or in pmsintr, because if this fails pms_enable() will 7604026eccfSkettenis * never be called, so pmsinput() will never be called. 7614026eccfSkettenis */ 7624026eccfSkettenis sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 7634026eccfSkettenis 7640fcbbc5fSanton task_set(&sc->sc_rsttask, pms_reset_task, sc); 7650fcbbc5fSanton timeout_set(&sc->sc_rsttimo, pms_reset_timo, sc); 7660fcbbc5fSanton 76775d69285Skrw sc->poll = 1; 768aaab154dSshadchin sc->sc_dev_enable = 0; 769aaab154dSshadchin 770f6323c95Smpi /* See if the device understands an extended (touchpad) protocol. */ 771f6323c95Smpi pms_protocol_lookup(sc); 7727327fb1eSmpi 773aaab154dSshadchin /* no interrupts until enabled */ 774aaab154dSshadchin pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE); 7754026eccfSkettenis } 7764026eccfSkettenis 7774026eccfSkettenis int 77889d55430Sderaadt pmsactivate(struct device *self, int act) 7794026eccfSkettenis { 78089d55430Sderaadt struct pms_softc *sc = (struct pms_softc *)self; 781*cf372defSderaadt int rv; 78289d55430Sderaadt 78389d55430Sderaadt switch (act) { 78489d55430Sderaadt case DVACT_SUSPEND: 785*cf372defSderaadt rv = config_activate_children(self, act); 78689d55430Sderaadt if (sc->sc_state == PMS_STATE_ENABLED) 787aaab154dSshadchin pms_change_state(sc, PMS_STATE_SUSPENDED, 788aaab154dSshadchin PMS_DEV_IGNORE); 78989d55430Sderaadt break; 79089d55430Sderaadt case DVACT_RESUME: 79189d55430Sderaadt if (sc->sc_state == PMS_STATE_SUSPENDED) 792aaab154dSshadchin pms_change_state(sc, PMS_STATE_ENABLED, 793aaab154dSshadchin PMS_DEV_IGNORE); 794*cf372defSderaadt rv = config_activate_children(self, act); 795*cf372defSderaadt break; 796*cf372defSderaadt default: 797*cf372defSderaadt rv = config_activate_children(self, act); 79889d55430Sderaadt break; 79989d55430Sderaadt } 800*cf372defSderaadt return (rv); 80189d55430Sderaadt } 80289d55430Sderaadt 80389d55430Sderaadt int 804aaab154dSshadchin pms_change_state(struct pms_softc *sc, int newstate, int dev) 80589d55430Sderaadt { 806aaab154dSshadchin if (dev != PMS_DEV_IGNORE) { 80789d55430Sderaadt switch (newstate) { 80889d55430Sderaadt case PMS_STATE_ENABLED: 809aaab154dSshadchin if (sc->sc_dev_enable & dev) 810e384b96cSkrw return (EBUSY); 811e384b96cSkrw 812aaab154dSshadchin sc->sc_dev_enable |= dev; 813aaab154dSshadchin 814aaab154dSshadchin if (sc->sc_state == PMS_STATE_ENABLED) 815aaab154dSshadchin return (0); 816aaab154dSshadchin 817aaab154dSshadchin break; 818aaab154dSshadchin case PMS_STATE_DISABLED: 819aaab154dSshadchin sc->sc_dev_enable &= ~dev; 820aaab154dSshadchin 821aaab154dSshadchin if (sc->sc_dev_enable) 822aaab154dSshadchin return (0); 823aaab154dSshadchin 824aaab154dSshadchin break; 825aaab154dSshadchin } 826aaab154dSshadchin } 827aaab154dSshadchin 828aaab154dSshadchin switch (newstate) { 829aaab154dSshadchin case PMS_STATE_ENABLED: 8304026eccfSkettenis sc->inputstate = 0; 8310fcbbc5fSanton sc->sc_rststate = 0; 8324026eccfSkettenis 833ac6d5436Stobias pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1); 8344026eccfSkettenis 835506bb5b0Skrw if (sc->poll) 836ac6d5436Stobias pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT); 83775d69285Skrw 83855fddf67Skrw pms_reset(sc); 839b72aedf1Smpi if (sc->protocol->enable != NULL && 840aaab154dSshadchin sc->protocol->enable(sc) == 0) 841f6323c95Smpi pms_protocol_lookup(sc); 8428cd42f84Smiod 84355fddf67Skrw pms_dev_enable(sc); 84489d55430Sderaadt break; 84589d55430Sderaadt case PMS_STATE_DISABLED: 84689d55430Sderaadt case PMS_STATE_SUSPENDED: 84755fddf67Skrw pms_dev_disable(sc); 848f7577b15Sshadchin 849f6323c95Smpi if (sc->protocol->disable) 850f7577b15Sshadchin sc->protocol->disable(sc); 851f7577b15Sshadchin 852ac6d5436Stobias pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0); 85389d55430Sderaadt break; 85489d55430Sderaadt } 855e384b96cSkrw 856e384b96cSkrw sc->sc_state = newstate; 857e384b96cSkrw sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0; 858e384b96cSkrw 859e384b96cSkrw return (0); 8604026eccfSkettenis } 8614026eccfSkettenis 86289d55430Sderaadt int 863b43e75e7Skrw pms_enable(void *v) 86489d55430Sderaadt { 86589d55430Sderaadt struct pms_softc *sc = v; 8660649de23Smpi int rv; 86789d55430Sderaadt 8680649de23Smpi rw_enter_write(&sc->sc_state_lock); 8690649de23Smpi rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY); 8700649de23Smpi rw_exit_write(&sc->sc_state_lock); 8710649de23Smpi 8720649de23Smpi return (rv); 87389d55430Sderaadt } 87489d55430Sderaadt 8754026eccfSkettenis void 876b43e75e7Skrw pms_disable(void *v) 8774026eccfSkettenis { 8784026eccfSkettenis struct pms_softc *sc = v; 8794026eccfSkettenis 8800649de23Smpi rw_enter_write(&sc->sc_state_lock); 881aaab154dSshadchin pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY); 8820649de23Smpi rw_exit_write(&sc->sc_state_lock); 8834026eccfSkettenis } 8844026eccfSkettenis 8854026eccfSkettenis int 886b43e75e7Skrw pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 8874026eccfSkettenis { 8884026eccfSkettenis struct pms_softc *sc = v; 8894026eccfSkettenis 890f6323c95Smpi if (sc->protocol->ioctl) 891f7577b15Sshadchin return (sc->protocol->ioctl(sc, cmd, data, flag, p)); 8925817301aSshadchin else 8935817301aSshadchin return (-1); 8944026eccfSkettenis } 8954026eccfSkettenis 896f96d6147Sshadchin int 897f96d6147Sshadchin pms_sec_enable(void *v) 898f96d6147Sshadchin { 899f96d6147Sshadchin struct pms_softc *sc = v; 9000649de23Smpi int rv; 901f96d6147Sshadchin 9020649de23Smpi rw_enter_write(&sc->sc_state_lock); 9030649de23Smpi rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY); 9040649de23Smpi rw_exit_write(&sc->sc_state_lock); 9050649de23Smpi 9060649de23Smpi return (rv); 907f96d6147Sshadchin } 908f96d6147Sshadchin 909f96d6147Sshadchin void 910f96d6147Sshadchin pms_sec_disable(void *v) 911f96d6147Sshadchin { 912f96d6147Sshadchin struct pms_softc *sc = v; 913f96d6147Sshadchin 9140649de23Smpi rw_enter_write(&sc->sc_state_lock); 915f96d6147Sshadchin pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY); 9160649de23Smpi rw_exit_write(&sc->sc_state_lock); 917f96d6147Sshadchin } 918f96d6147Sshadchin 919f96d6147Sshadchin int 920f96d6147Sshadchin pms_sec_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 921f96d6147Sshadchin { 922f96d6147Sshadchin switch (cmd) { 923f96d6147Sshadchin case WSMOUSEIO_GTYPE: 924f96d6147Sshadchin *(u_int *)data = WSMOUSE_TYPE_PS2; 925f96d6147Sshadchin break; 926f96d6147Sshadchin default: 927f96d6147Sshadchin return (-1); 928f96d6147Sshadchin } 929f96d6147Sshadchin return (0); 930f96d6147Sshadchin } 931f96d6147Sshadchin 9325c7f814cSmpi #ifdef DIAGNOSTIC 9334f06b9beSbru static inline void 9344f06b9beSbru pms_print_packet(struct pms_softc *sc) 9354f06b9beSbru { 9364f06b9beSbru int i, state, size; 9374f06b9beSbru 9384f06b9beSbru state = sc->inputstate; 9394f06b9beSbru size = sc->protocol->packetsize; 9404f06b9beSbru for (i = 0; i < size; i++) 9414f06b9beSbru printf(i == state ? " %02x |" : " %02x", sc->packet[i]); 9424f06b9beSbru } 9435c7f814cSmpi #endif 9444f06b9beSbru 945b43e75e7Skrw void 946b43e75e7Skrw pmsinput(void *vsc, int data) 9474026eccfSkettenis { 9484026eccfSkettenis struct pms_softc *sc = vsc; 9494026eccfSkettenis 95089d55430Sderaadt if (sc->sc_state != PMS_STATE_ENABLED) { 9514026eccfSkettenis /* Interrupts are not expected. Discard the byte. */ 9524026eccfSkettenis return; 9534026eccfSkettenis } 9544026eccfSkettenis 955b2dcd1e6Sshadchin sc->packet[sc->inputstate] = data; 9560fcbbc5fSanton pms_reset_detect(sc, data); 957f7577b15Sshadchin if (sc->protocol->sync(sc, data)) { 958aaab154dSshadchin #ifdef DIAGNOSTIC 95987f59ec1Santon printf("%s: not in sync yet, discard input " 9604f06b9beSbru "(state = %d,", 9614f06b9beSbru DEVNAME(sc), sc->inputstate); 9624f06b9beSbru pms_print_packet(sc); 9634f06b9beSbru printf(")\n"); 964f7577b15Sshadchin #endif 96587f59ec1Santon 9664026eccfSkettenis sc->inputstate = 0; 967f7577b15Sshadchin return; 9684026eccfSkettenis } 9694026eccfSkettenis 970b2dcd1e6Sshadchin sc->inputstate++; 971f96d6147Sshadchin 972f7577b15Sshadchin if (sc->inputstate != sc->protocol->packetsize) 9734026eccfSkettenis return; 974f7577b15Sshadchin 975f7577b15Sshadchin sc->inputstate = 0; 976b2dcd1e6Sshadchin sc->protocol->proc(sc); 9774026eccfSkettenis } 978aaab154dSshadchin 979aaab154dSshadchin int 9800329de75Smglocker synaptics_set_mode(struct pms_softc *sc, int mode, int rate) 981aaab154dSshadchin { 982aaab154dSshadchin struct synaptics_softc *syn = sc->synaptics; 983aaab154dSshadchin 984aaab154dSshadchin if (pms_spec_cmd(sc, mode) || 9850329de75Smglocker pms_set_rate(sc, rate == 0 ? SYNAPTICS_CMD_SET_MODE : rate)) 986aaab154dSshadchin return (-1); 987aaab154dSshadchin 9887ecc5202Smglocker /* 9897ecc5202Smglocker * Make sure that the set mode command has finished. 9907ecc5202Smglocker * Otherwise enabling the device before that will make it fail. 9917ecc5202Smglocker */ 9927ecc5202Smglocker delay(10000); 9937ecc5202Smglocker 9940329de75Smglocker if (rate == 0) 995aaab154dSshadchin syn->mode = mode; 996aaab154dSshadchin 997aaab154dSshadchin return (0); 998aaab154dSshadchin } 999aaab154dSshadchin 1000aaab154dSshadchin int 1001aaab154dSshadchin synaptics_query(struct pms_softc *sc, int query, int *val) 1002aaab154dSshadchin { 1003aaab154dSshadchin u_char resp[3]; 1004aaab154dSshadchin 1005aaab154dSshadchin if (pms_spec_cmd(sc, query) || 1006aaab154dSshadchin pms_get_status(sc, resp)) 1007aaab154dSshadchin return (-1); 1008aaab154dSshadchin 1009aaab154dSshadchin if (val) 1010aaab154dSshadchin *val = (resp[0] << 16) | (resp[1] << 8) | resp[2]; 1011aaab154dSshadchin 1012aaab154dSshadchin return (0); 1013aaab154dSshadchin } 1014aaab154dSshadchin 1015aaab154dSshadchin int 1016aaab154dSshadchin synaptics_get_hwinfo(struct pms_softc *sc) 1017aaab154dSshadchin { 1018aaab154dSshadchin struct synaptics_softc *syn = sc->synaptics; 1019dc727cfdSbru struct wsmousehw *hw; 1020ceee28bcSbru int resolution = 0, max_coords = 0, min_coords = 0; 1021dc727cfdSbru 1022dc727cfdSbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 1023aaab154dSshadchin 1024aaab154dSshadchin if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify)) 1025aaab154dSshadchin return (-1); 1026aaab154dSshadchin if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES, 1027aaab154dSshadchin &syn->capabilities)) 1028aaab154dSshadchin return (-1); 1029aaab154dSshadchin if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model)) 1030aaab154dSshadchin return (-1); 1031aaab154dSshadchin if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) && 1032aaab154dSshadchin synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model)) 1033aaab154dSshadchin return (-1); 1034aaab154dSshadchin if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) && 1035aaab154dSshadchin synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES, 1036aaab154dSshadchin &syn->ext_capabilities)) 1037aaab154dSshadchin return (-1); 1038aaab154dSshadchin if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) && 103932aca7fdSbru synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &resolution)) 1040aaab154dSshadchin return (-1); 1041aaab154dSshadchin if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) && 104232aca7fdSbru (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_COORDS) && 104332aca7fdSbru synaptics_query(sc, SYNAPTICS_QUE_EXT_MAX_COORDS, &max_coords)) 1044aaab154dSshadchin return (-1); 104532aca7fdSbru if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 7 || 104632aca7fdSbru SYNAPTICS_ID_FULL(syn->identify) == 0x801) && 104732aca7fdSbru (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MIN_COORDS) && 104832aca7fdSbru synaptics_query(sc, SYNAPTICS_QUE_EXT_MIN_COORDS, &min_coords)) 104932aca7fdSbru return (-1); 105032aca7fdSbru 1051dc9b7ba2Sjsg if (SYNAPTICS_ID_FULL(syn->identify) >= 0x705) { 1052dc9b7ba2Sjsg if (synaptics_query(sc, SYNAPTICS_QUE_MODES, &syn->modes)) 1053dc9b7ba2Sjsg return (-1); 1054dc9b7ba2Sjsg if ((syn->modes & SYNAPTICS_EXT2_CAP) && 1055dc9b7ba2Sjsg synaptics_query(sc, SYNAPTICS_QUE_EXT2_CAPABILITIES, 1056dc9b7ba2Sjsg &syn->ext2_capabilities)) 1057dc9b7ba2Sjsg return (-1); 1058dc9b7ba2Sjsg } 1059aaab154dSshadchin 1060dc727cfdSbru if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) && 1061dc727cfdSbru !(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK) 1062dc727cfdSbru && mouse_has_softbtn) 1063dc727cfdSbru hw->type = WSMOUSE_TYPE_SYNAP_SBTN; 1064dc727cfdSbru else 1065dc727cfdSbru hw->type = WSMOUSE_TYPE_SYNAPTICS; 1066dc727cfdSbru 1067dc727cfdSbru hw->hw_type = (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) 1068dc727cfdSbru ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD; 1069dc727cfdSbru 107032aca7fdSbru if (resolution & SYNAPTICS_RESOLUTION_VALID) { 107132aca7fdSbru hw->h_res = SYNAPTICS_RESOLUTION_X(resolution); 107232aca7fdSbru hw->v_res = SYNAPTICS_RESOLUTION_Y(resolution); 1073a7511290Sbru } 107432aca7fdSbru 107532aca7fdSbru hw->x_min = (min_coords ? 107632aca7fdSbru SYNAPTICS_X_LIMIT(min_coords) : SYNAPTICS_XMIN_BEZEL); 107732aca7fdSbru hw->y_min = (min_coords ? 107832aca7fdSbru SYNAPTICS_Y_LIMIT(min_coords) : SYNAPTICS_YMIN_BEZEL); 107932aca7fdSbru hw->x_max = (max_coords ? 108032aca7fdSbru SYNAPTICS_X_LIMIT(max_coords) : SYNAPTICS_XMAX_BEZEL); 108132aca7fdSbru hw->y_max = (max_coords ? 108232aca7fdSbru SYNAPTICS_Y_LIMIT(max_coords) : SYNAPTICS_YMAX_BEZEL); 1083aaab154dSshadchin 10843eb7743cSbru if ((syn->capabilities & SYNAPTICS_CAP_MULTIFINGER) || 10853eb7743cSbru SYNAPTICS_SUPPORTS_AGM(syn->ext_capabilities)) 1086dc727cfdSbru hw->contacts_max = SYNAPTICS_MAX_FINGERS; 10873eb7743cSbru else 10883eb7743cSbru hw->contacts_max = 1; 1089dc727cfdSbru 1090dc9b7ba2Sjsg syn->sec_buttons = 0; 1091dc9b7ba2Sjsg 1092aaab154dSshadchin if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8) 1093aaab154dSshadchin syn->ext_model &= ~0xf000; 1094aaab154dSshadchin 1095f96998ceSmpi if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) { 1096f96998ceSmpi printf("%s: don't support Synaptics OLDABS\n", DEVNAME(sc)); 1097f96998ceSmpi return (-1); 1098f96998ceSmpi } 1099f96998ceSmpi 110043a3c8cdSshadchin if ((SYNAPTICS_ID_MAJOR(syn->identify) == 5) && 110143a3c8cdSshadchin (SYNAPTICS_ID_MINOR(syn->identify) == 9)) 110243a3c8cdSshadchin syn->mask = SYNAPTICS_MASK_NEWABS_RELAXED; 110343a3c8cdSshadchin else 110443a3c8cdSshadchin syn->mask = SYNAPTICS_MASK_NEWABS_STRICT; 110543a3c8cdSshadchin 1106aaab154dSshadchin return (0); 1107aaab154dSshadchin } 1108aaab154dSshadchin 1109aaab154dSshadchin void 1110f96d6147Sshadchin synaptics_sec_proc(struct pms_softc *sc) 1111aaab154dSshadchin { 1112dc9b7ba2Sjsg struct synaptics_softc *syn = sc->synaptics; 1113aaab154dSshadchin u_int buttons; 1114aaab154dSshadchin int dx, dy; 1115aaab154dSshadchin 1116aaab154dSshadchin if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0) 1117aaab154dSshadchin return; 1118aaab154dSshadchin 1119aaab154dSshadchin buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK]; 1120dc9b7ba2Sjsg buttons |= syn->sec_buttons; 1121aaab154dSshadchin dx = (sc->packet[1] & PMS_PS2_XNEG) ? 1122aaab154dSshadchin (int)sc->packet[4] - 256 : sc->packet[4]; 1123aaab154dSshadchin dy = (sc->packet[1] & PMS_PS2_YNEG) ? 1124aaab154dSshadchin (int)sc->packet[5] - 256 : sc->packet[5]; 1125aaab154dSshadchin 11265ad8c6a9Sbru WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0); 1127aaab154dSshadchin } 1128aaab154dSshadchin 1129aaab154dSshadchin int 1130748e7d13Sstsp synaptics_knock(struct pms_softc *sc) 1131aaab154dSshadchin { 1132aaab154dSshadchin u_char resp[3]; 1133aaab154dSshadchin 1134aaab154dSshadchin if (pms_set_resolution(sc, 0) || 1135aaab154dSshadchin pms_set_resolution(sc, 0) || 1136aaab154dSshadchin pms_set_resolution(sc, 0) || 1137aaab154dSshadchin pms_set_resolution(sc, 0) || 1138aaab154dSshadchin pms_get_status(sc, resp) || 1139aaab154dSshadchin resp[1] != SYNAPTICS_ID_MAGIC) 1140748e7d13Sstsp return (-1); 1141748e7d13Sstsp 1142748e7d13Sstsp return (0); 1143748e7d13Sstsp } 1144748e7d13Sstsp 1145748e7d13Sstsp int 1146748e7d13Sstsp pms_enable_synaptics(struct pms_softc *sc) 1147748e7d13Sstsp { 1148748e7d13Sstsp struct synaptics_softc *syn = sc->synaptics; 1149748e7d13Sstsp struct wsmousedev_attach_args a; 1150748e7d13Sstsp int mode, i; 1151748e7d13Sstsp 1152748e7d13Sstsp if (synaptics_knock(sc)) { 1153748e7d13Sstsp if (sc->synaptics == NULL) 11546138841fSshadchin goto err; 1155748e7d13Sstsp /* 1156748e7d13Sstsp * Some synaptics touchpads don't resume quickly. 1157748e7d13Sstsp * Retry a few times. 1158748e7d13Sstsp */ 1159748e7d13Sstsp for (i = 10; i > 0; --i) { 1160748e7d13Sstsp printf("%s: device not resuming, retrying\n", 1161748e7d13Sstsp DEVNAME(sc)); 1162748e7d13Sstsp pms_reset(sc); 1163748e7d13Sstsp if (synaptics_knock(sc) == 0) 1164748e7d13Sstsp break; 1165748e7d13Sstsp delay(100000); 1166748e7d13Sstsp } 1167748e7d13Sstsp if (i == 0) { 1168748e7d13Sstsp printf("%s: lost device\n", DEVNAME(sc)); 1169748e7d13Sstsp goto err; 1170748e7d13Sstsp } 1171748e7d13Sstsp } 1172aaab154dSshadchin 1173aaab154dSshadchin if (sc->synaptics == NULL) { 1174aaab154dSshadchin sc->synaptics = syn = malloc(sizeof(struct synaptics_softc), 1175aaab154dSshadchin M_DEVBUF, M_WAITOK | M_ZERO); 1176aaab154dSshadchin if (syn == NULL) { 1177aaab154dSshadchin printf("%s: synaptics: not enough memory\n", 1178aaab154dSshadchin DEVNAME(sc)); 11796138841fSshadchin goto err; 1180aaab154dSshadchin } 1181aaab154dSshadchin 1182f96998ceSmpi if (synaptics_get_hwinfo(sc)) { 1183a31dcc2eSderaadt free(sc->synaptics, M_DEVBUF, 1184a31dcc2eSderaadt sizeof(struct synaptics_softc)); 1185f96998ceSmpi sc->synaptics = NULL; 11866138841fSshadchin goto err; 1187aaab154dSshadchin } 1188aaab154dSshadchin 1189aaab154dSshadchin /* enable pass-through PS/2 port if supported */ 1190aaab154dSshadchin if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) { 1191f96d6147Sshadchin a.accessops = &pms_sec_accessops; 1192aaab154dSshadchin a.accesscookie = sc; 1193f96d6147Sshadchin sc->sc_sec_wsmousedev = config_found((void *)sc, &a, 1194aaab154dSshadchin wsmousedevprint); 1195aaab154dSshadchin } 1196aaab154dSshadchin 11973d300623Smpi if (wsmouse_configure(sc->sc_wsmousedev, synaptics_params, 11983d300623Smpi nitems(synaptics_params))) 1199dc727cfdSbru goto err; 1200aaab154dSshadchin 1201db8ad24eSbru printf("%s: Synaptics %s, firmware %d.%d, " 1202db8ad24eSbru "0x%x 0x%x 0x%x 0x%x 0x%x\n", 120356fa05d9Sbru DEVNAME(sc), 1204aaab154dSshadchin (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ? 1205aaab154dSshadchin "clickpad" : "touchpad"), 1206aaab154dSshadchin SYNAPTICS_ID_MAJOR(syn->identify), 120756fa05d9Sbru SYNAPTICS_ID_MINOR(syn->identify), 1208db8ad24eSbru syn->model, syn->ext_model, syn->modes, 1209db8ad24eSbru syn->capabilities, syn->ext_capabilities); 1210aaab154dSshadchin } 1211aaab154dSshadchin 1212db8ad24eSbru /* 1213db8ad24eSbru * Enable absolute mode, plain W-mode and "advanced gesture mode" 1214db8ad24eSbru * (AGM), if possible. AGM, which seems to be a prerequisite for the 1215db8ad24eSbru * extended W-mode, might not always be necessary here, but at least 1216db8ad24eSbru * some older Synaptics models do not report finger counts without it. 1217db8ad24eSbru */ 1218aaab154dSshadchin mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE; 1219aaab154dSshadchin if (syn->capabilities & SYNAPTICS_CAP_EXTENDED) 1220aaab154dSshadchin mode |= SYNAPTICS_W_MODE; 1221db8ad24eSbru else if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4) 1222db8ad24eSbru mode |= SYNAPTICS_DISABLE_GESTURE; 12230329de75Smglocker if (synaptics_set_mode(sc, mode, 0)) 12246138841fSshadchin goto err; 1225aaab154dSshadchin 1226db8ad24eSbru if (SYNAPTICS_SUPPORTS_AGM(syn->ext_capabilities) && 12270329de75Smglocker synaptics_set_mode(sc, SYNAPTICS_QUE_MODEL, 12280329de75Smglocker SYNAPTICS_CMD_SET_ADV_GESTURE_MODE)) 12296138841fSshadchin goto err; 1230aaab154dSshadchin 1231aaab154dSshadchin return (1); 12326138841fSshadchin 12336138841fSshadchin err: 12346138841fSshadchin pms_reset(sc); 12356138841fSshadchin 12366138841fSshadchin return (0); 1237aaab154dSshadchin } 1238aaab154dSshadchin 1239aaab154dSshadchin int 1240aaab154dSshadchin pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 1241aaab154dSshadchin struct proc *p) 1242aaab154dSshadchin { 1243aaab154dSshadchin struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 1244dc727cfdSbru struct wsmousehw *hw; 1245aaab154dSshadchin int wsmode; 1246aaab154dSshadchin 1247dc727cfdSbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 1248aaab154dSshadchin switch (cmd) { 1249aaab154dSshadchin case WSMOUSEIO_GTYPE: 1250dc727cfdSbru *(u_int *)data = hw->type; 1251aaab154dSshadchin break; 1252aaab154dSshadchin case WSMOUSEIO_GCALIBCOORDS: 1253dc727cfdSbru wsmc->minx = hw->x_min; 1254dc727cfdSbru wsmc->maxx = hw->x_max; 1255dc727cfdSbru wsmc->miny = hw->y_min; 1256dc727cfdSbru wsmc->maxy = hw->y_max; 1257aaab154dSshadchin wsmc->swapxy = 0; 1258dc727cfdSbru wsmc->resx = hw->h_res; 1259dc727cfdSbru wsmc->resy = hw->v_res; 1260aaab154dSshadchin break; 1261aaab154dSshadchin case WSMOUSEIO_SETMODE: 1262aaab154dSshadchin wsmode = *(u_int *)data; 1263aaab154dSshadchin if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) 1264aaab154dSshadchin return (EINVAL); 1265dc727cfdSbru wsmouse_set_mode(sc->sc_wsmousedev, wsmode); 1266aaab154dSshadchin break; 1267aaab154dSshadchin default: 1268aaab154dSshadchin return (-1); 1269aaab154dSshadchin } 1270aaab154dSshadchin return (0); 1271aaab154dSshadchin } 1272aaab154dSshadchin 1273aaab154dSshadchin int 1274aaab154dSshadchin pms_sync_synaptics(struct pms_softc *sc, int data) 1275aaab154dSshadchin { 127643a3c8cdSshadchin struct synaptics_softc *syn = sc->synaptics; 127743a3c8cdSshadchin 1278aaab154dSshadchin switch (sc->inputstate) { 1279aaab154dSshadchin case 0: 128043a3c8cdSshadchin if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_FIRST) 1281aaab154dSshadchin return (-1); 1282aaab154dSshadchin break; 1283aaab154dSshadchin case 3: 128443a3c8cdSshadchin if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_NEXT) 1285aaab154dSshadchin return (-1); 1286aaab154dSshadchin break; 1287aaab154dSshadchin } 1288aaab154dSshadchin 1289aaab154dSshadchin return (0); 1290aaab154dSshadchin } 1291aaab154dSshadchin 1292aaab154dSshadchin void 1293aaab154dSshadchin pms_proc_synaptics(struct pms_softc *sc) 1294aaab154dSshadchin { 1295aaab154dSshadchin struct synaptics_softc *syn = sc->synaptics; 1296aaab154dSshadchin u_int buttons; 1297dc727cfdSbru int x, y, z, w, fingerwidth; 1298aaab154dSshadchin 1299aaab154dSshadchin w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) | 1300aaab154dSshadchin ((sc->packet[3] & 0x04) >> 2); 1301a7511290Sbru z = sc->packet[2]; 1302aaab154dSshadchin 1303a7511290Sbru if ((syn->capabilities & SYNAPTICS_CAP_EXTENDED) == 0) { 13049274e18dSkrw /* 1305a7511290Sbru * Emulate W mode for models that don't provide it. Bit 3 1306a7511290Sbru * of the w-input signals a touch ("finger"), Bit 2 and 1307a7511290Sbru * the "gesture" bits 1-0 can be ignored. 13089274e18dSkrw */ 1309a7511290Sbru if (w & 8) 13109274e18dSkrw w = 4; 1311a7511290Sbru else 1312a7511290Sbru z = w = 0; 13139274e18dSkrw } 13149274e18dSkrw 1315a7511290Sbru 1316db8ad24eSbru if (w == 3) { 1317db8ad24eSbru if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) 1318f96d6147Sshadchin synaptics_sec_proc(sc); 1319aaab154dSshadchin return; 1320aaab154dSshadchin } 1321aaab154dSshadchin 1322aaab154dSshadchin if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0) 1323aaab154dSshadchin return; 1324aaab154dSshadchin 1325db8ad24eSbru if (w == 2) 1326db8ad24eSbru return; /* EW-mode packets are not expected here. */ 1327aaab154dSshadchin 1328aaab154dSshadchin x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) | 1329aaab154dSshadchin sc->packet[4]; 1330aaab154dSshadchin y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) | 1331aaab154dSshadchin sc->packet[5]; 1332aaab154dSshadchin 1333aaab154dSshadchin buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ? 1334aaab154dSshadchin WSMOUSE_BUTTON(1) : 0; 1335aaab154dSshadchin buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ? 1336aaab154dSshadchin WSMOUSE_BUTTON(3) : 0; 1337aaab154dSshadchin 1338aaab154dSshadchin if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) { 1339aaab154dSshadchin buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 1340aaab154dSshadchin WSMOUSE_BUTTON(1) : 0; 1341aaab154dSshadchin } else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) { 1342aaab154dSshadchin buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 1343aaab154dSshadchin WSMOUSE_BUTTON(2) : 0; 1344aaab154dSshadchin } 1345aaab154dSshadchin 1346aaab154dSshadchin if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) { 1347aaab154dSshadchin buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 1348aaab154dSshadchin WSMOUSE_BUTTON(4) : 0; 1349aaab154dSshadchin buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ? 1350aaab154dSshadchin WSMOUSE_BUTTON(5) : 0; 1351aaab154dSshadchin } else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) && 1352aaab154dSshadchin ((sc->packet[0] ^ sc->packet[3]) & 0x02)) { 1353dc9b7ba2Sjsg if (syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK) { 1354dc9b7ba2Sjsg /* 1355dc9b7ba2Sjsg * Trackstick buttons on this machine are wired to the 1356dc9b7ba2Sjsg * trackpad as extra buttons, so route the event 1357dc9b7ba2Sjsg * through the trackstick interface as normal buttons 1358dc9b7ba2Sjsg */ 1359dc9b7ba2Sjsg syn->sec_buttons = 1360dc9b7ba2Sjsg (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(1) : 0; 1361dc9b7ba2Sjsg syn->sec_buttons |= 1362dc9b7ba2Sjsg (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3) : 0; 1363dc9b7ba2Sjsg syn->sec_buttons |= 1364dc9b7ba2Sjsg (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2) : 0; 13655ad8c6a9Sbru wsmouse_buttons( 13665ad8c6a9Sbru sc->sc_sec_wsmousedev, syn->sec_buttons); 13675ad8c6a9Sbru wsmouse_input_sync(sc->sc_sec_wsmousedev); 1368dc9b7ba2Sjsg return; 1369dc9b7ba2Sjsg } 1370dc9b7ba2Sjsg 1371aaab154dSshadchin buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0; 1372aaab154dSshadchin buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0; 1373aaab154dSshadchin buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0; 1374aaab154dSshadchin buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0; 1375aaab154dSshadchin buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0; 1376aaab154dSshadchin buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0; 1377aaab154dSshadchin buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0; 1378aaab154dSshadchin buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0; 1379aaab154dSshadchin x &= ~0x0f; 1380aaab154dSshadchin y &= ~0x0f; 1381aaab154dSshadchin } 1382aaab154dSshadchin 13835ad8c6a9Sbru if (z) { 1384dc727cfdSbru fingerwidth = max(w, 4); 13855ad8c6a9Sbru w = (w < 2 ? w + 2 : 1); 13865ad8c6a9Sbru } else { 1387dc727cfdSbru fingerwidth = 0; 1388dc727cfdSbru w = 0; 13895ad8c6a9Sbru } 1390dc727cfdSbru wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, fingerwidth, 0); 13915ad8c6a9Sbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w); 1392aaab154dSshadchin } 1393aaab154dSshadchin 1394aaab154dSshadchin void 1395aaab154dSshadchin pms_disable_synaptics(struct pms_softc *sc) 1396aaab154dSshadchin { 1397aaab154dSshadchin struct synaptics_softc *syn = sc->synaptics; 1398aaab154dSshadchin 1399aaab154dSshadchin if (syn->capabilities & SYNAPTICS_CAP_SLEEP) 1400aaab154dSshadchin synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE | 14010329de75Smglocker SYNAPTICS_DISABLE_GESTURE, 0); 1402aaab154dSshadchin } 14037327fb1eSmpi 14047327fb1eSmpi int 1405b2dcd1e6Sshadchin alps_sec_proc(struct pms_softc *sc) 1406b2dcd1e6Sshadchin { 1407b2dcd1e6Sshadchin struct alps_softc *alps = sc->alps; 1408b2dcd1e6Sshadchin int dx, dy, pos = 0; 1409b2dcd1e6Sshadchin 1410b2dcd1e6Sshadchin if ((sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) { 1411b2dcd1e6Sshadchin /* 1412b2dcd1e6Sshadchin * We need to keep buttons states because interleaved 1413b2dcd1e6Sshadchin * packets only signalize x/y movements. 1414b2dcd1e6Sshadchin */ 1415b2dcd1e6Sshadchin alps->sec_buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK]; 1416b2dcd1e6Sshadchin } else if ((sc->packet[3] & PMS_ALPS_INTERLEAVED_MASK) == 1417b2dcd1e6Sshadchin PMS_ALPS_INTERLEAVED_VALID) { 1418b2dcd1e6Sshadchin sc->inputstate = 3; 1419b2dcd1e6Sshadchin pos = 3; 1420b2dcd1e6Sshadchin } else { 1421b2dcd1e6Sshadchin return (0); 1422b2dcd1e6Sshadchin } 1423b2dcd1e6Sshadchin 1424b2dcd1e6Sshadchin if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0) 1425b2dcd1e6Sshadchin return (1); 1426b2dcd1e6Sshadchin 1427b2dcd1e6Sshadchin dx = (sc->packet[pos] & PMS_PS2_XNEG) ? 1428b2dcd1e6Sshadchin (int)sc->packet[pos + 1] - 256 : sc->packet[pos + 1]; 1429b2dcd1e6Sshadchin dy = (sc->packet[pos] & PMS_PS2_YNEG) ? 1430b2dcd1e6Sshadchin (int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2]; 1431b2dcd1e6Sshadchin 14325ad8c6a9Sbru WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0); 1433b2dcd1e6Sshadchin 1434b2dcd1e6Sshadchin return (1); 1435b2dcd1e6Sshadchin } 1436b2dcd1e6Sshadchin 1437b2dcd1e6Sshadchin int 14387327fb1eSmpi alps_get_hwinfo(struct pms_softc *sc) 14397327fb1eSmpi { 14407327fb1eSmpi struct alps_softc *alps = sc->alps; 14417327fb1eSmpi u_char resp[3]; 14427327fb1eSmpi int i; 1443995fbf01Sbru struct wsmousehw *hw; 14447327fb1eSmpi 14457327fb1eSmpi if (pms_set_resolution(sc, 0) || 14467327fb1eSmpi pms_set_scaling(sc, 2) || 14477327fb1eSmpi pms_set_scaling(sc, 2) || 14487327fb1eSmpi pms_set_scaling(sc, 2) || 14497327fb1eSmpi pms_get_status(sc, resp)) { 14507327fb1eSmpi DPRINTF("%s: alps: model query error\n", DEVNAME(sc)); 14517327fb1eSmpi return (-1); 14527327fb1eSmpi } 14537327fb1eSmpi 14547327fb1eSmpi alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1); 14557327fb1eSmpi 14567327fb1eSmpi for (i = 0; i < nitems(alps_models); i++) 14577327fb1eSmpi if (alps->version == alps_models[i].version) { 14587327fb1eSmpi alps->model = alps_models[i].model; 1459dd24e85bSmpi alps->mask = alps_models[i].mask; 1460995fbf01Sbru 1461995fbf01Sbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 1462995fbf01Sbru hw->type = WSMOUSE_TYPE_ALPS; 1463995fbf01Sbru hw->hw_type = WSMOUSEHW_TOUCHPAD; 1464995fbf01Sbru hw->x_min = ALPS_XMIN_BEZEL; 1465995fbf01Sbru hw->y_min = ALPS_YMIN_BEZEL; 1466995fbf01Sbru hw->x_max = ALPS_XMAX_BEZEL; 1467995fbf01Sbru hw->y_max = ALPS_YMAX_BEZEL; 1468529d4085Sbru hw->contacts_max = 1; 1469995fbf01Sbru 14707327fb1eSmpi return (0); 14717327fb1eSmpi } 14727327fb1eSmpi 14737327fb1eSmpi return (-1); 14747327fb1eSmpi } 14757327fb1eSmpi 14767327fb1eSmpi int 14777327fb1eSmpi pms_enable_alps(struct pms_softc *sc) 14787327fb1eSmpi { 14797327fb1eSmpi struct alps_softc *alps = sc->alps; 1480419d60bbSmpi struct wsmousedev_attach_args a; 14817327fb1eSmpi u_char resp[3]; 14827327fb1eSmpi 14837327fb1eSmpi if (pms_set_resolution(sc, 0) || 14847327fb1eSmpi pms_set_scaling(sc, 1) || 14857327fb1eSmpi pms_set_scaling(sc, 1) || 14867327fb1eSmpi pms_set_scaling(sc, 1) || 14877327fb1eSmpi pms_get_status(sc, resp) || 14887327fb1eSmpi resp[0] != PMS_ALPS_MAGIC1 || 14897327fb1eSmpi resp[1] != PMS_ALPS_MAGIC2 || 1490fa7039d6Stobias (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2 && 1491fa7039d6Stobias resp[2] != PMS_ALPS_MAGIC3_3)) 14926138841fSshadchin goto err; 14937327fb1eSmpi 14947327fb1eSmpi if (sc->alps == NULL) { 14957327fb1eSmpi sc->alps = alps = malloc(sizeof(struct alps_softc), 14967327fb1eSmpi M_DEVBUF, M_WAITOK | M_ZERO); 14977327fb1eSmpi if (alps == NULL) { 14987327fb1eSmpi printf("%s: alps: not enough memory\n", DEVNAME(sc)); 14997327fb1eSmpi goto err; 15007327fb1eSmpi } 15017327fb1eSmpi 1502f96998ceSmpi if (alps_get_hwinfo(sc)) { 1503a31dcc2eSderaadt free(sc->alps, M_DEVBUF, sizeof(struct alps_softc)); 1504f96998ceSmpi sc->alps = NULL; 15057327fb1eSmpi goto err; 1506f96998ceSmpi } 15077327fb1eSmpi 1508995fbf01Sbru if (wsmouse_configure(sc->sc_wsmousedev, alps_params, 1509995fbf01Sbru nitems(alps_params))) { 1510995fbf01Sbru free(sc->alps, M_DEVBUF, sizeof(struct alps_softc)); 1511995fbf01Sbru sc->alps = NULL; 1512995fbf01Sbru printf("%s: setup failed\n", DEVNAME(sc)); 1513995fbf01Sbru goto err; 1514995fbf01Sbru } 1515995fbf01Sbru 15167327fb1eSmpi printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc), 15177327fb1eSmpi (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"), 15187327fb1eSmpi alps->version); 15197327fb1eSmpi 1520419d60bbSmpi 1521419d60bbSmpi if (alps->model & ALPS_DUALPOINT) { 1522f96d6147Sshadchin a.accessops = &pms_sec_accessops; 1523419d60bbSmpi a.accesscookie = sc; 1524f96d6147Sshadchin sc->sc_sec_wsmousedev = config_found((void *)sc, &a, 1525419d60bbSmpi wsmousedevprint); 1526419d60bbSmpi } 15277327fb1eSmpi } 15287327fb1eSmpi 15297327fb1eSmpi if (alps->model == 0) 15307327fb1eSmpi goto err; 15317327fb1eSmpi 15327327fb1eSmpi if ((alps->model & ALPS_PASSTHROUGH) && 15337327fb1eSmpi (pms_set_scaling(sc, 2) || 15347327fb1eSmpi pms_set_scaling(sc, 2) || 15357327fb1eSmpi pms_set_scaling(sc, 2) || 15367327fb1eSmpi pms_dev_disable(sc))) { 15377327fb1eSmpi DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc)); 15387327fb1eSmpi goto err; 15397327fb1eSmpi } 15407327fb1eSmpi 15417327fb1eSmpi if (pms_dev_disable(sc) || 15427327fb1eSmpi pms_dev_disable(sc) || 15437327fb1eSmpi pms_set_rate(sc, 0x0a)) { 15447327fb1eSmpi DPRINTF("%s: alps: tapping error\n", DEVNAME(sc)); 15457327fb1eSmpi goto err; 15467327fb1eSmpi } 15477327fb1eSmpi 15487327fb1eSmpi if (pms_dev_disable(sc) || 15497327fb1eSmpi pms_dev_disable(sc) || 15507327fb1eSmpi pms_dev_disable(sc) || 15517327fb1eSmpi pms_dev_disable(sc) || 15527327fb1eSmpi pms_dev_enable(sc)) { 15537327fb1eSmpi DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc)); 15547327fb1eSmpi goto err; 15557327fb1eSmpi } 15567327fb1eSmpi 15577327fb1eSmpi if ((alps->model & ALPS_PASSTHROUGH) && 15587327fb1eSmpi (pms_set_scaling(sc, 1) || 15597327fb1eSmpi pms_set_scaling(sc, 1) || 15607327fb1eSmpi pms_set_scaling(sc, 1) || 15617327fb1eSmpi pms_dev_disable(sc))) { 15627327fb1eSmpi DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc)); 15637327fb1eSmpi goto err; 15647327fb1eSmpi } 15657327fb1eSmpi 1566b2dcd1e6Sshadchin alps->sec_buttons = 0; 1567b2dcd1e6Sshadchin 15687327fb1eSmpi return (1); 15697327fb1eSmpi 15707327fb1eSmpi err: 15717327fb1eSmpi pms_reset(sc); 15727327fb1eSmpi 15737327fb1eSmpi return (0); 15747327fb1eSmpi } 15757327fb1eSmpi 15767327fb1eSmpi int 15777327fb1eSmpi pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 15787327fb1eSmpi struct proc *p) 15797327fb1eSmpi { 15807327fb1eSmpi struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 15817327fb1eSmpi int wsmode; 1582995fbf01Sbru struct wsmousehw *hw; 15837327fb1eSmpi 15847327fb1eSmpi switch (cmd) { 15857327fb1eSmpi case WSMOUSEIO_GTYPE: 15867327fb1eSmpi *(u_int *)data = WSMOUSE_TYPE_ALPS; 15877327fb1eSmpi break; 15887327fb1eSmpi case WSMOUSEIO_GCALIBCOORDS: 1589995fbf01Sbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 1590995fbf01Sbru wsmc->minx = hw->x_min; 1591995fbf01Sbru wsmc->maxx = hw->x_max; 1592995fbf01Sbru wsmc->miny = hw->y_min; 1593995fbf01Sbru wsmc->maxy = hw->y_max; 15947327fb1eSmpi wsmc->swapxy = 0; 15957327fb1eSmpi break; 15967327fb1eSmpi case WSMOUSEIO_SETMODE: 15977327fb1eSmpi wsmode = *(u_int *)data; 15987327fb1eSmpi if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) 15997327fb1eSmpi return (EINVAL); 1600995fbf01Sbru wsmouse_set_mode(sc->sc_wsmousedev, wsmode); 16017327fb1eSmpi break; 16027327fb1eSmpi default: 16037327fb1eSmpi return (-1); 16047327fb1eSmpi } 16057327fb1eSmpi return (0); 16067327fb1eSmpi } 16077327fb1eSmpi 16087327fb1eSmpi int 16097327fb1eSmpi pms_sync_alps(struct pms_softc *sc, int data) 16107327fb1eSmpi { 1611dd24e85bSmpi struct alps_softc *alps = sc->alps; 1612dd24e85bSmpi 1613b2dcd1e6Sshadchin if ((alps->model & ALPS_DUALPOINT) && 1614b2dcd1e6Sshadchin (sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) { 1615b2dcd1e6Sshadchin if (sc->inputstate == 2) 1616b2dcd1e6Sshadchin sc->inputstate += 3; 1617b2dcd1e6Sshadchin return (0); 1618b2dcd1e6Sshadchin } 1619b2dcd1e6Sshadchin 16207327fb1eSmpi switch (sc->inputstate) { 16217327fb1eSmpi case 0: 1622dd24e85bSmpi if ((data & alps->mask) != alps->mask) 16237327fb1eSmpi return (-1); 16247327fb1eSmpi break; 16257327fb1eSmpi case 1: 16267327fb1eSmpi case 2: 16277327fb1eSmpi case 3: 1628b2dcd1e6Sshadchin if ((data & PMS_ALPS_MASK) != PMS_ALPS_VALID) 1629b2dcd1e6Sshadchin return (-1); 1630b2dcd1e6Sshadchin break; 16317327fb1eSmpi case 4: 16327327fb1eSmpi case 5: 1633b2dcd1e6Sshadchin if ((alps->model & ALPS_INTERLEAVED) == 0 && 1634b2dcd1e6Sshadchin (data & PMS_ALPS_MASK) != PMS_ALPS_VALID) 16357327fb1eSmpi return (-1); 16367327fb1eSmpi break; 16377327fb1eSmpi } 16387327fb1eSmpi 16397327fb1eSmpi return (0); 16407327fb1eSmpi } 16417327fb1eSmpi 16427327fb1eSmpi void 16437327fb1eSmpi pms_proc_alps(struct pms_softc *sc) 16447327fb1eSmpi { 16457327fb1eSmpi struct alps_softc *alps = sc->alps; 16465ad8c6a9Sbru int x, y, z, dx, dy; 16472445272cSbru u_int buttons, gesture; 16487327fb1eSmpi 1649b2dcd1e6Sshadchin if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc)) 1650b2dcd1e6Sshadchin return; 1651b2dcd1e6Sshadchin 16527327fb1eSmpi x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4); 16537327fb1eSmpi y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3); 16547327fb1eSmpi z = sc->packet[5]; 16557327fb1eSmpi 1656419d60bbSmpi buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) | 1657419d60bbSmpi ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) | 1658419d60bbSmpi ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0); 1659419d60bbSmpi 1660419d60bbSmpi if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) { 1661419d60bbSmpi dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x; 1662419d60bbSmpi dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y; 1663419d60bbSmpi 16645ad8c6a9Sbru WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0); 1665419d60bbSmpi 1666419d60bbSmpi return; 1667419d60bbSmpi } 1668419d60bbSmpi 1669419d60bbSmpi if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0) 1670419d60bbSmpi return; 1671419d60bbSmpi 16727327fb1eSmpi /* 16737327fb1eSmpi * XXX The Y-axis is in the oposit direction compared to 16747327fb1eSmpi * Synaptics touchpads and PS/2 mouses. 16757327fb1eSmpi * It's why we need to translate the y value here for both 16767327fb1eSmpi * NATIVE and COMPAT modes. 16777327fb1eSmpi */ 16787327fb1eSmpi y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL; 16797327fb1eSmpi 16802445272cSbru if (alps->gesture == ALPS_TAP) { 16812445272cSbru /* Report a touch with the tap coordinates. */ 16825ad8c6a9Sbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, 16835ad8c6a9Sbru alps->old_x, alps->old_y, ALPS_PRESSURE, 0); 16842445272cSbru if (z > 0) { 16852445272cSbru /* 16862445272cSbru * The hardware doesn't send a null pressure 16872445272cSbru * event when dragging starts. 16882445272cSbru */ 16895ad8c6a9Sbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, 16905ad8c6a9Sbru alps->old_x, alps->old_y, 0, 0); 16912445272cSbru } 16922445272cSbru } 16937327fb1eSmpi 16942445272cSbru gesture = sc->packet[2] & 0x03; 16955ad8c6a9Sbru if (gesture != ALPS_TAP) 16965ad8c6a9Sbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0); 16977327fb1eSmpi 16982445272cSbru if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP) 16992445272cSbru alps->gesture = gesture; 17002445272cSbru 17012445272cSbru alps->old_x = x; 17022445272cSbru alps->old_y = y; 17037327fb1eSmpi } 170472ec3668Sstsp 170572ec3668Sstsp int 170672ec3668Sstsp elantech_set_absolute_mode_v1(struct pms_softc *sc) 170772ec3668Sstsp { 170872ec3668Sstsp int i; 170972ec3668Sstsp u_char resp[3]; 171072ec3668Sstsp 171172ec3668Sstsp /* Enable absolute mode. Magic numbers from Linux driver. */ 171272ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) || 171372ec3668Sstsp pms_spec_cmd(sc, 0x10) || 171472ec3668Sstsp pms_spec_cmd(sc, 0x16) || 171572ec3668Sstsp pms_set_scaling(sc, 1) || 171672ec3668Sstsp pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) || 171772ec3668Sstsp pms_spec_cmd(sc, 0x11) || 171872ec3668Sstsp pms_spec_cmd(sc, 0x8f) || 171972ec3668Sstsp pms_set_scaling(sc, 1)) 172072ec3668Sstsp return (-1); 172172ec3668Sstsp 172272ec3668Sstsp /* Read back reg 0x10 to ensure hardware is ready. */ 172372ec3668Sstsp for (i = 0; i < 5; i++) { 172472ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG) || 172572ec3668Sstsp pms_spec_cmd(sc, 0x10) || 172672ec3668Sstsp pms_get_status(sc, resp) == 0) 172772ec3668Sstsp break; 172872ec3668Sstsp delay(2000); 172972ec3668Sstsp } 173072ec3668Sstsp if (i == 5) 173172ec3668Sstsp return (-1); 173272ec3668Sstsp 173372ec3668Sstsp if ((resp[0] & ELANTECH_ABSOLUTE_MODE) == 0) 173472ec3668Sstsp return (-1); 173572ec3668Sstsp 173672ec3668Sstsp return (0); 173772ec3668Sstsp } 173872ec3668Sstsp 173972ec3668Sstsp int 174072ec3668Sstsp elantech_set_absolute_mode_v2(struct pms_softc *sc) 174172ec3668Sstsp { 174272ec3668Sstsp int i; 174372ec3668Sstsp u_char resp[3]; 17447b88a336Sstsp u_char reg10 = (sc->elantech->fw_version == 0x20030 ? 0x54 : 0xc4); 174572ec3668Sstsp 174672ec3668Sstsp /* Enable absolute mode. Magic numbers from Linux driver. */ 174772ec3668Sstsp if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 174872ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) || 174972ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 175072ec3668Sstsp elantech_ps2_cmd(sc, 0x10) || 175172ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 17527b88a336Sstsp elantech_ps2_cmd(sc, reg10) || 175372ec3668Sstsp pms_set_scaling(sc, 1) || 175472ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 175572ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) || 175672ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 175772ec3668Sstsp elantech_ps2_cmd(sc, 0x11) || 175872ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 175972ec3668Sstsp elantech_ps2_cmd(sc, 0x88) || 176072ec3668Sstsp pms_set_scaling(sc, 1)) 176172ec3668Sstsp return (-1); 176272ec3668Sstsp 176372ec3668Sstsp /* Read back reg 0x10 to ensure hardware is ready. */ 176472ec3668Sstsp for (i = 0; i < 5; i++) { 176572ec3668Sstsp if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 176672ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG) || 176772ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 176872ec3668Sstsp elantech_ps2_cmd(sc, 0x10) || 176972ec3668Sstsp pms_get_status(sc, resp) == 0) 177072ec3668Sstsp break; 177172ec3668Sstsp delay(2000); 177272ec3668Sstsp } 177372ec3668Sstsp if (i == 5) 177472ec3668Sstsp return (-1); 177572ec3668Sstsp 177672ec3668Sstsp return (0); 177772ec3668Sstsp } 177872ec3668Sstsp 177972ec3668Sstsp int 178072ec3668Sstsp elantech_set_absolute_mode_v3(struct pms_softc *sc) 178172ec3668Sstsp { 178272ec3668Sstsp int i; 178372ec3668Sstsp u_char resp[3]; 178472ec3668Sstsp 178572ec3668Sstsp /* Enable absolute mode. Magic numbers from Linux driver. */ 178672ec3668Sstsp if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 178772ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) || 178872ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 178972ec3668Sstsp elantech_ps2_cmd(sc, 0x10) || 179072ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 179172ec3668Sstsp elantech_ps2_cmd(sc, 0x0b) || 179272ec3668Sstsp pms_set_scaling(sc, 1)) 179372ec3668Sstsp return (-1); 179472ec3668Sstsp 179572ec3668Sstsp /* Read back reg 0x10 to ensure hardware is ready. */ 179672ec3668Sstsp for (i = 0; i < 5; i++) { 179772ec3668Sstsp if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 179872ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) || 179972ec3668Sstsp elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 180072ec3668Sstsp elantech_ps2_cmd(sc, 0x10) || 180172ec3668Sstsp pms_get_status(sc, resp) == 0) 180272ec3668Sstsp break; 180372ec3668Sstsp delay(2000); 180472ec3668Sstsp } 180572ec3668Sstsp if (i == 5) 180672ec3668Sstsp return (-1); 180772ec3668Sstsp 180872ec3668Sstsp return (0); 180972ec3668Sstsp } 181072ec3668Sstsp 181172ec3668Sstsp int 18127f1c67caSjcs elantech_set_absolute_mode_v4(struct pms_softc *sc) 18137f1c67caSjcs { 18147f1c67caSjcs /* Enable absolute mode. Magic numbers from Linux driver. */ 18157f1c67caSjcs if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 18167f1c67caSjcs elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) || 18177f1c67caSjcs elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 18187f1c67caSjcs elantech_ps2_cmd(sc, 0x07) || 18197f1c67caSjcs elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 18207f1c67caSjcs elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) || 18217f1c67caSjcs elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) || 18227f1c67caSjcs elantech_ps2_cmd(sc, 0x01) || 18237f1c67caSjcs pms_set_scaling(sc, 1)) 18247f1c67caSjcs return (-1); 18257f1c67caSjcs 18267f1c67caSjcs /* v4 has no register 0x10 to read response from */ 18277f1c67caSjcs 18287f1c67caSjcs return (0); 18297f1c67caSjcs } 18307f1c67caSjcs 18317f1c67caSjcs int 183272ec3668Sstsp elantech_get_hwinfo_v1(struct pms_softc *sc) 183372ec3668Sstsp { 183472ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 18354101186eSbru struct wsmousehw *hw; 183672ec3668Sstsp int fw_version; 183772ec3668Sstsp u_char capabilities[3]; 183872ec3668Sstsp 183972ec3668Sstsp if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version)) 184072ec3668Sstsp return (-1); 184172ec3668Sstsp 184272ec3668Sstsp if (fw_version < 0x20030 || fw_version == 0x20600) { 184372ec3668Sstsp if (fw_version < 0x20000) 184472ec3668Sstsp elantech->flags |= ELANTECH_F_HW_V1_OLD; 184572ec3668Sstsp } else 184672ec3668Sstsp return (-1); 184772ec3668Sstsp 18487b88a336Sstsp elantech->fw_version = fw_version; 18497b88a336Sstsp 185072ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) || 185172ec3668Sstsp pms_get_status(sc, capabilities)) 185272ec3668Sstsp return (-1); 185372ec3668Sstsp 185472ec3668Sstsp if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER) 185572ec3668Sstsp elantech->flags |= ELANTECH_F_HAS_ROCKER; 185672ec3668Sstsp 185772ec3668Sstsp if (elantech_set_absolute_mode_v1(sc)) 185872ec3668Sstsp return (-1); 185972ec3668Sstsp 18604101186eSbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 18614101186eSbru hw->type = WSMOUSE_TYPE_ELANTECH; 18624101186eSbru hw->hw_type = WSMOUSEHW_TOUCHPAD; 18634101186eSbru hw->x_min = ELANTECH_V1_X_MIN; 18644101186eSbru hw->x_max = ELANTECH_V1_X_MAX; 18654101186eSbru hw->y_min = ELANTECH_V1_Y_MIN; 18664101186eSbru hw->y_max = ELANTECH_V1_Y_MAX; 186772ec3668Sstsp 186872ec3668Sstsp return (0); 186972ec3668Sstsp } 187072ec3668Sstsp 187172ec3668Sstsp int 187272ec3668Sstsp elantech_get_hwinfo_v2(struct pms_softc *sc) 187372ec3668Sstsp { 187472ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 18754101186eSbru struct wsmousehw *hw; 187672ec3668Sstsp int fw_version, ic_ver; 187772ec3668Sstsp u_char capabilities[3]; 187872ec3668Sstsp int i, fixed_dpi; 187972ec3668Sstsp u_char resp[3]; 188072ec3668Sstsp 188172ec3668Sstsp if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version)) 188272ec3668Sstsp return (-1); 188372ec3668Sstsp 188472ec3668Sstsp ic_ver = (fw_version & 0x0f0000) >> 16; 188572ec3668Sstsp if (ic_ver != 2 && ic_ver != 4) 188672ec3668Sstsp return (-1); 188772ec3668Sstsp 18887b88a336Sstsp elantech->fw_version = fw_version; 188972ec3668Sstsp if (fw_version >= 0x20800) 189072ec3668Sstsp elantech->flags |= ELANTECH_F_REPORTS_PRESSURE; 189172ec3668Sstsp 189272ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) || 189372ec3668Sstsp pms_get_status(sc, capabilities)) 189472ec3668Sstsp return (-1); 189572ec3668Sstsp 189672ec3668Sstsp if (elantech_set_absolute_mode_v2(sc)) 189772ec3668Sstsp return (-1); 189872ec3668Sstsp 18994101186eSbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 19004101186eSbru hw->type = WSMOUSE_TYPE_ELANTECH; 19014101186eSbru hw->hw_type = WSMOUSEHW_TOUCHPAD; 19024101186eSbru 190372ec3668Sstsp if (fw_version == 0x20800 || fw_version == 0x20b00 || 190472ec3668Sstsp fw_version == 0x20030) { 19054101186eSbru hw->x_max = ELANTECH_V2_X_MAX; 19064101186eSbru hw->y_max = ELANTECH_V2_Y_MAX; 190772ec3668Sstsp } else { 190872ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) || 190972ec3668Sstsp pms_get_status(sc, resp)) 191072ec3668Sstsp return (-1); 191172ec3668Sstsp fixed_dpi = resp[1] & 0x10; 191272ec3668Sstsp i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2; 191372ec3668Sstsp if ((fw_version >> 16) == 0x14 && fixed_dpi) { 191472ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) || 191572ec3668Sstsp pms_get_status(sc, resp)) 191672ec3668Sstsp return (-1); 19174101186eSbru hw->x_max = (capabilities[1] - i) * resp[1] / 2; 19184101186eSbru hw->y_max = (capabilities[2] - i) * resp[2] / 2; 191972ec3668Sstsp } else if (fw_version == 0x040216) { 19204101186eSbru hw->x_max = 819; 19214101186eSbru hw->y_max = 405; 192272ec3668Sstsp } else if (fw_version == 0x040219 || fw_version == 0x040215) { 19234101186eSbru hw->x_max = 900; 19244101186eSbru hw->y_max = 500; 192572ec3668Sstsp } else { 19264101186eSbru hw->x_max = (capabilities[1] - i) * 64; 19274101186eSbru hw->y_max = (capabilities[2] - i) * 64; 192872ec3668Sstsp } 192972ec3668Sstsp } 193072ec3668Sstsp 193172ec3668Sstsp return (0); 193272ec3668Sstsp } 193372ec3668Sstsp 193472ec3668Sstsp int 193572ec3668Sstsp elantech_get_hwinfo_v3(struct pms_softc *sc) 193672ec3668Sstsp { 193772ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 19384101186eSbru struct wsmousehw *hw; 193972ec3668Sstsp int fw_version; 194072ec3668Sstsp u_char resp[3]; 194172ec3668Sstsp 194272ec3668Sstsp if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version)) 194372ec3668Sstsp return (-1); 194472ec3668Sstsp 194572ec3668Sstsp if (((fw_version & 0x0f0000) >> 16) != 5) 194672ec3668Sstsp return (-1); 194772ec3668Sstsp 19487b88a336Sstsp elantech->fw_version = fw_version; 194972ec3668Sstsp elantech->flags |= ELANTECH_F_REPORTS_PRESSURE; 195072ec3668Sstsp 195140f7e3e4Sstsp if ((fw_version & 0x4000) == 0x4000) 195240f7e3e4Sstsp elantech->flags |= ELANTECH_F_CRC_ENABLED; 195340f7e3e4Sstsp 195472ec3668Sstsp if (elantech_set_absolute_mode_v3(sc)) 195572ec3668Sstsp return (-1); 195672ec3668Sstsp 195772ec3668Sstsp if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) || 195872ec3668Sstsp pms_get_status(sc, resp)) 195972ec3668Sstsp return (-1); 196072ec3668Sstsp 19614101186eSbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 19624101186eSbru hw->x_max = elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1]; 19634101186eSbru hw->y_max = elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2]; 19644101186eSbru 19654101186eSbru hw->type = WSMOUSE_TYPE_ELANTECH; 19664101186eSbru hw->hw_type = WSMOUSEHW_TOUCHPAD; 196772ec3668Sstsp 196872ec3668Sstsp return (0); 196972ec3668Sstsp } 197072ec3668Sstsp 197172ec3668Sstsp int 19727f1c67caSjcs elantech_get_hwinfo_v4(struct pms_softc *sc) 19737f1c67caSjcs { 19747f1c67caSjcs struct elantech_softc *elantech = sc->elantech; 1975505a3534Sbru struct wsmousehw *hw; 19767f1c67caSjcs int fw_version; 19777f1c67caSjcs u_char capabilities[3]; 19787f1c67caSjcs u_char resp[3]; 19797f1c67caSjcs 19807f1c67caSjcs if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version)) 19817f1c67caSjcs return (-1); 19827f1c67caSjcs 19839665f110Sbru if ((fw_version & 0x0f0000) >> 16 < 6) 19847f1c67caSjcs return (-1); 19857f1c67caSjcs 19867b88a336Sstsp elantech->fw_version = fw_version; 19877f1c67caSjcs elantech->flags |= ELANTECH_F_REPORTS_PRESSURE; 19887f1c67caSjcs 19894f06b9beSbru if ((fw_version & 0x4000) == 0x4000) 19904f06b9beSbru elantech->flags |= ELANTECH_F_CRC_ENABLED; 19914f06b9beSbru 19927f1c67caSjcs if (elantech_set_absolute_mode_v4(sc)) 19937f1c67caSjcs return (-1); 19947f1c67caSjcs 19957f1c67caSjcs if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) || 19967f1c67caSjcs pms_get_status(sc, capabilities)) 19977f1c67caSjcs return (-1); 19987f1c67caSjcs 19997f1c67caSjcs if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) || 20007f1c67caSjcs pms_get_status(sc, resp)) 20017f1c67caSjcs return (-1); 20027f1c67caSjcs 2003505a3534Sbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 2004505a3534Sbru hw->x_max = (resp[0] & 0x0f) << 8 | resp[1]; 2005505a3534Sbru hw->y_max = (resp[0] & 0xf0) << 4 | resp[2]; 20067f1c67caSjcs 2007505a3534Sbru if ((capabilities[1] < 2) || (capabilities[1] > hw->x_max)) 20087f1c67caSjcs return (-1); 20097f1c67caSjcs 20108e9e9c10Sbru if (capabilities[0] & ELANTECH_CAP_TRACKPOINT) 20118e9e9c10Sbru elantech->flags |= ELANTECH_F_TRACKPOINT; 20128e9e9c10Sbru 2013505a3534Sbru hw->type = WSMOUSE_TYPE_ELANTECH; 20149665f110Sbru hw->hw_type = (ELANTECH_IS_CLICKPAD(sc) 20159665f110Sbru ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD); 2016505a3534Sbru hw->mt_slots = ELANTECH_MAX_FINGERS; 2017505a3534Sbru 2018505a3534Sbru elantech->width = hw->x_max / (capabilities[1] - 1); 20197f1c67caSjcs 20207f1c67caSjcs return (0); 20217f1c67caSjcs } 20227f1c67caSjcs 20237f1c67caSjcs int 202472ec3668Sstsp elantech_ps2_cmd(struct pms_softc *sc, u_char command) 202572ec3668Sstsp { 202672ec3668Sstsp u_char cmd[1]; 202772ec3668Sstsp 202872ec3668Sstsp cmd[0] = command; 202972ec3668Sstsp return (pms_cmd(sc, cmd, 1, NULL, 0)); 203072ec3668Sstsp } 203172ec3668Sstsp 203272ec3668Sstsp int 203372ec3668Sstsp elantech_knock(struct pms_softc *sc) 203472ec3668Sstsp { 203572ec3668Sstsp u_char resp[3]; 203672ec3668Sstsp 203772ec3668Sstsp if (pms_dev_disable(sc) || 203872ec3668Sstsp pms_set_scaling(sc, 1) || 203972ec3668Sstsp pms_set_scaling(sc, 1) || 204072ec3668Sstsp pms_set_scaling(sc, 1) || 204172ec3668Sstsp pms_get_status(sc, resp) || 204272ec3668Sstsp resp[0] != PMS_ELANTECH_MAGIC1 || 204372ec3668Sstsp resp[1] != PMS_ELANTECH_MAGIC2 || 204472ec3668Sstsp (resp[2] != PMS_ELANTECH_MAGIC3_1 && 204572ec3668Sstsp resp[2] != PMS_ELANTECH_MAGIC3_2)) 204672ec3668Sstsp return (-1); 204772ec3668Sstsp 204872ec3668Sstsp return (0); 204972ec3668Sstsp } 205072ec3668Sstsp 205172ec3668Sstsp int 205272ec3668Sstsp pms_enable_elantech_v1(struct pms_softc *sc) 205372ec3668Sstsp { 205472ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 205572ec3668Sstsp int i; 205672ec3668Sstsp 205772ec3668Sstsp if (elantech_knock(sc)) 20586138841fSshadchin goto err; 205972ec3668Sstsp 206072ec3668Sstsp if (sc->elantech == NULL) { 206172ec3668Sstsp sc->elantech = elantech = malloc(sizeof(struct elantech_softc), 206272ec3668Sstsp M_DEVBUF, M_WAITOK | M_ZERO); 206372ec3668Sstsp if (elantech == NULL) { 206472ec3668Sstsp printf("%s: elantech: not enough memory\n", 206572ec3668Sstsp DEVNAME(sc)); 206672ec3668Sstsp goto err; 206772ec3668Sstsp } 206872ec3668Sstsp 2069f96998ceSmpi if (elantech_get_hwinfo_v1(sc)) { 2070a31dcc2eSderaadt free(sc->elantech, M_DEVBUF, 2071a31dcc2eSderaadt sizeof(struct elantech_softc)); 2072f96998ceSmpi sc->elantech = NULL; 207372ec3668Sstsp goto err; 2074f96998ceSmpi } 20754101186eSbru if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) { 20764101186eSbru free(sc->elantech, M_DEVBUF, 20774101186eSbru sizeof(struct elantech_softc)); 20784101186eSbru sc->elantech = NULL; 20794101186eSbru printf("%s: elantech: setup failed\n", DEVNAME(sc)); 20804101186eSbru goto err; 20814101186eSbru } 208272ec3668Sstsp 20837b88a336Sstsp printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", 20847b88a336Sstsp DEVNAME(sc), 1, sc->elantech->fw_version); 20856138841fSshadchin } else if (elantech_set_absolute_mode_v1(sc)) 208672ec3668Sstsp goto err; 208772ec3668Sstsp 208872ec3668Sstsp for (i = 0; i < nitems(sc->elantech->parity); i++) 208972ec3668Sstsp sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1; 209072ec3668Sstsp 209172ec3668Sstsp return (1); 209272ec3668Sstsp 209372ec3668Sstsp err: 209472ec3668Sstsp pms_reset(sc); 209572ec3668Sstsp 209672ec3668Sstsp return (0); 209772ec3668Sstsp } 209872ec3668Sstsp 209972ec3668Sstsp int 210072ec3668Sstsp pms_enable_elantech_v2(struct pms_softc *sc) 210172ec3668Sstsp { 210272ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 210372ec3668Sstsp 210472ec3668Sstsp if (elantech_knock(sc)) 21056138841fSshadchin goto err; 210672ec3668Sstsp 210772ec3668Sstsp if (sc->elantech == NULL) { 210872ec3668Sstsp sc->elantech = elantech = malloc(sizeof(struct elantech_softc), 210972ec3668Sstsp M_DEVBUF, M_WAITOK | M_ZERO); 211072ec3668Sstsp if (elantech == NULL) { 211172ec3668Sstsp printf("%s: elantech: not enough memory\n", 211272ec3668Sstsp DEVNAME(sc)); 211372ec3668Sstsp goto err; 211472ec3668Sstsp } 211572ec3668Sstsp 2116f96998ceSmpi if (elantech_get_hwinfo_v2(sc)) { 2117a31dcc2eSderaadt free(sc->elantech, M_DEVBUF, 2118a31dcc2eSderaadt sizeof(struct elantech_softc)); 2119f96998ceSmpi sc->elantech = NULL; 212072ec3668Sstsp goto err; 2121f96998ceSmpi } 21224101186eSbru if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) { 21234101186eSbru free(sc->elantech, M_DEVBUF, 21244101186eSbru sizeof(struct elantech_softc)); 21254101186eSbru sc->elantech = NULL; 21264101186eSbru printf("%s: elantech: setup failed\n", DEVNAME(sc)); 21274101186eSbru goto err; 21284101186eSbru } 212972ec3668Sstsp 21307b88a336Sstsp printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", 21317b88a336Sstsp DEVNAME(sc), 2, sc->elantech->fw_version); 21326138841fSshadchin } else if (elantech_set_absolute_mode_v2(sc)) 213372ec3668Sstsp goto err; 213472ec3668Sstsp 213572ec3668Sstsp return (1); 213672ec3668Sstsp 213772ec3668Sstsp err: 213872ec3668Sstsp pms_reset(sc); 213972ec3668Sstsp 214072ec3668Sstsp return (0); 214172ec3668Sstsp } 214272ec3668Sstsp 214372ec3668Sstsp int 214472ec3668Sstsp pms_enable_elantech_v3(struct pms_softc *sc) 214572ec3668Sstsp { 214672ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 214772ec3668Sstsp 214872ec3668Sstsp if (elantech_knock(sc)) 21496138841fSshadchin goto err; 215072ec3668Sstsp 215172ec3668Sstsp if (sc->elantech == NULL) { 215272ec3668Sstsp sc->elantech = elantech = malloc(sizeof(struct elantech_softc), 215372ec3668Sstsp M_DEVBUF, M_WAITOK | M_ZERO); 215472ec3668Sstsp if (elantech == NULL) { 215572ec3668Sstsp printf("%s: elantech: not enough memory\n", 215672ec3668Sstsp DEVNAME(sc)); 215772ec3668Sstsp goto err; 215872ec3668Sstsp } 215972ec3668Sstsp 2160f96998ceSmpi if (elantech_get_hwinfo_v3(sc)) { 2161a31dcc2eSderaadt free(sc->elantech, M_DEVBUF, 2162a31dcc2eSderaadt sizeof(struct elantech_softc)); 2163f96998ceSmpi sc->elantech = NULL; 216472ec3668Sstsp goto err; 2165f96998ceSmpi } 21664101186eSbru if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) { 21674101186eSbru free(sc->elantech, M_DEVBUF, 21684101186eSbru sizeof(struct elantech_softc)); 21694101186eSbru sc->elantech = NULL; 21704101186eSbru printf("%s: elantech: setup failed\n", DEVNAME(sc)); 21714101186eSbru goto err; 21724101186eSbru } 217372ec3668Sstsp 21747b88a336Sstsp printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", 21757b88a336Sstsp DEVNAME(sc), 3, sc->elantech->fw_version); 21766138841fSshadchin } else if (elantech_set_absolute_mode_v3(sc)) 217772ec3668Sstsp goto err; 217872ec3668Sstsp 217972ec3668Sstsp return (1); 218072ec3668Sstsp 218172ec3668Sstsp err: 218272ec3668Sstsp pms_reset(sc); 218372ec3668Sstsp 218472ec3668Sstsp return (0); 218572ec3668Sstsp } 218672ec3668Sstsp 218772ec3668Sstsp int 21887f1c67caSjcs pms_enable_elantech_v4(struct pms_softc *sc) 21897f1c67caSjcs { 21907f1c67caSjcs struct elantech_softc *elantech = sc->elantech; 21918e9e9c10Sbru struct wsmousedev_attach_args a; 21927f1c67caSjcs 21937f1c67caSjcs if (elantech_knock(sc)) 21947f1c67caSjcs goto err; 21957f1c67caSjcs 21967f1c67caSjcs if (sc->elantech == NULL) { 21977f1c67caSjcs sc->elantech = elantech = malloc(sizeof(struct elantech_softc), 21987f1c67caSjcs M_DEVBUF, M_WAITOK | M_ZERO); 21997f1c67caSjcs if (elantech == NULL) { 22007f1c67caSjcs printf("%s: elantech: not enough memory\n", 22017f1c67caSjcs DEVNAME(sc)); 22027f1c67caSjcs goto err; 22037f1c67caSjcs } 22047f1c67caSjcs 2205f96998ceSmpi if (elantech_get_hwinfo_v4(sc)) { 2206a31dcc2eSderaadt free(sc->elantech, M_DEVBUF, 2207a31dcc2eSderaadt sizeof(struct elantech_softc)); 2208f96998ceSmpi sc->elantech = NULL; 22097f1c67caSjcs goto err; 2210f96998ceSmpi } 2211bc260131Sbru if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) { 22124101186eSbru free(sc->elantech, M_DEVBUF, 22134101186eSbru sizeof(struct elantech_softc)); 22145ad8c6a9Sbru sc->elantech = NULL; 22154101186eSbru printf("%s: elantech: setup failed\n", DEVNAME(sc)); 22165ad8c6a9Sbru goto err; 22175ad8c6a9Sbru } 22185ad8c6a9Sbru 22199665f110Sbru printf("%s: Elantech %s, version 4, firmware 0x%x\n", 22209665f110Sbru DEVNAME(sc), (ELANTECH_IS_CLICKPAD(sc) ? "Clickpad" 22219665f110Sbru : "Touchpad"), sc->elantech->fw_version); 22228e9e9c10Sbru 22238e9e9c10Sbru if (sc->elantech->flags & ELANTECH_F_TRACKPOINT) { 22248e9e9c10Sbru a.accessops = &pms_sec_accessops; 22258e9e9c10Sbru a.accesscookie = sc; 22268e9e9c10Sbru sc->sc_sec_wsmousedev = config_found((void *) sc, &a, 22278e9e9c10Sbru wsmousedevprint); 22288e9e9c10Sbru } 22298e9e9c10Sbru 22307f1c67caSjcs } else if (elantech_set_absolute_mode_v4(sc)) 22317f1c67caSjcs goto err; 22327f1c67caSjcs 22337f1c67caSjcs return (1); 22347f1c67caSjcs 22357f1c67caSjcs err: 22367f1c67caSjcs pms_reset(sc); 22377f1c67caSjcs 22387f1c67caSjcs return (0); 22397f1c67caSjcs } 22407f1c67caSjcs 22417f1c67caSjcs int 224272ec3668Sstsp pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 224372ec3668Sstsp struct proc *p) 224472ec3668Sstsp { 224572ec3668Sstsp struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 2246505a3534Sbru struct wsmousehw *hw; 224772ec3668Sstsp int wsmode; 224872ec3668Sstsp 224972ec3668Sstsp switch (cmd) { 225072ec3668Sstsp case WSMOUSEIO_GTYPE: 225172ec3668Sstsp *(u_int *)data = WSMOUSE_TYPE_ELANTECH; 225272ec3668Sstsp break; 225372ec3668Sstsp case WSMOUSEIO_GCALIBCOORDS: 2254505a3534Sbru hw = wsmouse_get_hw(sc->sc_wsmousedev); 2255505a3534Sbru wsmc->minx = hw->x_min; 2256505a3534Sbru wsmc->maxx = hw->x_max; 2257505a3534Sbru wsmc->miny = hw->y_min; 2258505a3534Sbru wsmc->maxy = hw->y_max; 225972ec3668Sstsp wsmc->swapxy = 0; 22604101186eSbru wsmc->resx = hw->h_res; 22614101186eSbru wsmc->resy = hw->v_res; 226272ec3668Sstsp break; 226372ec3668Sstsp case WSMOUSEIO_SETMODE: 226472ec3668Sstsp wsmode = *(u_int *)data; 226572ec3668Sstsp if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) 226672ec3668Sstsp return (EINVAL); 22675ad8c6a9Sbru wsmouse_set_mode(sc->sc_wsmousedev, wsmode); 226872ec3668Sstsp break; 226972ec3668Sstsp default: 227072ec3668Sstsp return (-1); 227172ec3668Sstsp } 227272ec3668Sstsp return (0); 227372ec3668Sstsp } 227472ec3668Sstsp 227572ec3668Sstsp int 227672ec3668Sstsp pms_sync_elantech_v1(struct pms_softc *sc, int data) 227772ec3668Sstsp { 227872ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 227972ec3668Sstsp u_char p; 228072ec3668Sstsp 228172ec3668Sstsp switch (sc->inputstate) { 228272ec3668Sstsp case 0: 228372ec3668Sstsp if (elantech->flags & ELANTECH_F_HW_V1_OLD) { 228472ec3668Sstsp elantech->p1 = (data & 0x20) >> 5; 228572ec3668Sstsp elantech->p2 = (data & 0x10) >> 4; 228672ec3668Sstsp } else { 228772ec3668Sstsp elantech->p1 = (data & 0x10) >> 4; 228872ec3668Sstsp elantech->p2 = (data & 0x20) >> 5; 228972ec3668Sstsp } 229072ec3668Sstsp elantech->p3 = (data & 0x04) >> 2; 229172ec3668Sstsp return (0); 229272ec3668Sstsp case 1: 229372ec3668Sstsp p = elantech->p1; 229472ec3668Sstsp break; 229572ec3668Sstsp case 2: 229672ec3668Sstsp p = elantech->p2; 229772ec3668Sstsp break; 229872ec3668Sstsp case 3: 229972ec3668Sstsp p = elantech->p3; 230072ec3668Sstsp break; 230172ec3668Sstsp default: 230272ec3668Sstsp return (-1); 230372ec3668Sstsp } 230472ec3668Sstsp 230572ec3668Sstsp if (data < 0 || data >= nitems(elantech->parity) || 2306c40fe122Smglocker /* 2307c40fe122Smglocker * FW 0x20022 sends inverted parity bits on cold boot, returning 2308c40fe122Smglocker * to normal after suspend & resume, so the parity check is 2309c40fe122Smglocker * disabled for this one. 2310c40fe122Smglocker */ 2311c40fe122Smglocker (elantech->fw_version != 0x20022 && elantech->parity[data] != p)) 231272ec3668Sstsp return (-1); 231372ec3668Sstsp 231472ec3668Sstsp return (0); 231572ec3668Sstsp } 231672ec3668Sstsp 231772ec3668Sstsp int 231872ec3668Sstsp pms_sync_elantech_v2(struct pms_softc *sc, int data) 231972ec3668Sstsp { 232072ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 232172ec3668Sstsp 232272ec3668Sstsp /* Variants reporting pressure always have the same constant bits. */ 232372ec3668Sstsp if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) { 232472ec3668Sstsp if (sc->inputstate == 0 && (data & 0x0c) != 0x04) 232572ec3668Sstsp return (-1); 232672ec3668Sstsp if (sc->inputstate == 3 && (data & 0x0f) != 0x02) 232772ec3668Sstsp return (-1); 232872ec3668Sstsp return (0); 232972ec3668Sstsp } 233072ec3668Sstsp 233172ec3668Sstsp /* For variants not reporting pressure, 1 and 3 finger touch packets 233243c56688Smmcc * have different constant bits than 2 finger touch packets. */ 233372ec3668Sstsp switch (sc->inputstate) { 233472ec3668Sstsp case 0: 233572ec3668Sstsp if ((data & 0xc0) == 0x80) { 233672ec3668Sstsp if ((data & 0x0c) != 0x0c) 233772ec3668Sstsp return (-1); 233872ec3668Sstsp elantech->flags |= ELANTECH_F_2FINGER_PACKET; 233972ec3668Sstsp } else { 234072ec3668Sstsp if ((data & 0x3c) != 0x3c) 234172ec3668Sstsp return (-1); 234272ec3668Sstsp elantech->flags &= ~ELANTECH_F_2FINGER_PACKET; 234372ec3668Sstsp } 234472ec3668Sstsp break; 234572ec3668Sstsp case 1: 234672ec3668Sstsp case 4: 234772ec3668Sstsp if (elantech->flags & ELANTECH_F_2FINGER_PACKET) 234872ec3668Sstsp break; 234972ec3668Sstsp if ((data & 0xf0) != 0x00) 235072ec3668Sstsp return (-1); 235172ec3668Sstsp break; 235272ec3668Sstsp case 3: 235372ec3668Sstsp if (elantech->flags & ELANTECH_F_2FINGER_PACKET) { 235472ec3668Sstsp if ((data & 0x0e) != 0x08) 235572ec3668Sstsp return (-1); 235672ec3668Sstsp } else { 235772ec3668Sstsp if ((data & 0x3e) != 0x38) 235872ec3668Sstsp return (-1); 235972ec3668Sstsp } 236072ec3668Sstsp break; 236172ec3668Sstsp default: 236272ec3668Sstsp break; 236372ec3668Sstsp } 236472ec3668Sstsp 236572ec3668Sstsp return (0); 236672ec3668Sstsp } 236772ec3668Sstsp 236872ec3668Sstsp int 236972ec3668Sstsp pms_sync_elantech_v3(struct pms_softc *sc, int data) 237072ec3668Sstsp { 237140f7e3e4Sstsp struct elantech_softc *elantech = sc->elantech; 237240f7e3e4Sstsp 237372ec3668Sstsp switch (sc->inputstate) { 237472ec3668Sstsp case 0: 237540f7e3e4Sstsp if (elantech->flags & ELANTECH_F_CRC_ENABLED) 237640f7e3e4Sstsp break; 237772ec3668Sstsp if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c) 237872ec3668Sstsp return (-1); 237972ec3668Sstsp break; 238072ec3668Sstsp case 3: 238140f7e3e4Sstsp if (elantech->flags & ELANTECH_F_CRC_ENABLED) { 238240f7e3e4Sstsp if ((data & 0x09) != 0x08 && (data & 0x09) != 0x09) 238340f7e3e4Sstsp return (-1); 238440f7e3e4Sstsp } else { 238572ec3668Sstsp if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c) 238672ec3668Sstsp return (-1); 238740f7e3e4Sstsp } 238872ec3668Sstsp break; 238972ec3668Sstsp } 239072ec3668Sstsp 239172ec3668Sstsp return (0); 239272ec3668Sstsp } 239372ec3668Sstsp 23948e9e9c10Sbru /* Extract the type bits from packet[3]. */ 23958e9e9c10Sbru static inline int 23964f06b9beSbru elantech_packet_type(struct elantech_softc *elantech, u_char b) 23978e9e9c10Sbru { 23984f06b9beSbru /* 23994f06b9beSbru * This looks dubious, but in the "crc-enabled" format bit 2 may 24004f06b9beSbru * be set even in MOTION packets. 24014f06b9beSbru */ 24024f06b9beSbru if ((elantech->flags & ELANTECH_F_TRACKPOINT) && ((b & 0x0f) == 0x06)) 24034f06b9beSbru return (ELANTECH_PKT_TRACKPOINT); 24044f06b9beSbru else 24054f06b9beSbru return (b & 0x03); 24068e9e9c10Sbru } 24078e9e9c10Sbru 24087f1c67caSjcs int 24097f1c67caSjcs pms_sync_elantech_v4(struct pms_softc *sc, int data) 24107f1c67caSjcs { 24114f06b9beSbru if (sc->inputstate == 0) 24124f06b9beSbru return ((data & 0x08) == 0 ? 0 : -1); 24134f06b9beSbru 24148e9e9c10Sbru if (sc->inputstate == 3) { 24154f06b9beSbru switch (elantech_packet_type(sc->elantech, data)) { 24168e9e9c10Sbru case ELANTECH_V4_PKT_STATUS: 24178e9e9c10Sbru case ELANTECH_V4_PKT_HEAD: 24188e9e9c10Sbru case ELANTECH_V4_PKT_MOTION: 24194f06b9beSbru if (sc->elantech->flags & ELANTECH_F_CRC_ENABLED) 24204f06b9beSbru return ((data & 0x08) == 0 ? 0 : -1); 24214f06b9beSbru else 24224f06b9beSbru return ((data & 0x1c) == 0x10 ? 0 : -1); 24238e9e9c10Sbru case ELANTECH_PKT_TRACKPOINT: 24248e9e9c10Sbru return ((sc->packet[0] & 0xc8) == 0 24258e9e9c10Sbru && sc->packet[1] == ((data & 0x10) << 3) 24268e9e9c10Sbru && sc->packet[2] == ((data & 0x20) << 2) 24278e9e9c10Sbru && (data ^ (sc->packet[0] & 0x30)) == 0x36 24288e9e9c10Sbru ? 0 : -1); 24298e9e9c10Sbru } 24308e9e9c10Sbru return (-1); 24318e9e9c10Sbru } 24327f1c67caSjcs return (0); 24337f1c67caSjcs } 24347f1c67caSjcs 243572ec3668Sstsp void 243672ec3668Sstsp pms_proc_elantech_v1(struct pms_softc *sc) 243772ec3668Sstsp { 243872ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 243972ec3668Sstsp int x, y, w, z; 24404101186eSbru u_int buttons; 24414101186eSbru 24424101186eSbru buttons = butmap[sc->packet[0] & 3]; 24434101186eSbru 24444101186eSbru if (elantech->flags & ELANTECH_F_HAS_ROCKER) { 24454101186eSbru if (sc->packet[0] & 0x40) /* up */ 24464101186eSbru buttons |= WSMOUSE_BUTTON(4); 24474101186eSbru if (sc->packet[0] & 0x80) /* down */ 24484101186eSbru buttons |= WSMOUSE_BUTTON(5); 24494101186eSbru } 245072ec3668Sstsp 245172ec3668Sstsp if (elantech->flags & ELANTECH_F_HW_V1_OLD) 245272ec3668Sstsp w = ((sc->packet[1] & 0x80) >> 7) + 245372ec3668Sstsp ((sc->packet[1] & 0x30) >> 4); 245472ec3668Sstsp else 245572ec3668Sstsp w = (sc->packet[0] & 0xc0) >> 6; 245672ec3668Sstsp 24576e9b9891Sbru /* 24586e9b9891Sbru * Firmwares 0x20022 and 0x20600 have a bug, position data in the 24596e9b9891Sbru * first two reports for single-touch contacts may be corrupt. 24606e9b9891Sbru */ 24616e9b9891Sbru if (elantech->fw_version == 0x20022 || 24626e9b9891Sbru elantech->fw_version == 0x20600) { 24636e9b9891Sbru if (w == 1) { 24646e9b9891Sbru if (elantech->initial_pkt < 2) { 24656e9b9891Sbru elantech->initial_pkt++; 24666e9b9891Sbru return; 24676e9b9891Sbru } 24686e9b9891Sbru } else if (elantech->initial_pkt) { 24696e9b9891Sbru elantech->initial_pkt = 0; 24706e9b9891Sbru } 24716e9b9891Sbru } 24726e9b9891Sbru 247372ec3668Sstsp /* Hardware version 1 doesn't report pressure. */ 247472ec3668Sstsp if (w) { 247572ec3668Sstsp x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2]; 247672ec3668Sstsp y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3]; 247772ec3668Sstsp z = SYNAPTICS_PRESSURE; 247872ec3668Sstsp } else { 24796e9b9891Sbru x = y = z = 0; 248072ec3668Sstsp } 248172ec3668Sstsp 24824101186eSbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w); 248372ec3668Sstsp } 248472ec3668Sstsp 248572ec3668Sstsp void 248672ec3668Sstsp pms_proc_elantech_v2(struct pms_softc *sc) 248772ec3668Sstsp { 248872ec3668Sstsp const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff }; 248972ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 249072ec3668Sstsp int x, y, w, z; 24914101186eSbru u_int buttons; 249272ec3668Sstsp 249372ec3668Sstsp /* 249472ec3668Sstsp * The hardware sends this packet when in debounce state. 249572ec3668Sstsp * The packet should be ignored. 249672ec3668Sstsp */ 249772ec3668Sstsp if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt))) 249872ec3668Sstsp return; 249972ec3668Sstsp 25004101186eSbru buttons = butmap[sc->packet[0] & 3]; 25014101186eSbru 250272ec3668Sstsp w = (sc->packet[0] & 0xc0) >> 6; 250372ec3668Sstsp if (w == 1 || w == 3) { 250472ec3668Sstsp x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2]; 250572ec3668Sstsp y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5]; 250672ec3668Sstsp if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) 250772ec3668Sstsp z = ((sc->packet[1] & 0xf0) | 250872ec3668Sstsp (sc->packet[4] & 0xf0) >> 4); 250972ec3668Sstsp else 251072ec3668Sstsp z = SYNAPTICS_PRESSURE; 251172ec3668Sstsp } else if (w == 2) { 251272ec3668Sstsp x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2; 251372ec3668Sstsp y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2; 251472ec3668Sstsp z = SYNAPTICS_PRESSURE; 251572ec3668Sstsp } else { 25166e9b9891Sbru x = y = z = 0; 251772ec3668Sstsp } 251872ec3668Sstsp 25194101186eSbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w); 252072ec3668Sstsp } 252172ec3668Sstsp 252272ec3668Sstsp void 252372ec3668Sstsp pms_proc_elantech_v3(struct pms_softc *sc) 252472ec3668Sstsp { 252572ec3668Sstsp const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff }; 252672ec3668Sstsp struct elantech_softc *elantech = sc->elantech; 252772ec3668Sstsp int x, y, w, z; 25284101186eSbru u_int buttons; 25294101186eSbru 25304101186eSbru buttons = butmap[sc->packet[0] & 3]; 253172ec3668Sstsp 253272ec3668Sstsp x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]); 253372ec3668Sstsp y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]); 253472ec3668Sstsp z = 0; 253572ec3668Sstsp w = (sc->packet[0] & 0xc0) >> 6; 253672ec3668Sstsp if (w == 2) { 253772ec3668Sstsp /* 253872ec3668Sstsp * Two-finger touch causes two packets -- a head packet 253972ec3668Sstsp * and a tail packet. We report a single event and ignore 254072ec3668Sstsp * the tail packet. 254172ec3668Sstsp */ 254240f7e3e4Sstsp if (elantech->flags & ELANTECH_F_CRC_ENABLED) { 254340f7e3e4Sstsp if ((sc->packet[3] & 0x09) != 0x08) 254440f7e3e4Sstsp return; 254540f7e3e4Sstsp } else { 254640f7e3e4Sstsp /* The hardware sends this packet when in debounce state. 254740f7e3e4Sstsp * The packet should be ignored. */ 254840f7e3e4Sstsp if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt))) 254940f7e3e4Sstsp return; 255072ec3668Sstsp if ((sc->packet[0] & 0x0c) != 0x04 && 255185c77397Sstsp (sc->packet[3] & 0xcf) != 0x02) { 255272ec3668Sstsp /* not the head packet -- ignore */ 255372ec3668Sstsp return; 255472ec3668Sstsp } 255572ec3668Sstsp } 255640f7e3e4Sstsp } 255772ec3668Sstsp 2558e48cb574Skrw /* Prevent jumping cursor if pad isn't touched or reports garbage. */ 255972ec3668Sstsp if (w == 0 || 256072ec3668Sstsp ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y) 256172ec3668Sstsp && (x != elantech->old_x || y != elantech->old_y))) { 256272ec3668Sstsp x = elantech->old_x; 256372ec3668Sstsp y = elantech->old_y; 256472ec3668Sstsp } 256572ec3668Sstsp 256672ec3668Sstsp if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) 256772ec3668Sstsp z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4); 256872ec3668Sstsp else if (w) 256972ec3668Sstsp z = SYNAPTICS_PRESSURE; 257072ec3668Sstsp 25714101186eSbru WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w); 25724101186eSbru elantech->old_x = x; 25734101186eSbru elantech->old_y = y; 257472ec3668Sstsp } 257572ec3668Sstsp 257672ec3668Sstsp void 25777f1c67caSjcs pms_proc_elantech_v4(struct pms_softc *sc) 25787f1c67caSjcs { 25797f1c67caSjcs struct elantech_softc *elantech = sc->elantech; 25805ad8c6a9Sbru struct device *sc_wsmousedev = sc->sc_wsmousedev; 25815ad8c6a9Sbru int id, weight, n, x, y, z; 25825ad8c6a9Sbru u_int buttons, slots; 25837f1c67caSjcs 25844f06b9beSbru switch (elantech_packet_type(elantech, sc->packet[3])) { 25857f1c67caSjcs case ELANTECH_V4_PKT_STATUS: 25865ad8c6a9Sbru slots = elantech->mt_slots; 25875ad8c6a9Sbru elantech->mt_slots = sc->packet[1] & 0x1f; 25885ad8c6a9Sbru slots &= ~elantech->mt_slots; 25895ad8c6a9Sbru for (id = 0; slots; id++, slots >>= 1) { 25905ad8c6a9Sbru if (slots & 1) 25915ad8c6a9Sbru wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0); 25925ad8c6a9Sbru } 25937f1c67caSjcs break; 25947f1c67caSjcs 25957f1c67caSjcs case ELANTECH_V4_PKT_HEAD: 25967f1c67caSjcs id = ((sc->packet[3] & 0xe0) >> 5) - 1; 25973c37ce5cSmpi if (id > -1 && id < ELANTECH_MAX_FINGERS) { 25985ad8c6a9Sbru x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2]; 25995ad8c6a9Sbru y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5]; 26005ad8c6a9Sbru z = (sc->packet[1] & 0xf0) 26013c37ce5cSmpi | ((sc->packet[4] & 0xf0) >> 4); 26025ad8c6a9Sbru wsmouse_mtstate(sc_wsmousedev, id, x, y, z); 26033c37ce5cSmpi } 26047f1c67caSjcs break; 26057f1c67caSjcs 26067f1c67caSjcs case ELANTECH_V4_PKT_MOTION: 26077f1c67caSjcs weight = (sc->packet[0] & 0x10) ? ELANTECH_V4_WEIGHT_VALUE : 1; 26083c37ce5cSmpi for (n = 0; n < 6; n += 3) { 26093c37ce5cSmpi id = ((sc->packet[n] & 0xe0) >> 5) - 1; 26103c37ce5cSmpi if (id < 0 || id >= ELANTECH_MAX_FINGERS) 26113c37ce5cSmpi continue; 26125ad8c6a9Sbru x = weight * (signed char)sc->packet[n + 1]; 26135ad8c6a9Sbru y = weight * (signed char)sc->packet[n + 2]; 26145ad8c6a9Sbru z = WSMOUSE_DEFAULT_PRESSURE; 26155ad8c6a9Sbru wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id); 26165ad8c6a9Sbru wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id); 26175ad8c6a9Sbru wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id); 26187f1c67caSjcs } 26197f1c67caSjcs break; 26207f1c67caSjcs 26218e9e9c10Sbru case ELANTECH_PKT_TRACKPOINT: 26228e9e9c10Sbru if (sc->sc_dev_enable & PMS_DEV_SECONDARY) { 2623c7b9c400Ssdk /* 2624c7b9c400Ssdk * This firmware misreport coordinates for trackpoint 2625c7b9c400Ssdk * occasionally. Discard packets outside of [-127, 127] range 2626c7b9c400Ssdk * to prevent cursor jumps. 2627c7b9c400Ssdk */ 2628c7b9c400Ssdk if (sc->packet[4] == 0x80 || sc->packet[5] == 0x80 || 2629c7b9c400Ssdk sc->packet[1] >> 7 == sc->packet[4] >> 7 || 2630c7b9c400Ssdk sc->packet[2] >> 7 == sc->packet[5] >> 7) 2631c7b9c400Ssdk return; 2632c7b9c400Ssdk 26338e9e9c10Sbru x = sc->packet[4] - 0x100 + (sc->packet[1] << 1); 26348e9e9c10Sbru y = sc->packet[5] - 0x100 + (sc->packet[2] << 1); 26358e9e9c10Sbru buttons = butmap[sc->packet[0] & 7]; 26368e9e9c10Sbru WSMOUSE_INPUT(sc->sc_sec_wsmousedev, 26378e9e9c10Sbru buttons, x, y, 0, 0); 26388e9e9c10Sbru } 26398e9e9c10Sbru return; 26408e9e9c10Sbru 26417f1c67caSjcs default: 26427f1c67caSjcs printf("%s: unknown packet type 0x%x\n", DEVNAME(sc), 26437f1c67caSjcs sc->packet[3] & 0x1f); 26447f1c67caSjcs return; 26457f1c67caSjcs } 26467f1c67caSjcs 26474101186eSbru buttons = butmap[sc->packet[0] & 3]; 26485ad8c6a9Sbru wsmouse_buttons(sc_wsmousedev, buttons); 26493c37ce5cSmpi 26505ad8c6a9Sbru wsmouse_input_sync(sc_wsmousedev); 26513c37ce5cSmpi } 2652