On Sun, Oct 28, 2012 at 03:47:14PM +0100, Stefan Sperling wrote:
> On Mon, Oct 08, 2012 at 06:16:22PM +0200, Martin Pieuchot wrote:
> > I don't have any Elantech hardware to test but I can tell you that this
> > version doesn't introduce any regression with my ALPS touchpad. It could
> > be nice to hear from other touchpad owners too ;)
>
> I haven't received test reports from anyone else. I suspect many don't
> realise that their laptop contains an elantech touchpad.
>
> > > +int
> > > +pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int
> > > flag,
> > > + struct proc *p)
> > > +{
> > > + struct elantech_softc *elantech = sc->elantech;
> > > + struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
> > > + int wsmode;
> > > +
> > > + switch (cmd) {
> > > + case WSMOUSEIO_GTYPE:
> > > + *(u_int *)data = WSMOUSE_TYPE_SYNAPTICS;
> >
> > Here you may want to define a WSMOUSE_TYPE_ELANTECH, it is used in the
> > synaptics(4) driver to describe which "features" are supported by this
> > touchpad, see xenocara/driver/xf86-input-synaptics/src/wsconscomm.c
> >
> > But if you do so, you'll obviously need to rebuild the X driver too ;)
>
> Yes, I've done this now. The new kernel + X driver diff is below.
> The synaptics driver already has some elantech-specific code which
> gets activated with this change.
>
> > > +void
> > > +pms_proc_elantech_v3(struct pms_softc *sc)
> > > +{
> > > + const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
> > > + struct elantech_softc *elantech = sc->elantech;
> > > + u_int buttons;
> > > + int fingers, x, y, w, z;
> > > +
> > > + /* The hardware sends this packet when in debounce state.
> > > + * The packet should be ignored. */
> > > + if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
> > > + return;
> > > +
> > > + buttons = ((sc->packet[0] & 0x01 ? WSMOUSE_BUTTON(1) : 0) |
> > > + ((sc->packet[0] & 0x02) ? WSMOUSE_BUTTON(3): 0));
> > > + x = y = z = 0;
> > > + w = -1; /* corresponds to no finger, see synaptics */
> > > + fingers = (sc->packet[0] & 0xc0) >> 6;
> > > + if (fingers == 2) {
> > > + /* Two-finger touch causes two packets -- a head packet
> > > + * and a tail packet. */
> > > + if ((sc->packet[0] & 0x0c) == 0x04 &&
> > > + (sc->packet[3] & 0xfc) == 0x02) {
> > > + /* head packet */
> > > + x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
> > > + y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
> > > + } else if ((sc->packet[0] & 0x0c) == 0x0c &&
> > > + (sc->packet[3] & 0xce) == 0x0c) {
> > > + /* tail packet */
> > > + x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
> > > + y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
> >
> > Is it a typo or the 'x' and 'y' calculation are the same in both cases?
>
> Yes. There are two packets, one per finger.
>
> It turns out we can ignore the second packet. This makes two-finger scrolling
> smoother than it was before. Reporting both events was causing occasional
> jumps during two-finger scrolling.
>
> >
> > > + }
> > > + w = 0; /* force 2 fingers in synaptics */
> >
> > This isn't related to your diff, but we should add defines for
> > w = {-1, 0, 3, 4} because these values come from the synaptics devices.
>
> Indeed. I've added a comment to wsconscomm.c in synaptics.
>
> > > + } else if (fingers == 1 || fingers == 3) {
> > > + x = (sc->packet[1] & 0x0f) << 8 | sc->packet[2];
> > > + y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
> > > + w = (fingers == 3 ? 1 : 4); /* values for synaptics */
> > > + }
> >
> > Same here for the 'x' and 'y', maybe you just need to convert the value
> > you call 'fingers' to the corresponding 'w' (synaptics finger value).
>
> By reporting the touchpad as an Elantech pad to synaptics we can get
> rid of the 'fingers' variable and just assign the value to 'w' instead.
>
> > > +void
> > > +elantech_send_input(struct pms_softc *sc, u_int buttons, int x, int y,
> > > int z,
> > > + int w)
> > > + {
> > > + struct elantech_softc *elantech = sc->elantech;
> > > + int dx, dy;
> > > +
> > > + if (elantech->wsmode == WSMOUSE_NATIVE) {
> > > + if ((x > 0 && y > 0) || buttons)
> >
> > I think this condition is always true, or does the elantech touchpads
> > generate packets without position nor buttons pressed? Plus in case of
> > the v3 if x or y are null you explicitly force them to their previous
> > know position.
>
> Yes, this check is bogus. I've removed it.
>
> > > +/* Hardware version 1 has hard-coded axis range values.
> > > + * X axis range is 0 to 576, Y axis range is 0 to 384.
> > > + * Edge offset accounts for bezel around the touchpad. */
> > > +#define ELANTECH_V1_EDGE_OFFSET 32
> > > +#define ELANTECH_V1_X_MIN (0 + ELANTECH_V1_EDGE_OFFSET)
> > > +#define ELANTECH_V1_X_MAX (576 - ELANTECH_V1_EDGE_OFFSET)
> > > +#define ELANTECH_V1_Y_MIN (0 + ELANTECH_V1_EDGE_OFFSET)
> > > +#define ELANTECH_V1_Y_MAX (384 - ELANTECH_V1_EDGE_OFFSET)
> >
> > Some nitpicking, can you use just one space after "#define" to be
> > coherent with the rest of the file ;)
>
> Done.
>
> > Apart from that, does your touchpad respond to the ELANTECH_QUE_FW_ID
> > query before being in absolute mode? Because if it does you can remove
> > the set_absolute_mode_vN() call from get_hwinfo_vN() and turn the latter
> > into a version independent function.
> > This would allow you to simplify the enable_elantech_vN() by sharing
> > the get_hwinfo() code and merging the set_absolute_mode_vN() code in it.
>
> Good idea but it doesn't work.
> We need to switch the pad into absolute mode first. Else it reports
> bogus width/height values which causes the mouse cursor to behave
> erratically and breaks edge scrolling.
>
> Thanks for the review! Is this good for commit now?
>
ok shadchin@, no regress with synaptics touchapd
Add this patch (for wsconsctl)
Index: util.c
===================================================================
RCS file: /cvs/src/sbin/wsconsctl/util.c,v
retrieving revision 1.58
diff -u -p -r1.58 util.c
--- util.c 14 Jul 2012 08:25:12 -0000 1.58
+++ util.c 28 Oct 2012 16:01:54 -0000
@@ -91,7 +91,8 @@ static const struct nameint mstype_tab[]
{ WSMOUSE_TYPE_SUN, "sun" },
{ WSMOUSE_TYPE_SYNAPTICS, "synaptics" },
{ WSMOUSE_TYPE_ALPS, "alps" },
- { WSMOUSE_TYPE_SGI, "sgi" }
+ { WSMOUSE_TYPE_SGI, "sgi" },
+ { WSMOUSE_TYPE_ELANTECH, "elantech" }
};
static const struct nameint dpytype_tab[] = {
Also few comments below.
> This diff doesn't attach to v1 and v2 hardware yet because these have
> not been tested. If anyone wants to test v1 or v2 hardware just remove
> the #ifdef notyet / #endif.
>
> Kernel patch (X driver patch below):
>
> Index: wscons/wsconsio.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
> retrieving revision 1.66
> diff -u -p -r1.66 wsconsio.h
> --- wscons/wsconsio.h 22 Jul 2012 18:28:36 -0000 1.66
> +++ wscons/wsconsio.h 28 Oct 2012 13:04:43 -0000
> @@ -213,6 +213,7 @@ struct wskbd_map_data {
> #define WSMOUSE_TYPE_SYNAPTICS 15 /* Synaptics touchpad */
> #define WSMOUSE_TYPE_ALPS 16 /* ALPS touchpad */
> #define WSMOUSE_TYPE_SGI 17 /* SGI serial mouse */
> +#define WSMOUSE_TYPE_ELANTECH 18 /* Elantech touchpad */
>
> /* Set resolution. Not applicable to all mouse types. */
> #define WSMOUSEIO_SRES _IOW('W', 33, u_int)
> Index: pckbc/pms.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/pckbc/pms.c,v
> retrieving revision 1.31
> diff -u -p -r1.31 pms.c
> --- pckbc/pms.c 22 Jul 2012 18:28:36 -0000 1.31
> +++ pckbc/pms.c 28 Oct 2012 14:31:19 -0000
> @@ -57,6 +57,9 @@ struct pms_protocol {
> #define PMS_INTELLI 1
> #define PMS_SYNAPTICS 2
> #define PMS_ALPS 3
> +#define PMS_ELANTECH_V1 4
> +#define PMS_ELANTECH_V2 5
> +#define PMS_ELANTECH_V3 6
> u_int packetsize;
> int (*enable)(struct pms_softc *);
> int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
> @@ -108,6 +111,25 @@ struct alps_softc {
> #define ALPS_PRESSURE 40
> };
>
> +struct elantech_softc {
> + int flags;
> +#define ELANTECH_F_REPORTS_PRESSURE 0x01
> +#define ELANTECH_F_HAS_ROCKER 0x02
> +#define ELANTECH_F_2FINGER_PACKET 0x04
> +#define ELANTECH_F_HW_V1_OLD 0x08
> +
> + int min_x, min_y;
> + int max_x, max_y;
> +
> + u_char parity[256];
> + u_char p1, p2, p3;
> +
> + /* Compat mode */
> + int wsmode;
> + int old_x, old_y;
> + u_int old_buttons;
> +};
> +
> struct pms_softc { /* driver status information */
> struct device sc_dev;
>
> @@ -129,6 +151,7 @@ struct pms_softc { /* driver status inf
> const struct pms_protocol *protocol;
> struct synaptics_softc *synaptics;
> struct alps_softc *alps;
> + struct elantech_softc *elantech;
>
> u_char packet[8];
>
> @@ -227,6 +250,18 @@ int pms_ioctl_alps(struct pms_softc *, u
> int pms_sync_alps(struct pms_softc *, int);
> void pms_proc_alps(struct pms_softc *);
>
> +int pms_enable_elantech_v1(struct pms_softc *);
> +int pms_enable_elantech_v2(struct pms_softc *);
> +int pms_enable_elantech_v3(struct pms_softc *);
> +int pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int,
> + struct proc *);
> +int pms_sync_elantech_v1(struct pms_softc *, int);
> +int pms_sync_elantech_v2(struct pms_softc *, int);
> +int pms_sync_elantech_v3(struct pms_softc *, int);
> +void pms_proc_elantech_v1(struct pms_softc *);
> +void pms_proc_elantech_v2(struct pms_softc *);
> +void pms_proc_elantech_v3(struct pms_softc *);
> +
> int synaptics_set_mode(struct pms_softc *, int);
> int synaptics_query(struct pms_softc *, int, int *);
> int synaptics_get_hwinfo(struct pms_softc *);
> @@ -235,6 +270,17 @@ void synaptics_sec_proc(struct pms_softc
> int alps_sec_proc(struct pms_softc *);
> int alps_get_hwinfo(struct pms_softc *);
>
> +int elantech_knock(struct pms_softc *);
> +void elantech_send_input(struct pms_softc *, u_int, int, int, int, int);
> +int elantech_get_hwinfo_v1(struct pms_softc *);
> +int elantech_get_hwinfo_v2(struct pms_softc *);
> +int elantech_get_hwinfo_v3(struct pms_softc *);
> +int elantech_ps2_cmd(struct pms_softc *, u_char);
> +int elantech_set_absolute_mode_v1(struct pms_softc *);
> +int elantech_set_absolute_mode_v2(struct pms_softc *);
> +int elantech_set_absolute_mode_v3(struct pms_softc *);
> +
> +
> struct cfattach pms_ca = {
> sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
> pmsactivate
> @@ -293,6 +339,35 @@ const struct pms_protocol pms_protocols[
> pms_proc_alps,
> NULL
> },
> +#ifdef notyet
> + /* Elantech touchpad (hardware version 1) */
> + {
> + PMS_ELANTECH_V1, 4,
> + pms_enable_elantech_v1,
> + pms_ioctl_elantech,
> + pms_sync_elantech_v1,
> + pms_proc_elantech_v1,
> + NULL
> + },
> + /* Elantech touchpad (hardware version 2) */
> + {
> + PMS_ELANTECH_V2, 6,
> + pms_enable_elantech_v2,
> + pms_ioctl_elantech,
> + pms_sync_elantech_v2,
> + pms_proc_elantech_v2,
> + NULL
> + },
> +#endif
> + /* Elantech touchpad (hardware version 3) */
> + {
> + PMS_ELANTECH_V3, 6,
> + pms_enable_elantech_v3,
> + pms_ioctl_elantech,
> + pms_sync_elantech_v3,
> + pms_proc_elantech_v3,
> + NULL
> + },
> };
>
> int
> @@ -1359,4 +1434,675 @@ pms_proc_alps(struct pms_softc *sc)
> alps->old_y = y;
> alps->old_buttons = buttons;
> }
> +}
> +
> +int
> +elantech_set_absolute_mode_v1(struct pms_softc *sc)
> +{
> + int i;
> + u_char resp[3];
> +
> + /* Enable absolute mode. Magic numbers from Linux driver. */
> + if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
> + pms_spec_cmd(sc, 0x10) ||
> + pms_spec_cmd(sc, 0x16) ||
> + pms_set_scaling(sc, 1) ||
> + pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
> + pms_spec_cmd(sc, 0x11) ||
> + pms_spec_cmd(sc, 0x8f) ||
> + pms_set_scaling(sc, 1))
> + return (-1);
> +
> + /* Read back reg 0x10 to ensure hardware is ready. */
> + for (i = 0; i < 5; i++) {
> + if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG) ||
> + pms_spec_cmd(sc, 0x10) ||
> + pms_get_status(sc, resp) == 0)
> + break;
> + delay(2000);
> + }
> + if (i == 5)
> + return (-1);
> +
> + if ((resp[0] & ELANTECH_ABSOLUTE_MODE) == 0)
> + return (-1);
> +
> + return (0);
> +}
> +
> +int
> +elantech_set_absolute_mode_v2(struct pms_softc *sc)
> +{
> + int i;
> + u_char resp[3];
> +
> + /* Enable absolute mode. Magic numbers from Linux driver. */
> + if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x10) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x54) ||
> + pms_set_scaling(sc, 1) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x11) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x88) ||
> + pms_set_scaling(sc, 1) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x21) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x88) ||
> + pms_set_scaling(sc, 1))
> + return (-1);
> +
> + /* Read back reg 0x10 to ensure hardware is ready. */
> + for (i = 0; i < 5; i++) {
> + if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x10) ||
> + pms_get_status(sc, resp) == 0)
> + break;
> + delay(2000);
> + }
> + if (i == 5)
> + return (-1);
> +
> + return (0);
> +}
> +
> +int
> +elantech_set_absolute_mode_v3(struct pms_softc *sc)
> +{
> + int i;
> + u_char resp[3];
> +
> + /* Enable absolute mode. Magic numbers from Linux driver. */
> + if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x10) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x0b) ||
> + pms_set_scaling(sc, 1))
> + return (-1);
> +
> + /* Read back reg 0x10 to ensure hardware is ready. */
> + for (i = 0; i < 5; i++) {
> + if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
> + elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
> + elantech_ps2_cmd(sc, 0x10) ||
> + pms_get_status(sc, resp) == 0)
> + break;
> + delay(2000);
> + }
> + if (i == 5)
> + return (-1);
> +
> + return (0);
> +}
> +
> +int
> +elantech_get_hwinfo_v1(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + int fw_version;
> + u_char capabilities[3];
> +
> + if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
> + return (-1);
> +
> + if (fw_version < 0x20030 || fw_version == 0x20600) {
> + if (fw_version < 0x20000)
> + elantech->flags |= ELANTECH_F_HW_V1_OLD;
> + } else
> + return (-1);
> +
> + if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
> + pms_get_status(sc, capabilities))
> + return (-1);
> +
> + if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER)
> + elantech->flags |= ELANTECH_F_HAS_ROCKER;
> +
> + if (elantech_set_absolute_mode_v1(sc))
> + return (-1);
> +
> + elantech->min_x = ELANTECH_V1_X_MIN;
> + elantech->max_x = ELANTECH_V1_X_MAX;
> + elantech->min_y = ELANTECH_V1_Y_MIN;
> + elantech->max_y = ELANTECH_V1_Y_MAX;
> +
> + return (0);
> +}
> +
> +int
> +elantech_get_hwinfo_v2(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + int fw_version, ic_ver;
> + u_char capabilities[3];
> + int i, fixed_dpi;
> + u_char resp[3];
> +
> + if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
> + return (-1);
> +
> + ic_ver = (fw_version & 0x0f0000) >> 16;
> + if (ic_ver != 2 && ic_ver != 4)
> + return (-1);
> +
> + if (fw_version >= 0x20800)
> + elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
> +
> + if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
> + pms_get_status(sc, capabilities))
> + return (-1);
> +
> + if (elantech_set_absolute_mode_v2(sc))
> + return (-1);
> +
> + if (fw_version == 0x20800 || fw_version == 0x20b00 ||
> + fw_version == 0x20030) {
> + elantech->max_x = ELANTECH_V2_X_MAX;
> + elantech->max_y = ELANTECH_V2_Y_MAX;
> + } else {
> + if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
> + pms_get_status(sc, resp))
> + return (-1);
> + fixed_dpi = resp[1] & 0x10;
> + i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2;
> + if ((fw_version >> 16) == 0x14 && fixed_dpi) {
> + if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) ||
> + pms_get_status(sc, resp))
> + return (-1);
> + elantech->max_x = (capabilities[1] - i) * resp[1] / 2;
> + elantech->max_y = (capabilities[2] - i) * resp[2] / 2;
> + } else if (fw_version == 0x040216) {
> + elantech->max_x = 819;
> + elantech->max_y = 405;
> + } else if (fw_version == 0x040219 || fw_version == 0x040215) {
> + elantech->max_x = 900;
> + elantech->max_y = 500;
> + } else {
> + elantech->max_x = (capabilities[1] - i) * 64;
> + elantech->max_y = (capabilities[2] - i) * 64;
> + }
> + }
> +
> + return (0);
> +}
> +
> +int
> +elantech_get_hwinfo_v3(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + int fw_version;
> + u_char resp[3];
> +
> + if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
> + return (-1);
> +
> + if (((fw_version & 0x0f0000) >> 16) != 5)
> + return (-1);
> +
> + elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
> +
> + if (elantech_set_absolute_mode_v3(sc))
> + return (-1);
> +
> + if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
> + pms_get_status(sc, resp))
> + return (-1);
> +
> + elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
> + elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
> +
> + return (0);
> +}
> +
> +int
> +elantech_ps2_cmd(struct pms_softc *sc, u_char command)
> +{
> + u_char cmd[1];
> +
> + cmd[0] = command;
> + return (pms_cmd(sc, cmd, 1, NULL, 0));
> +}
> +
> +int
> +elantech_knock(struct pms_softc *sc)
> +{
> + u_char resp[3];
> +
> + if (pms_dev_disable(sc) ||
> + pms_set_scaling(sc, 1) ||
> + pms_set_scaling(sc, 1) ||
> + pms_set_scaling(sc, 1) ||
> + pms_get_status(sc, resp) ||
> + resp[0] != PMS_ELANTECH_MAGIC1 ||
> + resp[1] != PMS_ELANTECH_MAGIC2 ||
> + (resp[2] != PMS_ELANTECH_MAGIC3_1 &&
> + resp[2] != PMS_ELANTECH_MAGIC3_2))
> + return (-1);
> +
> + return (0);
> +}
> +
> +int
> +pms_enable_elantech_v1(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + int i;
> +
> + if (elantech_knock(sc))
> + return (0);
> +
> + if (sc->elantech == NULL) {
> + sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
> + M_DEVBUF, M_WAITOK | M_ZERO);
> + if (elantech == NULL) {
> + printf("%s: elantech: not enough memory\n",
> + DEVNAME(sc));
> + goto err;
> + }
> +
> + if (elantech_get_hwinfo_v1(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 1);
> + } else if (elantech_set_absolute_mode_v1(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + for (i = 0; i < nitems(sc->elantech->parity); i++)
> + sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1;
> +
> + return (1);
> +
> +err:
> + pms_reset(sc);
> +
> + return (0);
> +}
> +
> +int
> +pms_enable_elantech_v2(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> +
> + if (elantech_knock(sc))
> + return (0);
> +
> + if (sc->elantech == NULL) {
> + sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
> + M_DEVBUF, M_WAITOK | M_ZERO);
> + if (elantech == NULL) {
> + printf("%s: elantech: not enough memory\n",
> + DEVNAME(sc));
> + goto err;
> + }
> +
> + if (elantech_get_hwinfo_v2(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 2);
> + } else if (elantech_set_absolute_mode_v2(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + return (1);
> +
> +err:
> + pms_reset(sc);
> +
> + return (0);
> +}
> +
> +int
> +pms_enable_elantech_v3(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> +
> + if (elantech_knock(sc))
> + return (0);
> +
> + if (sc->elantech == NULL) {
> + sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
> + M_DEVBUF, M_WAITOK | M_ZERO);
> + if (elantech == NULL) {
> + printf("%s: elantech: not enough memory\n",
> + DEVNAME(sc));
> + goto err;
> + }
> +
> + if (elantech_get_hwinfo_v3(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 3);
> + } else if (elantech_set_absolute_mode_v3(sc)) {
> + free(sc->elantech, M_DEVBUF);
> + sc->elantech = NULL;
> + goto err;
> + }
> +
> + return (1);
> +
> +err:
> + pms_reset(sc);
> +
> + return (0);
> +}
> +
> +int
> +pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
> + struct proc *p)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
> + int wsmode;
> +
> + switch (cmd) {
> + case WSMOUSEIO_GTYPE:
> + *(u_int *)data = WSMOUSE_TYPE_ELANTECH;
> + break;
> + case WSMOUSEIO_GCALIBCOORDS:
> + wsmc->minx = elantech->min_x;
> + wsmc->maxx = elantech->max_x;
> + wsmc->miny = elantech->min_y;
> + wsmc->maxy = elantech->max_y;
> + wsmc->swapxy = 0;
> + wsmc->resx = 0;
> + wsmc->resy = 0;
> + break;
> + case WSMOUSEIO_SETMODE:
> + wsmode = *(u_int *)data;
> + if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
> + return (EINVAL);
> + elantech->wsmode = wsmode;
> + break;
> + default:
> + return (-1);
> + }
> + return (0);
> +}
> +
> +int
> +pms_sync_elantech_v1(struct pms_softc *sc, int data)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + u_char p;
> +
> + switch (sc->inputstate) {
> + case 0:
> + if (elantech->flags & ELANTECH_F_HW_V1_OLD) {
> + elantech->p1 = (data & 0x20) >> 5;
> + elantech->p2 = (data & 0x10) >> 4;
> + } else {
> + elantech->p1 = (data & 0x10) >> 4;
> + elantech->p2 = (data & 0x20) >> 5;
> + }
> + elantech->p3 = (data & 0x04) >> 2;
> + return (0);
> + case 1:
> + p = elantech->p1;
> + break;
> + case 2:
> + p = elantech->p2;
> + break;
> + case 3:
> + p = elantech->p3;
> + break;
> + default:
> + return (-1);
> + }
> +
> + if (data < 0 || data >= nitems(elantech->parity) ||
> + elantech->parity[data] != p)
> + return (-1);
> +
> + return (0);
> +}
> +
> +int
> +pms_sync_elantech_v2(struct pms_softc *sc, int data)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> +
> + /* Variants reporting pressure always have the same constant bits. */
> + if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) {
> + if (sc->inputstate == 0 && (data & 0x0c) != 0x04)
> + return (-1);
> + if (sc->inputstate == 3 && (data & 0x0f) != 0x02)
> + return (-1);
> + return (0);
> + }
> +
> + /* For variants not reporting pressure, 1 and 3 finger touch packets
> + * have different constant bits than 2 finger touch pakets. */
> + switch (sc->inputstate) {
> + case 0:
> + if ((data & 0xc0) == 0x80) {
> + if ((data & 0x0c) != 0x0c)
> + return (-1);
> + elantech->flags |= ELANTECH_F_2FINGER_PACKET;
> + } else {
> + if ((data & 0x3c) != 0x3c)
> + return (-1);
> + elantech->flags &= ~ELANTECH_F_2FINGER_PACKET;
> + }
> + break;
^^^^^^^^
trailing tab
> + case 1:
> + case 4:
> + if (elantech->flags & ELANTECH_F_2FINGER_PACKET)
> + break;
> + if ((data & 0xf0) != 0x00)
> + return (-1);
> + break;
> + case 3:
> + if (elantech->flags & ELANTECH_F_2FINGER_PACKET) {
> + if ((data & 0x0e) != 0x08)
> + return (-1);
> + } else {
> + if ((data & 0x3e) != 0x38)
> + return (-1);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + return (0);
> +}
> +
> +int
> +pms_sync_elantech_v3(struct pms_softc *sc, int data)
> +{
> + switch (sc->inputstate) {
> + case 0:
> + if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c)
> + return (-1);
> + break;
> + case 3:
> + if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c)
> + return (-1);
> + break;
> + }
> +
> + return (0);
> +}
> +
> +void
> +pms_proc_elantech_v1(struct pms_softc *sc)
> +{
> + struct elantech_softc *elantech = sc->elantech;
> + u_int buttons;
> + int x, y, w, z;
> +
> + if (elantech->flags & ELANTECH_F_HW_V1_OLD)
> + w = ((sc->packet[1] & 0x80) >> 7) +
> + ((sc->packet[1] & 0x30) >> 4);
> + else
> + w = (sc->packet[0] & 0xc0) >> 6;
> +
> + /* Hardware version 1 doesn't report pressure. */
> + if (w) {
> + x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2];
> + y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3];
> + z = SYNAPTICS_PRESSURE;
> + } else {
> + x = elantech->old_x;
> + y = elantech->old_y;
> + z = 0;
> + }
> +
> + if (sc->packet[0] & 0x01)
> + buttons |= WSMOUSE_BUTTON(1);
> + if (sc->packet[1] & 0x02)
^^^
should be 0
> + buttons |= WSMOUSE_BUTTON(3);
> + if (elantech->flags & ELANTECH_F_HAS_ROCKER) {
> + if (sc->packet[0] & 0x40) /* up */
> + buttons |= WSMOUSE_BUTTON(4);
> + if (sc->packet[1] & 0x80) /* down */
^^^
should be 0
> + buttons |= WSMOUSE_BUTTON(5);
> + }
> +
> + elantech_send_input(sc, buttons, x, y, z, w);
> +}
> +
> +void
> +pms_proc_elantech_v2(struct pms_softc *sc)
> +{
> + const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
> + struct elantech_softc *elantech = sc->elantech;
> + u_int buttons;
> + int x, y, w, z;
> +
> + /* The hardware sends this packet when in debounce state.
> + * The packet should be ignored. */
> + if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
> + return;
> +
> + w = (sc->packet[0] & 0xc0) >> 6;
> + if (w == 1 || w == 3) {
> + x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
> + y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
> + if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
> + z = ((sc->packet[1] & 0xf0) |
> + (sc->packet[4] & 0xf0) >> 4);
^^^^^^
trailing space
> + else
> + z = SYNAPTICS_PRESSURE;
> + } else if (w == 2) {
> + x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2;
> + y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2;
> + z = SYNAPTICS_PRESSURE;
> + } else {
> + x = elantech->old_x;
> + y = elantech->old_y;
> + z = 0;
> + }
> +
> + buttons = ((sc->packet[0] & 0x01 ? WSMOUSE_BUTTON(1) : 0) |
> + ((sc->packet[0] & 0x02) ? WSMOUSE_BUTTON(3): 0));
> +
> + elantech_send_input(sc, buttons, x, y, z, w);
> +}
> +
> +void
> +pms_proc_elantech_v3(struct pms_softc *sc)
> +{
> + const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
> + struct elantech_softc *elantech = sc->elantech;
> + u_int buttons;
> + int x, y, w, z;
> +
> + /* The hardware sends this packet when in debounce state.
> + * The packet should be ignored. */
> + if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
> + return;
> +
> + buttons = ((sc->packet[0] & 0x01 ? WSMOUSE_BUTTON(1) : 0) |
> + ((sc->packet[0] & 0x02) ? WSMOUSE_BUTTON(3): 0));
> + x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
> + y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
> + z = 0;
> + w = (sc->packet[0] & 0xc0) >> 6;
> + if (w == 2) {
> + /* Two-finger touch causes two packets -- a head packet
> + * and a tail packet. We report a single event and ignore
> + * the tail packet. */
> + if ((sc->packet[0] & 0x0c) != 0x04 &&
> + (sc->packet[3] & 0xfc) != 0x02) {
> + /* not the head packet -- ignore */
^^^^^
spaces
> + return;
> + }
> + }
> +
> + /* Prevent juming cursor if pad isn't touched or reports garbage. */
> + if (w == 0 ||
> + ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y)
> + && (x != elantech->old_x || y != elantech->old_y))) {
> + x = elantech->old_x;
> + y = elantech->old_y;
> + }
> +
> + if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
> + z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4);
> + else if (w)
> + z = SYNAPTICS_PRESSURE;
> +
> + elantech_send_input(sc, buttons, x, y, z, w);
> +}
> +
> +void
> +elantech_send_input(struct pms_softc *sc, u_int buttons, int x, int y, int z,
> + int w)
> + {
> + struct elantech_softc *elantech = sc->elantech;
> + int dx, dy;
> +
> + if (elantech->wsmode == WSMOUSE_NATIVE) {
> + wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
> + WSMOUSE_INPUT_ABSOLUTE_X |
> + WSMOUSE_INPUT_ABSOLUTE_Y |
> + WSMOUSE_INPUT_ABSOLUTE_Z |
> + WSMOUSE_INPUT_ABSOLUTE_W |
> + WSMOUSE_INPUT_SYNC);
> + } else {
> + dx = dy = 0;
> +
> + if ((elantech->flags & ELANTECH_F_REPORTS_PRESSURE) &&
> + z > SYNAPTICS_PRESSURE) {
> + dx = x - elantech->old_x;
> + dy = y - elantech->old_y;
> + dx /= SYNAPTICS_SCALE;
> + dy /= SYNAPTICS_SCALE;
> + }
> + if (dx || dy || buttons != elantech->old_buttons)
> + wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
> + WSMOUSE_INPUT_DELTA);
> + elantech->old_buttons = buttons;
> + }
> +
> + elantech->old_x = x;
> + elantech->old_y = y;
> }
> Index: pckbc/pmsreg.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/pckbc/pmsreg.h,v
> retrieving revision 1.8
> diff -u -p -r1.8 pmsreg.h
> --- pckbc/pmsreg.h 28 Apr 2012 09:43:24 -0000 1.8
> +++ pckbc/pmsreg.h 28 Oct 2012 12:24:11 -0000
> @@ -43,6 +43,11 @@
> #define PMS_ALPS_MAGIC3_2 80
> #define PMS_ALPS_MAGIC3_3 100
>
> +#define PMS_ELANTECH_MAGIC1 0x3c
> +#define PMS_ELANTECH_MAGIC2 0x03
> +#define PMS_ELANTECH_MAGIC3_1 0xc8
> +#define PMS_ELANTECH_MAGIC3_2 0x00
> +
> /*
> * Checking for almost-standard PS/2 packet
> * Note: ALPS devices never signal overflow condition
> @@ -151,5 +156,37 @@
> #define ALPS_YSEC_BEZEL 512
>
> #define ALPS_Z_MAGIC 127
> +
> +/* Elantech queries */
> +#define ELANTECH_QUE_FW_ID 0
> +#define ELANTECH_QUE_FW_VER 1
> +#define ELANTECH_QUE_CAPABILITIES 2
> +#define ELANTECH_QUE_SAMPLE 3
> +#define ELANTECH_QUE_RESOLUTION 4
> +
> +/* Elantech capabilities */
> +#define ELANTECH_CAP_HAS_ROCKER 4
> +
> +#define ELANTECH_PS2_CUSTOM_COMMAND 0xf8
> +
> +#define ELANTECH_CMD_READ_REG 0x10
> +#define ELANTECH_CMD_WRITE_REG 0x11
> +#define ELANTECH_CMD_READ_WRITE_REG 0x00
> +
> +#define ELANTECH_ABSOLUTE_MODE 0x04
> +
> +/* Hardware version 1 has hard-coded axis range values.
> + * X axis range is 0 to 576, Y axis range is 0 to 384.
> + * Edge offset accounts for bezel around the touchpad. */
> +#define ELANTECH_V1_EDGE_OFFSET 32
> +#define ELANTECH_V1_X_MIN (0 + ELANTECH_V1_EDGE_OFFSET)
> +#define ELANTECH_V1_X_MAX (576 - ELANTECH_V1_EDGE_OFFSET)
> +#define ELANTECH_V1_Y_MIN (0 + ELANTECH_V1_EDGE_OFFSET)
> +#define ELANTECH_V1_Y_MAX (384 - ELANTECH_V1_EDGE_OFFSET)
> +
> +/* Older hardware version 2 variants lack ID query capability. */
> +#define ELANTECH_V2_X_MAX 1152
> +#define ELANTECH_V2_Y_MAX 768
> +
>
> #endif /* SYS_DEV_PCKBC_PMSREG_H */
>
>
> X driver patch:
>
> Index: xf86-input-synaptics/src/wsconscomm.c
> ===================================================================
> RCS file: /cvs/xenocara/driver/xf86-input-synaptics/src/wsconscomm.c,v
> retrieving revision 1.6
> diff -u -p -r1.6 wsconscomm.c
> --- xf86-input-synaptics/src/wsconscomm.c 22 Jul 2012 18:33:01 -0000
> 1.6
> +++ xf86-input-synaptics/src/wsconscomm.c 28 Oct 2012 14:07:37 -0000
> @@ -61,7 +61,8 @@ WSConsIsTouchpad(InputInfoPtr pInfo, con
> }
>
> if (wsmouse_type == WSMOUSE_TYPE_SYNAPTICS ||
> - wsmouse_type == WSMOUSE_TYPE_ALPS)
> + wsmouse_type == WSMOUSE_TYPE_ALPS ||
> + wsmouse_type == WSMOUSE_TYPE_ELANTECH)
> rc = TRUE;
>
> out:
> @@ -194,6 +195,13 @@ WSConsReadHwState(InputInfoPtr pInfo,
> hw->z = event.value;
> break;
> case WSCONS_EVENT_MOUSE_ABSOLUTE_W:
> + if (priv->model == MODEL_ELANTECH) {
> + /* Elantech touchpads report number of fingers directly. */
> + hw->fingerWidth = 5;
> + hw->numFingers = event.value;
> + break;
> + }
> + /* XXX magic number mapping which is mirrored in pms driver */
> switch (event.value) {
> case 0:
> hw->fingerWidth = 5;
> @@ -291,6 +299,12 @@ WSConsReadDevDimensions(InputInfoPtr pIn
> priv->has_width = FALSE;
> priv->has_double = FALSE;
> priv->has_triple = FALSE;
> + break;
> + case WSMOUSE_TYPE_ELANTECH:
> + priv->model = MODEL_ELANTECH;
> + priv->has_width = TRUE;
> + priv->has_double = TRUE;
> + priv->has_triple = TRUE;
> break;
> }
> }
--
Alexandr Shadchin