summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2011-10-04 06:30:40 +0000
committermpi <mpi@openbsd.org>2011-10-04 06:30:40 +0000
commit7327fb1ee728ab2bd9ff55ac3725a4fa4eaddbde (patch)
treeaad1f196c0776494a87581efbbeb2f30f04d00fc
parentAdd a 'flag' locator to gpioiic(4), and define a first flag (diff)
downloadwireguard-openbsd-7327fb1ee728ab2bd9ff55ac3725a4fa4eaddbde.tar.xz
wireguard-openbsd-7327fb1ee728ab2bd9ff55ac3725a4fa4eaddbde.zip
Add support for ALPS touchpads.
This also fix the "slow touchpad" problem due to the fact that some ALPS devices respond to some Synaptics messages and stay in un half-configured state. Tested by Rivo Nurges, armani@, shadchin@ and matthieu@ ok shadchin@
-rw-r--r--sys/dev/pckbc/pms.c307
-rw-r--r--sys/dev/pckbc/pmsreg.h16
2 files changed, 320 insertions, 3 deletions
diff --git a/sys/dev/pckbc/pms.c b/sys/dev/pckbc/pms.c
index 8991ed2c4ec..10a5b7a54ba 100644
--- a/sys/dev/pckbc/pms.c
+++ b/sys/dev/pckbc/pms.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pms.c,v 1.21 2011/08/24 15:34:25 shadchin Exp $ */
+/* $OpenBSD: pms.c,v 1.22 2011/10/04 06:30:40 mpi Exp $ */
/* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
/*-
@@ -39,6 +39,12 @@
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
+#ifdef DEBUG
+#define DPRINTF(x...) do { printf(x); } while (0);
+#else
+#define DPRINTF(x...)
+#endif
+
#define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
#define WSMOUSE_BUTTON(x) (1 << ((x) - 1))
@@ -50,6 +56,7 @@ struct pms_protocol {
#define PMS_STANDARD 0
#define PMS_INTELLI 1
#define PMS_SYNAPTICS 2
+#define PMS_ALPS 3
u_int packetsize;
int (*enable)(struct pms_softc *);
int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
@@ -78,6 +85,21 @@ struct synaptics_softc {
#define SYNAPTICS_PRESSURE 30
};
+struct alps_softc {
+ int model;
+ int version;
+
+ int min_x, min_y;
+ int max_x, max_y;
+ int old_fin;
+
+ /* Compat mode */
+ int wsmode;
+ int old_x, old_y;
+ u_int old_buttons;
+#define ALPS_PRESSURE 40
+};
+
struct pms_softc { /* driver status information */
struct device sc_dev;
@@ -98,6 +120,7 @@ struct pms_softc { /* driver status information */
const struct pms_protocol *protocol;
struct synaptics_softc *synaptics;
+ struct alps_softc *alps;
u_char packet[8];
@@ -116,6 +139,35 @@ static const u_int butmap[8] = {
WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
};
+static const struct alps_model {
+ int version;
+ int model;
+} alps_models[] = {
+ { 0x2021, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x2221, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x2222, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x3222, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x5212, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x5321, ALPS_GLIDEPOINT },
+ { 0x5322, ALPS_GLIDEPOINT },
+ { 0x603b, ALPS_GLIDEPOINT },
+ { 0x6222, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x6321, ALPS_GLIDEPOINT },
+ { 0x6322, ALPS_GLIDEPOINT },
+ { 0x6323, ALPS_GLIDEPOINT },
+ { 0x6324, ALPS_GLIDEPOINT },
+ { 0x6325, ALPS_GLIDEPOINT },
+ { 0x6326, ALPS_GLIDEPOINT },
+ { 0x633b, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
+ { 0x7301, ALPS_DUALPOINT },
+ { 0x7321, ALPS_GLIDEPOINT },
+ { 0x7322, ALPS_GLIDEPOINT },
+ { 0x7325, ALPS_GLIDEPOINT },
+#if 0
+ { 0x7326, 0 }, /* XXX Uses unknown v3 protocol */
+#endif
+};
+
int pmsprobe(struct device *, void *, void *);
void pmsattach(struct device *, struct device *, void *);
int pmsactivate(struct device *, int);
@@ -150,6 +202,11 @@ int pms_sync_synaptics(struct pms_softc *, int);
void pms_proc_synaptics(struct pms_softc *);
void pms_disable_synaptics(struct pms_softc *);
+int pms_enable_alps(struct pms_softc *);
+int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *);
+int pms_sync_alps(struct pms_softc *, int);
+void pms_proc_alps(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 *);
@@ -160,6 +217,8 @@ int synaptics_pt_ioctl(void *, u_long, caddr_t, int, struct proc *);
int synaptics_pt_enable(void *);
void synaptics_pt_disable(void *);
+int alps_get_hwinfo(struct pms_softc *);
+
struct cfattach pms_ca = {
sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
pmsactivate
@@ -208,7 +267,16 @@ const struct pms_protocol pms_protocols[] = {
pms_sync_synaptics,
pms_proc_synaptics,
pms_disable_synaptics
- }
+ },
+ /* ALPS touchpad */
+ {
+ PMS_ALPS, 6,
+ pms_enable_alps,
+ pms_ioctl_alps,
+ pms_sync_alps,
+ pms_proc_alps,
+ NULL
+ },
};
int
@@ -483,6 +551,8 @@ pmsattach(struct device *parent, struct device *self, void *aux)
sc->protocol = &pms_protocols[i];
}
+ DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
+
/* no interrupts until enabled */
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
}
@@ -966,3 +1036,236 @@ pms_disable_synaptics(struct pms_softc *sc)
synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
SYNAPTICS_DISABLE_GESTURE);
}
+
+int
+alps_get_hwinfo(struct pms_softc *sc)
+{
+ struct alps_softc *alps = sc->alps;
+ u_char resp[3];
+ int i;
+
+ if (pms_set_resolution(sc, 0) ||
+ pms_set_scaling(sc, 2) ||
+ pms_set_scaling(sc, 2) ||
+ pms_set_scaling(sc, 2) ||
+ pms_get_status(sc, resp)) {
+ DPRINTF("%s: alps: model query error\n", DEVNAME(sc));
+ return (-1);
+ }
+
+ alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);
+
+ for (i = 0; i < nitems(alps_models); i++)
+ if (alps->version == alps_models[i].version) {
+ alps->model = alps_models[i].model;
+ return (0);
+ }
+
+ return (-1);
+
+}
+
+int
+pms_enable_alps(struct pms_softc *sc)
+{
+ struct alps_softc *alps = sc->alps;
+ u_char resp[3];
+
+ if (pms_set_resolution(sc, 0) ||
+ pms_set_scaling(sc, 1) ||
+ pms_set_scaling(sc, 1) ||
+ pms_set_scaling(sc, 1) ||
+ pms_get_status(sc, resp) ||
+ resp[0] != PMS_ALPS_MAGIC1 ||
+ resp[1] != PMS_ALPS_MAGIC2 ||
+ (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2))
+ return (0);
+
+ if (sc->alps == NULL) {
+ sc->alps = alps = malloc(sizeof(struct alps_softc),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ if (alps == NULL) {
+ printf("%s: alps: not enough memory\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (alps_get_hwinfo(sc))
+ goto err;
+
+ printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc),
+ (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
+ alps->version);
+
+ alps->min_x = ALPS_XMIN_BEZEL;
+ alps->min_y = ALPS_YMIN_BEZEL;
+ alps->max_x = ALPS_XMAX_BEZEL;
+ alps->max_y = ALPS_YMAX_BEZEL;
+
+ alps->wsmode = WSMOUSE_COMPAT;
+ }
+
+ if (alps->model == 0)
+ goto err;
+
+ if ((alps->model & ALPS_PASSTHROUGH) &&
+ (pms_set_scaling(sc, 2) ||
+ pms_set_scaling(sc, 2) ||
+ pms_set_scaling(sc, 2) ||
+ pms_dev_disable(sc))) {
+ DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (pms_dev_disable(sc) ||
+ pms_dev_disable(sc) ||
+ pms_set_rate(sc, 0x0a)) {
+ DPRINTF("%s: alps: tapping error\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (pms_dev_disable(sc) ||
+ pms_dev_disable(sc) ||
+ pms_dev_disable(sc) ||
+ pms_dev_disable(sc) ||
+ pms_dev_enable(sc)) {
+ DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if ((alps->model & ALPS_PASSTHROUGH) &&
+ (pms_set_scaling(sc, 1) ||
+ pms_set_scaling(sc, 1) ||
+ pms_set_scaling(sc, 1) ||
+ pms_dev_disable(sc))) {
+ DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc));
+ goto err;
+ }
+
+ return (1);
+
+err:
+ pms_reset(sc);
+
+ return (0);
+}
+
+int
+pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
+ struct proc *p)
+{
+ struct alps_softc *alps = sc->alps;
+ struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
+ int wsmode;
+
+ switch (cmd) {
+ case WSMOUSEIO_GTYPE:
+ *(u_int *)data = WSMOUSE_TYPE_ALPS;
+ break;
+ case WSMOUSEIO_GCALIBCOORDS:
+ wsmc->minx = alps->min_x;
+ wsmc->maxx = alps->max_x;
+ wsmc->miny = alps->min_y;
+ wsmc->maxy = alps->max_y;
+ wsmc->swapxy = 0;
+ break;
+ case WSMOUSEIO_SETMODE:
+ wsmode = *(u_int *)data;
+ if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
+ return (EINVAL);
+ alps->wsmode = wsmode;
+ break;
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+int
+pms_sync_alps(struct pms_softc *sc, int data)
+{
+ switch (sc->inputstate) {
+ case 0:
+ if ((data & 0xf8) != 0xf8) /* XXX model dependant? */
+ return (-1);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ if ((data & 0x80) != 0)
+ return (-1);
+ break;
+ }
+
+ return (0);
+}
+
+void
+pms_proc_alps(struct pms_softc *sc)
+{
+ struct alps_softc *alps = sc->alps;
+ int x, y, z, dx, dy;
+ u_int buttons;
+ int fin, ges;
+
+ x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4);
+ y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3);
+ z = sc->packet[5];
+
+ /*
+ * XXX The Y-axis is in the oposit direction compared to
+ * Synaptics touchpads and PS/2 mouses.
+ * It's why we need to translate the y value here for both
+ * NATIVE and COMPAT modes.
+ */
+ y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
+
+ buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) |
+ ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) |
+ ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0);
+
+ if (alps->wsmode == WSMOUSE_NATIVE) {
+ if (z == 127) {
+ /* DualPoint touchpads are not absolute. */
+ wsmouse_input(sc->sc_wsmousedev, buttons, x, y, 0, 0,
+ WSMOUSE_INPUT_DELTA);
+ return;
+ }
+
+ ges = sc->packet[2] & 0x01;
+ fin = sc->packet[2] & 0x02;
+
+ /* Simulate click (tap) */
+ if (ges && !fin)
+ z = 35;
+
+ /* Generate a null pressure event (needed for tap & drag) */
+ if (ges && fin && !alps->old_fin)
+ z = 0;
+
+ wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, 0,
+ WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
+ WSMOUSE_INPUT_ABSOLUTE_Z);
+
+ alps->old_fin = fin;
+ } else {
+ dx = dy = 0;
+ if (z > ALPS_PRESSURE) {
+ dx = x - alps->old_x;
+ dy = y - alps->old_y;
+
+ /* Prevent jump */
+ dx = abs(dx) > 50 ? 0 : dx;
+ dy = abs(dy) > 50 ? 0 : dy;
+ }
+
+ if (dx || dy || buttons != alps->old_buttons)
+ wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
+ WSMOUSE_INPUT_DELTA);
+
+ alps->old_x = x;
+ alps->old_y = y;
+ alps->old_buttons = buttons;
+ }
+}
diff --git a/sys/dev/pckbc/pmsreg.h b/sys/dev/pckbc/pmsreg.h
index 1bf2f7da0ec..c2ce5567ac8 100644
--- a/sys/dev/pckbc/pmsreg.h
+++ b/sys/dev/pckbc/pmsreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pmsreg.h,v 1.4 2011/08/24 15:34:25 shadchin Exp $ */
+/* $OpenBSD: pmsreg.h,v 1.5 2011/10/04 06:30:40 mpi Exp $ */
/* $NetBSD: psmreg.h,v 1.1 1998/03/22 15:41:28 drochner Exp $ */
#ifndef SYS_DEV_PCKBC_PMSREG_H
@@ -37,6 +37,11 @@
#define PMS_INTELLI_MAGIC3 80
#define PMS_INTELLI_ID 0x03
+#define PMS_ALPS_MAGIC1 0
+#define PMS_ALPS_MAGIC2 0
+#define PMS_ALPS_MAGIC3_1 10
+#define PMS_ALPS_MAGIC3_2 100
+
/* Synaptics queries */
#define SYNAPTICS_QUE_IDENTIFY 0x00
#define SYNAPTICS_QUE_MODES 0x01
@@ -88,6 +93,10 @@
#define SYNAPTICS_MODEL_SIMPLC (1 << 5)
#define SYNAPTICS_MODEL_GEOMETRY(m) ((m) & 0x0f)
+#define ALPS_GLIDEPOINT (1 << 1)
+#define ALPS_DUALPOINT (1 << 2)
+#define ALPS_PASSTHROUGH (1 << 3)
+
/* Resolutions */
#define SYNAPTICS_RESOLUTION_X(r) (((r) >> 16) & 0xff)
#define SYNAPTICS_RESOLUTION_Y(r) ((r) & 0xff)
@@ -121,4 +130,9 @@
#define SYNAPTICS_YMIN_BEZEL 1408
#define SYNAPTICS_YMAX_BEZEL 4448
+#define ALPS_XMIN_BEZEL 130
+#define ALPS_XMAX_BEZEL 840
+#define ALPS_YMIN_BEZEL 130
+#define ALPS_YMAX_BEZEL 640
+
#endif /* SYS_DEV_PCKBC_PMSREG_H */