From owner-svn-src-head@freebsd.org Mon Sep 26 22:06:20 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 97F25BEB6C7; Mon, 26 Sep 2016 22:06:20 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 6020076C; Mon, 26 Sep 2016 22:06:20 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u8QM6J51070988; Mon, 26 Sep 2016 22:06:19 GMT (envelope-from gonzo@FreeBSD.org) Received: (from gonzo@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u8QM6J5q070986; Mon, 26 Sep 2016 22:06:19 GMT (envelope-from gonzo@FreeBSD.org) Message-Id: <201609262206.u8QM6J5q070986@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: gonzo set sender to gonzo@FreeBSD.org using -f From: Oleksandr Tymoshenko Date: Mon, 26 Sep 2016 22:06:19 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r306355 - in head/sys: dev/atkbdc sys X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 26 Sep 2016 22:06:20 -0000 Author: gonzo Date: Mon Sep 26 22:06:19 2016 New Revision: 306355 URL: https://svnweb.freebsd.org/changeset/base/306355 Log: Add Elantech trackpad support Elantech trackpads are found in some laptops like the Asus UX31E. They are "synaptics compatible" but use a slightly different protocol. Elantech hardware support is not enabled by default and just like Synaptic or TrackPoint devices it should be enabled by setting tunable, in this case hw.psm.elantech_support, to non-zero value PR: 205690 Submitted by: Vladimir Kondratyev MFC after: 1 week Modified: head/sys/dev/atkbdc/psm.c head/sys/sys/mouse.h Modified: head/sys/dev/atkbdc/psm.c ============================================================================== --- head/sys/dev/atkbdc/psm.c Mon Sep 26 20:26:19 2016 (r306354) +++ head/sys/dev/atkbdc/psm.c Mon Sep 26 22:06:19 2016 (r306355) @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -161,40 +162,6 @@ typedef struct packetbuf { #define PSM_PACKETQUEUE 128 #endif -enum { - SYNAPTICS_SYSCTL_MIN_PRESSURE, - SYNAPTICS_SYSCTL_MAX_PRESSURE, - SYNAPTICS_SYSCTL_MAX_WIDTH, - SYNAPTICS_SYSCTL_MARGIN_TOP, - SYNAPTICS_SYSCTL_MARGIN_RIGHT, - SYNAPTICS_SYSCTL_MARGIN_BOTTOM, - SYNAPTICS_SYSCTL_MARGIN_LEFT, - SYNAPTICS_SYSCTL_NA_TOP, - SYNAPTICS_SYSCTL_NA_RIGHT, - SYNAPTICS_SYSCTL_NA_BOTTOM, - SYNAPTICS_SYSCTL_NA_LEFT, - SYNAPTICS_SYSCTL_WINDOW_MIN, - SYNAPTICS_SYSCTL_WINDOW_MAX, - SYNAPTICS_SYSCTL_MULTIPLICATOR, - SYNAPTICS_SYSCTL_WEIGHT_CURRENT, - SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS, - SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA, - SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED, - SYNAPTICS_SYSCTL_DIV_MIN, - SYNAPTICS_SYSCTL_DIV_MAX, - SYNAPTICS_SYSCTL_DIV_MAX_NA, - SYNAPTICS_SYSCTL_DIV_LEN, - SYNAPTICS_SYSCTL_TAP_MAX_DELTA, - SYNAPTICS_SYSCTL_TAP_MIN_QUEUE, - SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT, - SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA, - SYNAPTICS_SYSCTL_VSCROLL_VER_AREA, - SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA, - SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN, - SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX, - SYNAPTICS_SYSCTL_TOUCHPAD_OFF -}; - typedef struct synapticsinfo { struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; @@ -231,6 +198,11 @@ typedef struct synapticsinfo { int vscroll_div_min; int vscroll_div_max; int touchpad_off; + int softbuttons_y; + int softbutton2_x; + int softbutton3_x; + int max_x; + int max_y; } synapticsinfo_t; typedef struct synapticspacket { @@ -246,22 +218,29 @@ typedef struct synapticspacket { ((synhw).infoMajor > (major) || \ ((synhw).infoMajor == (major) && (synhw).infoMinor >= (minor))) -typedef struct synapticsaction { +typedef struct smoother { synapticspacket_t queue[SYNAPTICS_PACKETQUEUE]; int queue_len; int queue_cursor; - int window_min; int start_x; int start_y; int avg_dx; int avg_dy; int squelch_x; int squelch_y; + int is_fuzzy; + int active; +} smoother_t; + +typedef struct gesture { + int window_min; int fingers_nb; int tap_button; int in_taphold; int in_vscroll; -} synapticsaction_t; + int zmax; /* maximum pressure value */ + struct timeval taptimeout; /* tap timeout for touchpads */ +} gesture_t; enum { TRACKPOINT_SYSCTL_SENSITIVITY, @@ -295,6 +274,100 @@ typedef struct trackpointinfo { int skipback; } trackpointinfo_t; +typedef struct finger { + int x; + int y; + int p; + int w; + int flags; +} finger_t; +#define PSM_FINGERS 2 /* # of processed fingers */ +#define PSM_FINGER_IS_PEN (1<<0) +#define PSM_FINGER_FUZZY (1<<1) +#define PSM_FINGER_DEFAULT_P tap_threshold +#define PSM_FINGER_DEFAULT_W 1 +#define PSM_FINGER_IS_SET(f) ((f).x != -1 && (f).y != -1 && (f).p != 0) +#define PSM_FINGER_RESET(f) do { \ + (f) = (finger_t) { .x = -1, .y = -1, .p = 0, .w = 0, .flags = 0 }; \ +} while (0) + +typedef struct elantechhw { + int hwversion; + int fwversion; + int sizex; + int sizey; + int dpmmx; + int dpmmy; + int ntracesx; + int ntracesy; + int issemimt; + int isclickpad; + int hascrc; + int hastrackpoint; + int haspressure; +} elantechhw_t; + +/* minimum versions supported by this driver */ +#define ELANTECH_HW_IS_V1(fwver) ((fwver) < 0x020030 || (fwver) == 0x020600) + +#define ELANTECH_MAGIC(magic) \ + ((magic)[0] == 0x3c && (magic)[1] == 0x03 && \ + ((magic)[2] == 0xc8 || (magic)[2] == 0x00)) + +#define ELANTECH_FW_ID 0x00 +#define ELANTECH_FW_VERSION 0x01 +#define ELANTECH_CAPABILITIES 0x02 +#define ELANTECH_SAMPLE 0x03 +#define ELANTECH_RESOLUTION 0x04 +#define ELANTECH_REG_READ 0x10 +#define ELANTECH_REG_WRITE 0x11 +#define ELANTECH_REG_RDWR 0x00 +#define ELANTECH_CUSTOM_CMD 0xf8 + +#define ELANTECH_MAX_FINGERS PSM_FINGERS + +#define ELANTECH_FINGER_SET_XYP(pb) (finger_t) { \ + .x = (((pb)->ipacket[1] & 0x0f) << 8) | (pb)->ipacket[2], \ + .y = (((pb)->ipacket[4] & 0x0f) << 8) | (pb)->ipacket[5], \ + .p = ((pb)->ipacket[1] & 0xf0) | (((pb)->ipacket[4] >> 4) & 0x0f), \ + .w = PSM_FINGER_DEFAULT_W, \ + .flags = 0 \ +} + +enum { + ELANTECH_PKT_NOP, + ELANTECH_PKT_TRACKPOINT, + ELANTECH_PKT_V2_COMMON, + ELANTECH_PKT_V2_2FINGER, + ELANTECH_PKT_V3, + ELANTECH_PKT_V4_STATUS, + ELANTECH_PKT_V4_HEAD, + ELANTECH_PKT_V4_MOTION +}; + +#define ELANTECH_PKT_IS_TRACKPOINT(pb) (((pb)->ipacket[3] & 0x0f) == 0x06) +#define ELANTECH_PKT_IS_DEBOUNCE(pb, hwversion) ((hwversion) == 4 ? 0 : \ + (pb)->ipacket[0] == ((hwversion) == 2 ? 0x84 : 0xc4) && \ + (pb)->ipacket[1] == 0xff && (pb)->ipacket[2] == 0xff && \ + (pb)->ipacket[3] == 0x02 && (pb)->ipacket[4] == 0xff && \ + (pb)->ipacket[5] == 0xff) +#define ELANTECH_PKT_IS_V2(pb) \ + (((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0x0f) == 0x02) +#define ELANTECH_PKT_IS_V3_HEAD(pb, hascrc) ((hascrc) ? \ + ((pb)->ipacket[3] & 0x09) == 0x08 : \ + ((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0xcf) == 0x02) +#define ELANTECH_PKT_IS_V3_TAIL(pb, hascrc) ((hascrc) ? \ + ((pb)->ipacket[3] & 0x09) == 0x09 : \ + ((pb)->ipacket[0] & 0x0c) == 0x0c && ((pb)->ipacket[3] & 0xce) == 0x0c) +#define ELANTECH_PKT_IS_V4(pb, hascrc) ((hascrc) ? \ + ((pb)->ipacket[3] & 0x08) == 0x00 : \ + ((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0x1c) == 0x10) + +typedef struct elantechaction { + finger_t fingers[ELANTECH_MAX_FINGERS]; + int mask; +} elantechaction_t; + /* driver control block */ struct psm_softc { /* Driver status information */ int unit; @@ -308,7 +381,10 @@ struct psm_softc { /* Driver status inf mousehw_t hw; /* hardware information */ synapticshw_t synhw; /* Synaptics hardware information */ synapticsinfo_t syninfo; /* Synaptics configuration */ - synapticsaction_t synaction; /* Synaptics action context */ + smoother_t smoother[PSM_FINGERS]; /* Motion smoothing */ + gesture_t gesture; /* Gesture context */ + elantechhw_t elanhw; /* Elantech hardware information */ + elantechaction_t elanaction; /* Elantech action context */ int tphw; /* TrackPoint hardware information */ trackpointinfo_t tpinfo; /* TrackPoint configuration */ mousemode_t mode; /* operation mode */ @@ -324,13 +400,13 @@ struct psm_softc { /* Driver status inf int xaverage; /* average X position */ int yaverage; /* average Y position */ int squelch; /* level to filter movement at low speed */ - int zmax; /* maximum pressure value for touchpads */ int syncerrors; /* # of bytes discarded to synchronize */ int pkterrors; /* # of packets failed during quaranteen. */ struct timeval inputtimeout; struct timeval lastsoftintr; /* time of last soft interrupt */ struct timeval lastinputerr; /* time last sync error happened */ - struct timeval taptimeout; /* tap timeout for touchpads */ + struct timeval idletimeout; + packetbuf_t idlepacket; /* packet to send after idle timeout */ int watchdog; /* watchdog timer flag */ struct callout callout; /* watchdog timer call out */ struct callout softcallout; /* buffer timer call out */ @@ -389,6 +465,9 @@ TUNABLE_INT("hw.psm.synaptics_support", static int trackpoint_support = 0; TUNABLE_INT("hw.psm.trackpoint_support", &trackpoint_support); +static int elantech_support = 0; +TUNABLE_INT("hw.psm.elantech_support", &elantech_support); + static int verbose = PSM_DEBUG; TUNABLE_INT("debug.psm.loglevel", &verbose); @@ -411,6 +490,44 @@ typedef struct old_mousemode { int accelfactor; } old_mousemode_t; +#define SYN_OFFSET(field) offsetof(struct psm_softc, syninfo.field) +enum { + SYNAPTICS_SYSCTL_MIN_PRESSURE = SYN_OFFSET(min_pressure), + SYNAPTICS_SYSCTL_MAX_PRESSURE = SYN_OFFSET(max_pressure), + SYNAPTICS_SYSCTL_MAX_WIDTH = SYN_OFFSET(max_width), + SYNAPTICS_SYSCTL_MARGIN_TOP = SYN_OFFSET(margin_top), + SYNAPTICS_SYSCTL_MARGIN_RIGHT = SYN_OFFSET(margin_right), + SYNAPTICS_SYSCTL_MARGIN_BOTTOM = SYN_OFFSET(margin_bottom), + SYNAPTICS_SYSCTL_MARGIN_LEFT = SYN_OFFSET(margin_left), + SYNAPTICS_SYSCTL_NA_TOP = SYN_OFFSET(na_top), + SYNAPTICS_SYSCTL_NA_RIGHT = SYN_OFFSET(na_right), + SYNAPTICS_SYSCTL_NA_BOTTOM = SYN_OFFSET(na_bottom), + SYNAPTICS_SYSCTL_NA_LEFT = SYN_OFFSET(na_left), + SYNAPTICS_SYSCTL_WINDOW_MIN = SYN_OFFSET(window_min), + SYNAPTICS_SYSCTL_WINDOW_MAX = SYN_OFFSET(window_max), + SYNAPTICS_SYSCTL_MULTIPLICATOR = SYN_OFFSET(multiplicator), + SYNAPTICS_SYSCTL_WEIGHT_CURRENT = SYN_OFFSET(weight_current), + SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS = SYN_OFFSET(weight_previous), + SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA = SYN_OFFSET(weight_previous_na), + SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED = SYN_OFFSET(weight_len_squared), + SYNAPTICS_SYSCTL_DIV_MIN = SYN_OFFSET(div_min), + SYNAPTICS_SYSCTL_DIV_MAX = SYN_OFFSET(div_max), + SYNAPTICS_SYSCTL_DIV_MAX_NA = SYN_OFFSET(div_max_na), + SYNAPTICS_SYSCTL_DIV_LEN = SYN_OFFSET(div_len), + SYNAPTICS_SYSCTL_TAP_MAX_DELTA = SYN_OFFSET(tap_max_delta), + SYNAPTICS_SYSCTL_TAP_MIN_QUEUE = SYN_OFFSET(tap_min_queue), + SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT = SYN_OFFSET(taphold_timeout), + SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA = SYN_OFFSET(vscroll_hor_area), + SYNAPTICS_SYSCTL_VSCROLL_VER_AREA = SYN_OFFSET(vscroll_ver_area), + SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA = SYN_OFFSET(vscroll_min_delta), + SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN = SYN_OFFSET(vscroll_div_min), + SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX = SYN_OFFSET(vscroll_div_max), + SYNAPTICS_SYSCTL_TOUCHPAD_OFF = SYN_OFFSET(touchpad_off), + SYNAPTICS_SYSCTL_SOFTBUTTONS_Y = SYN_OFFSET(softbuttons_y), + SYNAPTICS_SYSCTL_SOFTBUTTON2_X = SYN_OFFSET(softbutton2_x), + SYNAPTICS_SYSCTL_SOFTBUTTON3_X = SYN_OFFSET(softbutton3_x), +}; + /* packet formatting function */ typedef int packetfunc_t(struct psm_softc *, u_char *, int *, int, mousestatus_t *); @@ -446,6 +563,7 @@ static int doopen(struct psm_softc *, in static int reinitialize(struct psm_softc *, int); static char *model_name(int); static void psmsoftintr(void *); +static void psmsoftintridle(void *); static void psmintr(void *); static void psmtimeout(void *); static int timeelapsed(const struct timeval *, int, int, @@ -458,6 +576,13 @@ static int proc_synaptics(struct psm_sof mousestatus_t *, int *, int *, int *); static void proc_versapad(struct psm_softc *, packetbuf_t *, mousestatus_t *, int *, int *, int *); +static int proc_elantech(struct psm_softc *, packetbuf_t *, + mousestatus_t *, int *, int *, int *); +static int psmpalmdetect(struct psm_softc *, finger_t *, int); +static void psmgestures(struct psm_softc *, finger_t *, int, + mousestatus_t *); +static void psmsmoother(struct psm_softc *, finger_t *, int, + mousestatus_t *, int *, int *); static int tame_mouse(struct psm_softc *, packetbuf_t *, mousestatus_t *, u_char *); @@ -480,6 +605,7 @@ static probefunc_t enable_mmanplus; static probefunc_t enable_synaptics; static probefunc_t enable_trackpoint; static probefunc_t enable_versapad; +static probefunc_t enable_elantech; static void set_trackpoint_parameters(struct psm_softc *sc); static void synaptics_passthrough_on(struct psm_softc *sc); @@ -511,6 +637,8 @@ static struct { 0xc8, MOUSE_4DPLUS_PACKETSIZE, enable_4dplus }, { MOUSE_MODEL_SYNAPTICS, /* Synaptics Touchpad */ 0xc0, MOUSE_SYNAPTICS_PACKETSIZE, enable_synaptics }, + { MOUSE_MODEL_ELANTECH, /* Elantech Touchpad */ + 0x04, MOUSE_ELANTECH_PACKETSIZE, enable_elantech }, { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */ 0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msintelli }, { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */ @@ -762,6 +890,7 @@ model_name(int model) { MOUSE_MODEL_4DPLUS, "4D+ Mouse" }, { MOUSE_MODEL_SYNAPTICS, "Synaptics Touchpad" }, { MOUSE_MODEL_TRACKPOINT, "IBM/Lenovo TrackPoint" }, + { MOUSE_MODEL_ELANTECH, "Elantech Touchpad" }, { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" }, { MOUSE_MODEL_UNKNOWN, "Unknown" }, }; @@ -1504,6 +1633,7 @@ psmattach(device_t dev) case MOUSE_MODEL_SYNAPTICS: case MOUSE_MODEL_GLIDEPOINT: case MOUSE_MODEL_VERSAPAD: + case MOUSE_MODEL_ELANTECH: sc->config |= PSM_CONFIG_INITAFTERSUSPEND; break; default: @@ -1512,6 +1642,11 @@ psmattach(device_t dev) break; } + /* Elantech trackpad`s sync bit differs from touchpad`s one */ + if (sc->hw.model == MOUSE_MODEL_ELANTECH && + (sc->elanhw.hascrc || sc->elanhw.hastrackpoint)) + sc->config |= PSM_CONFIG_NOCHECKSYNC; + if (!verbose) printf("psm%d: model %s, device ID %d\n", unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff); @@ -2165,20 +2300,6 @@ psmioctl(struct cdev *dev, u_long cmd, c (*(int *)addr > PSM_LEVEL_MAX)) return (EINVAL); sc->mode.level = *(int *)addr; - - if (sc->hw.model == MOUSE_MODEL_SYNAPTICS) { - /* - * If we are entering PSM_LEVEL_NATIVE, we want to - * enable sending of "extended W mode" packets to - * userland. Reset the mode of the touchpad so that the - * change in the level is picked up. - */ - error = block_mouse_data(sc, &command_byte); - if (error) - return (error); - synaptics_set_mode(sc, synaptics_preferred_mode(sc)); - unblock_mouse_data(sc, command_byte); - } break; case MOUSE_GETSTATUS: @@ -2627,7 +2748,8 @@ proc_synaptics(struct psm_softc *sc, pac { static int touchpad_buttons; static int guest_buttons; - int w, x0, y0; + static finger_t f[PSM_FINGERS]; + int w, id, nfingers, ewcode; /* TouchPad PS/2 absolute mode message format with capFourButtons: * @@ -2683,6 +2805,7 @@ proc_synaptics(struct psm_softc *sc, pac return (-1); *x = *y = 0; + ms->button = ms->obutton; /* * Pressure value. @@ -2718,34 +2841,79 @@ proc_synaptics(struct psm_softc *sc, pac w = 4; } - /* - * Handle packets from the guest device. See: - * Synaptics PS/2 TouchPad Interfacing Guide, Section 5.1 - */ - if (w == 3 && sc->synhw.capPassthrough) { - *x = ((pb->ipacket[1] & 0x10) ? - pb->ipacket[4] - 256 : pb->ipacket[4]); - *y = ((pb->ipacket[1] & 0x20) ? - pb->ipacket[5] - 256 : pb->ipacket[5]); - *z = 0; + switch (w) { + case 3: + /* + * Handle packets from the guest device. See: + * Synaptics PS/2 TouchPad Interfacing Guide, Section 5.1 + */ + if (sc->synhw.capPassthrough) { + *x = ((pb->ipacket[1] & 0x10) ? + pb->ipacket[4] - 256 : pb->ipacket[4]); + *y = ((pb->ipacket[1] & 0x20) ? + pb->ipacket[5] - 256 : pb->ipacket[5]); + *z = 0; + + guest_buttons = 0; + if (pb->ipacket[1] & 0x01) + guest_buttons |= MOUSE_BUTTON1DOWN; + if (pb->ipacket[1] & 0x04) + guest_buttons |= MOUSE_BUTTON2DOWN; + if (pb->ipacket[1] & 0x02) + guest_buttons |= MOUSE_BUTTON3DOWN; - guest_buttons = 0; - if (pb->ipacket[1] & 0x01) - guest_buttons |= MOUSE_BUTTON1DOWN; - if (pb->ipacket[1] & 0x04) - guest_buttons |= MOUSE_BUTTON2DOWN; - if (pb->ipacket[1] & 0x02) - guest_buttons |= MOUSE_BUTTON3DOWN; + ms->button = touchpad_buttons | guest_buttons; + } + goto SYNAPTICS_END; - ms->button = touchpad_buttons | guest_buttons; + case 2: + /* Handle Extended W mode packets */ + ewcode = (pb->ipacket[5] & 0xf0) >> 4; +#if PSM_FINGERS > 1 + switch (ewcode) { + case 1: + /* Secondary finger */ + if (sc->synhw.capAdvancedGestures) + f[1] = (finger_t) { + .x = (((pb->ipacket[4] & 0x0f) << 8) | + pb->ipacket[1]) << 1, + .y = (((pb->ipacket[4] & 0xf0) << 4) | + pb->ipacket[2]) << 1, + .p = ((pb->ipacket[3] & 0x30) | + (pb->ipacket[5] & 0x0f)) << 1, + .w = PSM_FINGER_DEFAULT_W, + .flags = PSM_FINGER_FUZZY, + }; + else if (sc->synhw.capReportsV) + f[1] = (finger_t) { + .x = (((pb->ipacket[4] & 0x0f) << 8) | + (pb->ipacket[1] & 0xfe)) << 1, + .y = (((pb->ipacket[4] & 0xf0) << 4) | + (pb->ipacket[2] & 0xfe)) << 1, + .p = ((pb->ipacket[3] & 0x30) | + (pb->ipacket[5] & 0x0e)) << 1, + .w = (((pb->ipacket[5] & 0x01) << 2) | + ((pb->ipacket[2] & 0x01) << 1) | + (pb->ipacket[1] & 0x01)) + 8, + .flags = PSM_FINGER_FUZZY, + }; + default: + break; + } +#endif goto SYNAPTICS_END; + + case 1: + case 0: + nfingers = w + 2; + break; + + default: + nfingers = 1; } - if (sc->syninfo.touchpad_off) { - *x = *y = *z = 0; - ms->button = ms->obutton; + if (sc->syninfo.touchpad_off) goto SYNAPTICS_END; - } /* Button presses */ touchpad_buttons = 0; @@ -2764,6 +2932,10 @@ proc_synaptics(struct psm_softc *sc, pac /* Middle Button */ if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01) touchpad_buttons |= MOUSE_BUTTON2DOWN; + } else if (sc->synhw.capExtended && sc->synhw.capClickPad) { + /* ClickPad Button */ + if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01) + touchpad_buttons = MOUSE_BUTTON1DOWN; } else if (sc->synhw.capExtended && (sc->synhw.nExtendedButtons > 0)) { /* Extended Buttons */ if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x02) { @@ -2806,7 +2978,7 @@ proc_synaptics(struct psm_softc *sc, pac pb->ipacket[4] &= ~(mask); pb->ipacket[5] &= ~(mask); } else if (!sc->syninfo.directional_scrolls && - !sc->synaction.in_vscroll) { + !sc->gesture.in_vscroll) { /* * Keep reporting MOUSE DOWN until we get a new packet * indicating otherwise. @@ -2814,93 +2986,179 @@ proc_synaptics(struct psm_softc *sc, pac touchpad_buttons |= sc->extended_buttons; } } - /* Handle ClickPad. */ - if (sc->synhw.capClickPad && - ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01)) - touchpad_buttons |= MOUSE_BUTTON1DOWN; + + if (sc->synhw.capReportsV && nfingers > 1) + f[0] = (finger_t) { + .x = ((pb->ipacket[3] & 0x10) << 8) | + ((pb->ipacket[1] & 0x0f) << 8) | + (pb->ipacket[4] & 0xfd), + .y = ((pb->ipacket[3] & 0x20) << 7) | + ((pb->ipacket[1] & 0xf0) << 4) | + (pb->ipacket[5] & 0xfd), + .p = *z & 0xfe, + .w = (((pb->ipacket[2] & 0x01) << 2) | + (pb->ipacket[5] & 0x02) | + ((pb->ipacket[4] & 0x02) >> 1)) + 8, + .flags = PSM_FINGER_FUZZY, + }; + else + f[0] = (finger_t) { + .x = ((pb->ipacket[3] & 0x10) << 8) | + ((pb->ipacket[1] & 0x0f) << 8) | + pb->ipacket[4], + .y = ((pb->ipacket[3] & 0x20) << 7) | + ((pb->ipacket[1] & 0xf0) << 4) | + pb->ipacket[5], + .p = *z, + .w = w, + .flags = nfingers > 1 ? PSM_FINGER_FUZZY : 0, + }; + + /* Ignore hovering and unmeasurable touches */ + if (f[0].p < sc->syninfo.min_pressure || f[0].x < 2) + nfingers = 0; + + for (id = 0; id < PSM_FINGERS; id++) + if (id >= nfingers) + PSM_FINGER_RESET(f[id]); ms->button = touchpad_buttons | guest_buttons; + /* Palm detection doesn't terminate the current action. */ + if (!psmpalmdetect(sc, &f[0], nfingers)) { + psmgestures(sc, &f[0], nfingers, ms); + for (id = 0; id < PSM_FINGERS; id++) + psmsmoother(sc, &f[id], id, ms, x, y); + } else { + VLOG(2, (LOG_DEBUG, "synaptics: palm detected! (%d)\n", f[0].w)); + } + +SYNAPTICS_END: + /* + * Use the extra buttons as a scrollwheel + * + * XXX X.Org uses the Z axis for vertical wheel only, + * whereas moused(8) understands special values to differ + * vertical and horizontal wheels. + * + * xf86-input-mouse needs therefore a small patch to + * understand these special values. Without it, the + * horizontal wheel acts as a vertical wheel in X.Org. + * + * That's why the horizontal wheel is disabled by + * default for now. + */ + if (ms->button & MOUSE_BUTTON4DOWN) + *z = -1; + else if (ms->button & MOUSE_BUTTON5DOWN) + *z = 1; + else if (ms->button & MOUSE_BUTTON6DOWN) + *z = -2; + else if (ms->button & MOUSE_BUTTON7DOWN) + *z = 2; + else + *z = 0; + ms->button &= ~(MOUSE_BUTTON4DOWN | MOUSE_BUTTON5DOWN | + MOUSE_BUTTON6DOWN | MOUSE_BUTTON7DOWN); + + return (0); +} + +static int +psmpalmdetect(struct psm_softc *sc, finger_t *f, int nfingers) +{ + if (!( + ((sc->synhw.capMultiFinger || + sc->synhw.capAdvancedGestures) && nfingers > 1) || + (sc->synhw.capPalmDetect && f->w <= sc->syninfo.max_width) || + (!sc->synhw.capPalmDetect && f->p <= sc->syninfo.max_pressure) || + (sc->synhw.capPen && f->flags & PSM_FINGER_IS_PEN))) { + /* + * We consider the packet irrelevant for the current + * action when: + * - the width isn't comprised in: + * [1; max_width] + * - the pressure isn't comprised in: + * [min_pressure; max_pressure] + * - pen aren't supported but PSM_FINGER_IS_PEN is set + */ + return (1); + } + return (0); +} + +static void +psmgestures(struct psm_softc *sc, finger_t *fingers, int nfingers, + mousestatus_t *ms) +{ + smoother_t *smoother; + gesture_t *gest; + finger_t *f; + int y_ok, center_button, center_x, right_button, right_x, i; + + f = &fingers[0]; + smoother = &sc->smoother[0]; + gest = &sc->gesture; + + /* Find first active finger. */ + if (nfingers > 0) { + for (i = 0; i < PSM_FINGERS; i++) { + if (PSM_FINGER_IS_SET(fingers[i])) { + f = &fingers[i]; + smoother = &sc->smoother[i]; + break; + } + } + } + /* * Check pressure to detect a real wanted action on the * touchpad. */ - if (*z >= sc->syninfo.min_pressure) { - synapticsaction_t *synaction; - int cursor, peer, window; - int dx, dy, dxp, dyp; - int max_width, max_pressure; + if (f->p >= sc->syninfo.min_pressure) { + int x0, y0; + int dxp, dyp; + int start_x, start_y; + int queue_len; int margin_top, margin_right, margin_bottom, margin_left; - int na_top, na_right, na_bottom, na_left; int window_min, window_max; - int multiplicator; - int weight_current, weight_previous, weight_len_squared; - int div_min, div_max, div_len; int vscroll_hor_area, vscroll_ver_area; int two_finger_scroll; - int len, weight_prev_x, weight_prev_y; - int div_max_x, div_max_y, div_x, div_y; - int exiting_scroll; + int max_x, max_y; /* Read sysctl. */ /* XXX Verify values? */ - max_width = sc->syninfo.max_width; - max_pressure = sc->syninfo.max_pressure; margin_top = sc->syninfo.margin_top; margin_right = sc->syninfo.margin_right; margin_bottom = sc->syninfo.margin_bottom; margin_left = sc->syninfo.margin_left; - na_top = sc->syninfo.na_top; - na_right = sc->syninfo.na_right; - na_bottom = sc->syninfo.na_bottom; - na_left = sc->syninfo.na_left; window_min = sc->syninfo.window_min; window_max = sc->syninfo.window_max; - multiplicator = sc->syninfo.multiplicator; - weight_current = sc->syninfo.weight_current; - weight_previous = sc->syninfo.weight_previous; - weight_len_squared = sc->syninfo.weight_len_squared; - div_min = sc->syninfo.div_min; - div_max = sc->syninfo.div_max; - div_len = sc->syninfo.div_len; vscroll_hor_area = sc->syninfo.vscroll_hor_area; vscroll_ver_area = sc->syninfo.vscroll_ver_area; two_finger_scroll = sc->syninfo.two_finger_scroll; - - exiting_scroll = 0; - - /* Palm detection. */ - if (!( - ((sc->synhw.capMultiFinger || - sc->synhw.capAdvancedGestures) && (w == 0 || w == 1)) || - (sc->synhw.capPalmDetect && w >= 4 && w <= max_width) || - (!sc->synhw.capPalmDetect && *z <= max_pressure) || - (sc->synhw.capPen && w == 2))) { - /* - * We consider the packet irrelevant for the current - * action when: - * - the width isn't comprised in: - * [4; max_width] - * - the pressure isn't comprised in: - * [min_pressure; max_pressure] - * - pen aren't supported but w is 2 - * - * Note that this doesn't terminate the current action. - */ - VLOG(2, (LOG_DEBUG, - "synaptics: palm detected! (%d)\n", w)); - goto SYNAPTICS_END; - } + max_x = sc->syninfo.max_x; + max_y = sc->syninfo.max_y; /* Read current absolute position. */ - x0 = ((pb->ipacket[3] & 0x10) << 8) | - ((pb->ipacket[1] & 0x0f) << 8) | - pb->ipacket[4]; - y0 = ((pb->ipacket[3] & 0x20) << 7) | - ((pb->ipacket[1] & 0xf0) << 4) | - pb->ipacket[5]; + x0 = f->x; + y0 = f->y; + + /* + * Limit the coordinates to the specified margins because + * this area isn't very reliable. + */ + if (x0 <= margin_left) + x0 = margin_left; + else if (x0 >= max_x - margin_right) + x0 = max_x - margin_right; + if (y0 <= margin_bottom) + y0 = margin_bottom; + else if (y0 >= max_y - margin_top) + y0 = max_y - margin_top; - synaction = &(sc->synaction); + VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n", + x0, y0, f->p, f->w)); /* * If the action is just beginning, init the structure and @@ -2909,107 +3167,82 @@ proc_synaptics(struct psm_softc *sc, pac if (!(sc->flags & PSM_FLAGS_FINGERDOWN)) { VLOG(3, (LOG_DEBUG, "synaptics: ----\n")); - /* Store the first point of this action. */ - synaction->start_x = x0; - synaction->start_y = y0; - dx = dy = 0; - /* Initialize queue. */ - synaction->queue_cursor = SYNAPTICS_PACKETQUEUE; - synaction->queue_len = 0; - synaction->window_min = window_min; - - /* Reset average. */ - synaction->avg_dx = 0; - synaction->avg_dy = 0; - - /* Reset squelch. */ - synaction->squelch_x = 0; - synaction->squelch_y = 0; + gest->window_min = window_min; /* Reset pressure peak. */ - sc->zmax = 0; + gest->zmax = 0; /* Reset fingers count. */ - synaction->fingers_nb = 0; + gest->fingers_nb = 0; /* Reset virtual scrolling state. */ - synaction->in_vscroll = 0; + gest->in_vscroll = 0; /* Compute tap timeout. */ - sc->taptimeout.tv_sec = tap_timeout / 1000000; - sc->taptimeout.tv_usec = tap_timeout % 1000000; - timevaladd(&sc->taptimeout, &sc->lastsoftintr); + gest->taptimeout.tv_sec = tap_timeout / 1000000; + gest->taptimeout.tv_usec = tap_timeout % 1000000; + timevaladd(&gest->taptimeout, &sc->lastsoftintr); sc->flags |= PSM_FLAGS_FINGERDOWN; + + /* Smoother has not been reset yet */ + queue_len = 1; + start_x = x0; + start_y = y0; } else { - /* Calculate the current delta. */ - cursor = synaction->queue_cursor; - dx = x0 - synaction->queue[cursor].x; - dy = y0 - synaction->queue[cursor].y; + queue_len = smoother->queue_len + 1; + start_x = smoother->start_x; + start_y = smoother->start_y; } - /* If in tap-hold, add the recorded button. */ - if (synaction->in_taphold) - ms->button |= synaction->tap_button; - - /* - * From now on, we can use the SYNAPTICS_END label to skip - * the current packet. - */ - - /* - * Limit the coordinates to the specified margins because - * this area isn't very reliable. - */ - if (x0 <= margin_left) - x0 = margin_left; - else if (x0 >= 6143 - margin_right) - x0 = 6143 - margin_right; - if (y0 <= margin_bottom) - y0 = margin_bottom; - else if (y0 >= 6143 - margin_top) - y0 = 6143 - margin_top; + /* Process ClickPad softbuttons */ + if (sc->synhw.capClickPad && ms->button & MOUSE_BUTTON1DOWN) { + y_ok = sc->syninfo.softbuttons_y >= 0 ? + start_y < sc->syninfo.softbuttons_y : + start_y > max_y - sc->syninfo.softbuttons_y; + + center_button = MOUSE_BUTTON2DOWN; + center_x = sc->syninfo.softbutton2_x; + right_button = MOUSE_BUTTON3DOWN; + right_x = sc->syninfo.softbutton3_x; + + if (center_x > 0 && right_x > 0 && center_x > right_x) { + center_button = MOUSE_BUTTON3DOWN; + center_x = sc->syninfo.softbutton3_x; + right_button = MOUSE_BUTTON2DOWN; + right_x = sc->syninfo.softbutton2_x; + } - VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n", - x0, y0, *z, w)); + if (right_x > 0 && start_x > right_x && y_ok) + ms->button = (ms->button & + ~MOUSE_BUTTON1DOWN) | right_button; + else if (center_x > 0 && start_x > center_x && y_ok) + ms->button = (ms->button & + ~MOUSE_BUTTON1DOWN) | center_button; + } - /* Queue this new packet. */ - cursor = SYNAPTICS_QUEUE_CURSOR(synaction->queue_cursor - 1); - synaction->queue[cursor].x = x0; - synaction->queue[cursor].y = y0; - synaction->queue_cursor = cursor; - if (synaction->queue_len < SYNAPTICS_PACKETQUEUE) - synaction->queue_len++; - VLOG(5, (LOG_DEBUG, - "synaptics: cursor[%d]: x=%d, y=%d, dx=%d, dy=%d\n", - cursor, x0, y0, dx, dy)); + /* If in tap-hold, add the recorded button. */ + if (gest->in_taphold) + ms->button |= gest->tap_button; /* * For tap, we keep the maximum number of fingers and the * pressure peak. Also with multiple fingers, we increase * the minimum window. */ - switch (w) { - case 1: /* Three or more fingers. */ - synaction->fingers_nb = imax(3, synaction->fingers_nb); - synaction->window_min = window_max; - break; - case 0: /* Two fingers. */ - synaction->fingers_nb = imax(2, synaction->fingers_nb); - synaction->window_min = window_max; - break; - default: /* One finger or undetectable. */ - synaction->fingers_nb = imax(1, synaction->fingers_nb); - } - sc->zmax = imax(*z, sc->zmax); - - /* Do we have enough packets to consider this a movement? */ - if (synaction->queue_len < synaction->window_min) - goto SYNAPTICS_END; + if (nfingers > 1) + gest->window_min = window_max; + gest->fingers_nb = imax(nfingers, gest->fingers_nb); + gest->zmax = imax(f->p, gest->zmax); + + /* Do we have enough packets to consider this a gesture? */ + if (queue_len < gest->window_min) + return; /* Is a scrolling action occurring? */ - if (!synaction->in_taphold && !synaction->in_vscroll) { + if (!gest->in_taphold && !ms->button && + (!gest->in_vscroll || two_finger_scroll)) { /* * A scrolling action must not conflict with a tap * action. Here are the conditions to consider a @@ -3020,12 +3253,10 @@ proc_synaptics(struct psm_softc *sc, pac * first should be above a configurable minimum * . tap timed out */ - dxp = abs(synaction->queue[synaction->queue_cursor].x - - synaction->start_x); - dyp = abs(synaction->queue[synaction->queue_cursor].y - - synaction->start_y); + dxp = abs(x0 - start_x); + dyp = abs(y0 - start_y); - if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, >) || + if (timevalcmp(&sc->lastsoftintr, &gest->taptimeout, >) || dxp >= sc->syninfo.vscroll_min_delta || dyp >= sc->syninfo.vscroll_min_delta) { /* @@ -3034,176 +3265,65 @@ proc_synaptics(struct psm_softc *sc, pac * as that keeps the maximum number of fingers. */ if (two_finger_scroll) { - if (w == 0) { - synaction->in_vscroll += + if (nfingers == 2) { + gest->in_vscroll += dyp ? 2 : 0; - synaction->in_vscroll += + gest->in_vscroll += dxp ? 1 : 0; } } else { /* Check for horizontal scrolling. */ if ((vscroll_hor_area > 0 && - synaction->start_y <= - vscroll_hor_area) || + start_y <= vscroll_hor_area) || (vscroll_hor_area < 0 && - synaction->start_y >= - 6143 + vscroll_hor_area)) - synaction->in_vscroll += 2; + start_y >= + max_y + vscroll_hor_area)) + gest->in_vscroll += 2; /* Check for vertical scrolling. */ if ((vscroll_ver_area > 0 && - synaction->start_x <= - vscroll_ver_area) || + start_x <= vscroll_ver_area) || (vscroll_ver_area < 0 && - synaction->start_x >= - 6143 + vscroll_ver_area)) - synaction->in_vscroll += 1; + start_x >= + max_x + vscroll_ver_area)) + gest->in_vscroll += 1; } /* Avoid conflicts if area overlaps. */ - if (synaction->in_vscroll >= 3) - synaction->in_vscroll = + if (gest->in_vscroll >= 3) + gest->in_vscroll = (dxp > dyp) ? 2 : 1; } } /* * Reset two finger scrolling when the number of fingers - * is different from two. + * is different from two or any button is pressed. */ - if (two_finger_scroll && w != 0 && synaction->in_vscroll != 0) { - synaction->in_vscroll = 0; - exiting_scroll = 1; - } + if (two_finger_scroll && gest->in_vscroll != 0 && + (nfingers != 2 || ms->button)) + gest->in_vscroll = 0; VLOG(5, (LOG_DEBUG, "synaptics: virtual scrolling: %s " "(direction=%d, dxp=%d, dyp=%d, fingers=%d)\n", - synaction->in_vscroll ? "YES" : "NO", - synaction->in_vscroll, dxp, dyp, - synaction->fingers_nb)); - - weight_prev_x = weight_prev_y = weight_previous; - div_max_x = div_max_y = div_max; - - if (synaction->in_vscroll) { - /* Dividers are different with virtual scrolling. */ - div_min = sc->syninfo.vscroll_div_min; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***