summaryrefslogtreecommitdiffstats
path: root/sys/dev/atkbdc/psm.c
diff options
context:
space:
mode:
authordumbbell <dumbbell@FreeBSD.org>2008-10-14 17:48:36 +0000
committerdumbbell <dumbbell@FreeBSD.org>2008-10-14 17:48:36 +0000
commite84c6ce51e6065481420e61a5c1bd521fa973c64 (patch)
treee29efaf44f1a7eea9af10d75604479db401d4343 /sys/dev/atkbdc/psm.c
parent183bdc8e412a5455dbf90bf8e5fc7d62e9ad19d4 (diff)
downloadFreeBSD-src-e84c6ce51e6065481420e61a5c1bd521fa973c64.zip
FreeBSD-src-e84c6ce51e6065481420e61a5c1bd521fa973c64.tar.gz
Rewrite Synaptics touchpads support with the following goals in mind:
o better quality of the movement smoothing o more features such as tap-hold and virtual scrolling Support must still be enabled with this line in your /boot/loader.conf: hw.psm.synaptics_support="1" The following sysctls were removed: hw.psm.synaptics.low_speed_threshold hw.psm.synaptics.min_movement hw.psm.synaptics.squelch_level An overview of this new driver and a short documentation about the added sysctls is available on the wiki: http://wiki.freebsd.org/SynapticsTouchpad
Diffstat (limited to 'sys/dev/atkbdc/psm.c')
-rw-r--r--sys/dev/atkbdc/psm.c1157
1 files changed, 982 insertions, 175 deletions
diff --git a/sys/dev/atkbdc/psm.c b/sys/dev/atkbdc/psm.c
index c173496..2b1117f 100644
--- a/sys/dev/atkbdc/psm.c
+++ b/sys/dev/atkbdc/psm.c
@@ -163,15 +163,101 @@ 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
+};
+
typedef struct synapticsinfo {
- struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
- int directional_scrolls;
- int low_speed_threshold;
- int min_movement;
- int squelch_level;
+ int directional_scrolls;
+ int min_pressure;
+ int max_pressure;
+ int max_width;
+ int margin_top;
+ int margin_right;
+ int margin_bottom;
+ int margin_left;
+ int na_top;
+ int na_right;
+ int na_bottom;
+ int na_left;
+ int window_min;
+ int window_max;
+ int multiplicator;
+ int weight_current;
+ int weight_previous;
+ int weight_previous_na;
+ int weight_len_squared;
+ int div_min;
+ int div_max;
+ int div_max_na;
+ int div_len;
+ int tap_max_delta;
+ int tap_min_queue;
+ int taphold_timeout;
+ int vscroll_ver_area;
+ int vscroll_hor_area;
+ int vscroll_min_delta;
+ int vscroll_div_min;
+ int vscroll_div_max;
} synapticsinfo_t;
+typedef struct synapticspacket {
+ int x;
+ int y;
+} synapticspacket_t;
+
+#define SYNAPTICS_PACKETQUEUE 10
+#define SYNAPTICS_QUEUE_CURSOR(x) \
+ (x + SYNAPTICS_PACKETQUEUE) % SYNAPTICS_PACKETQUEUE
+
+typedef struct synapticsaction {
+ 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 fingers_nb;
+ int tap_button;
+ int in_taphold;
+ int in_vscroll;
+} synapticsaction_t;
+
/* driver control block */
struct psm_softc { /* Driver status information */
int unit;
@@ -185,6 +271,7 @@ struct psm_softc { /* Driver status information */
mousehw_t hw; /* hardware information */
synapticshw_t synhw; /* Synaptics hardware information */
synapticsinfo_t syninfo; /* Synaptics configuration */
+ synapticsaction_t synaction; /* Synaptics action context */
mousemode_t mode; /* operation mode */
mousemode_t dflt_mode; /* default operation mode */
mousestatus_t status; /* accumulated mouse movement */
@@ -781,6 +868,35 @@ doopen(struct psm_softc *sc, int command_byte)
{
int stat[3];
+ /*
+ * FIXME: Synaptics TouchPad seems to go back to Relative Mode with
+ * no obvious reason. Thus we check the current mode and restore the
+ * Absolute Mode if it was cleared.
+ *
+ * The previous hack at the end of psmprobe() wasn't efficient when
+ * moused(8) was restarted.
+ *
+ * A Reset (FF) or Set Defaults (F6) command would clear the
+ * Absolute Mode bit. But a verbose boot or debug.psm.loglevel=5
+ * doesn't show any evidence of such a command.
+ */
+ if (sc->hw.model == MOUSE_MODEL_SYNAPTICS) {
+ mouse_ext_command(sc->kbdc, 1);
+ get_mouse_status(sc->kbdc, stat, 0, 3);
+ if (stat[1] == 0x47 && stat[2] == 0x40) {
+ /* Set the mode byte -- request wmode where
+ * available */
+ if (sc->synhw.capExtended)
+ mouse_ext_command(sc->kbdc, 0xc1);
+ else
+ mouse_ext_command(sc->kbdc, 0xc0);
+ set_mouse_sampling_rate(sc->kbdc, 20);
+ VLOG(5, (LOG_DEBUG, "psm%d: Synaptis Absolute Mode "
+ "hopefully restored\n",
+ sc->unit));
+ }
+ }
+
/* enable the mouse device */
if (!enable_aux_dev(sc->kbdc)) {
/* MOUSE ERROR: failed to enable the mouse because:
@@ -1272,15 +1388,6 @@ psmprobe(device_t dev)
endprobe(ENXIO);
}
- /*
- * Synaptics TouchPad seems to go back to Relative Mode after
- * the previous set_controller_command_byte() call; by issueing
- * a Read Mode Byte command, the touchpad is in Absolute Mode
- * again.
- */
- if (sc->hw.model == MOUSE_MODEL_SYNAPTICS)
- mouse_ext_command(sc->kbdc, 1);
-
/* done */
kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
kbdc_lock(sc->kbdc, FALSE);
@@ -2398,7 +2505,7 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
{
static int touchpad_buttons;
static int guest_buttons;
- int w, x0, y0, xavg, yavg, xsensitivity, ysensitivity, sensitivity = 0;
+ int w, x0, y0;
/* TouchPad PS/2 absolute mode message format
*
@@ -2418,7 +2525,7 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
* U: up button
* W: "wrist" value
* X: x position
- * Y: x position
+ * Y: y position
* Z: pressure
*
* Absolute reportable limits: 0 - 6143.
@@ -2438,22 +2545,44 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
(pb->ipacket[3] & 0xc8) != 0xc0)
return (-1);
- *x = *y = x0 = y0 = 0;
+ *x = *y = 0;
- /* Pressure value. */
+ /*
+ * Pressure value.
+ * Interpretation:
+ * z = 0 No finger contact
+ * z = 10 Finger hovering near the pad
+ * z = 30 Very light finger contact
+ * z = 80 Normal finger contact
+ * z = 110 Very heavy finger contact
+ * z = 200 Finger lying flat on pad surface
+ * z = 255 Maximum reportable Z
+ */
*z = pb->ipacket[2];
- /* Finger width value */
+ /*
+ * Finger width value
+ * Interpretation:
+ * w = 0 Two finger on the pad (capMultiFinger needed)
+ * w = 1 Three or more fingers (capMultiFinger needed)
+ * w = 2 Pen (instead of finger) (capPen needed)
+ * w = 3 Reserved (passthrough?)
+ * w = 4-7 Finger of normal width (capPalmDetect needed)
+ * w = 8-14 Very wide finger or palm (capPalmDetect needed)
+ * w = 15 Maximum reportable width (capPalmDetect needed)
+ */
+ /* XXX Is checking capExtended enough? */
if (sc->synhw.capExtended)
w = ((pb->ipacket[0] & 0x30) >> 2) |
((pb->ipacket[0] & 0x04) >> 1) |
((pb->ipacket[3] & 0x04) >> 2);
else {
- /* Assume a finger of regular width */
+ /* Assume a finger of regular width. */
w = 4;
}
/* Handle packets from the guest device */
+ /* XXX Documentation? */
if (w == 3 && sc->synhw.capPassthrough) {
*x = ((pb->ipacket[1] & 0x10) ?
pb->ipacket[4] - 256 : pb->ipacket[4]);
@@ -2470,7 +2599,7 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
guest_buttons |= MOUSE_BUTTON3DOWN;
ms->button = touchpad_buttons | guest_buttons;
- return (0);
+ goto SYNAPTICS_END;
}
/* Button presses */
@@ -2492,6 +2621,7 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
* the packet indicates that we have an extended
* button press.
*/
+ /* XXX Documentation? */
if (pb->ipacket[3] & 0x02) {
/*
* if directional_scrolls is not 1, we treat any of
@@ -2507,151 +2637,495 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
if (pb->ipacket[5] & 0x02)
touchpad_buttons |= MOUSE_BUTTON7DOWN;
} else {
- if ((pb->ipacket[4] & 0x0F) || (pb->ipacket[5] & 0x0F))
+ if ((pb->ipacket[4] & 0x0F) ||
+ (pb->ipacket[5] & 0x0F))
touchpad_buttons |= MOUSE_BUTTON2DOWN;
}
}
ms->button = touchpad_buttons | guest_buttons;
- /* There is a finger on the pad. */
- if ((w >= 4 && w <= 7) && (*z >= 16 && *z < 200)) {
+ /* 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;
+ 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 len, weight_prev_x, weight_prev_y;
+ int div_max_x, div_max_y, div_x, div_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;
+
+ /* Palm detection. */
+ if (!(
+ (sc->synhw.capMultiFinger && (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;
+ }
+
+ /* Read current absolute position. */
x0 = ((pb->ipacket[3] & 0x10) << 8) |
- ((pb->ipacket[1] & 0x0f) << 8) | pb->ipacket[4];
+ ((pb->ipacket[1] & 0x0f) << 8) |
+ pb->ipacket[4];
y0 = ((pb->ipacket[3] & 0x20) << 7) |
- ((pb->ipacket[1] & 0xf0) << 4) | pb->ipacket[5];
+ ((pb->ipacket[1] & 0xf0) << 4) |
+ pb->ipacket[5];
- if (sc->flags & PSM_FLAGS_FINGERDOWN) {
- *x = x0 - sc->xold;
- *y = y0 - sc->yold;
+ synaction = &(sc->synaction);
- /*
- * we compute averages of x and y
- * movement
- */
- if (sc->xaverage == 0)
- sc->xaverage = *x;
+ /*
+ * If the action is just beginning, init the structure and
+ * compute tap timeout.
+ */
+ if (!(sc->flags & PSM_FLAGS_FINGERDOWN)) {
+ VLOG(3, (LOG_DEBUG, "synaptics: ----\n"));
- if (sc->yaverage == 0)
- sc->yaverage = *y;
+ /* Store the first point of this action. */
+ synaction->start_x = x0;
+ synaction->start_y = y0;
+ dx = dy = 0;
- xavg = sc->xaverage;
- yavg = sc->yaverage;
+ /* Initialize queue. */
+ synaction->queue_cursor = SYNAPTICS_PACKETQUEUE;
+ synaction->queue_len = 0;
+ synaction->window_min = window_min;
- sc->xaverage = (xavg + *x) >> 1;
- sc->yaverage = (yavg + *y) >> 1;
+ /* Reset average. */
+ synaction->avg_dx = 0;
+ synaction->avg_dy = 0;
- /*
- * then use the averages to compute
- * a sensitivity level in each dimension
- */
- xsensitivity = (sc->xaverage - xavg);
- if (xsensitivity < 0)
- xsensitivity = -xsensitivity;
+ /* Reset squelch. */
+ synaction->squelch_x = 0;
+ synaction->squelch_y = 0;
+
+ /* Reset pressure peak. */
+ sc->zmax = 0;
+
+ /* Reset fingers count. */
+ synaction->fingers_nb = 0;
- ysensitivity = (sc->yaverage - yavg);
- if (ysensitivity < 0)
- ysensitivity = -ysensitivity;
+ /* Reset virtual scrolling state. */
+ synaction->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);
+
+ sc->flags |= PSM_FLAGS_FINGERDOWN;
+ } else {
+ /* Calculate the current delta. */
+ cursor = synaction->queue_cursor;
+ dx = x0 - synaction->queue[cursor].x;
+ dy = y0 - synaction->queue[cursor].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;
+
+ VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n",
+ x0, y0, *z, w));
+
+ /* 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));
+
+ /*
+ * 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;
+
+ /* Is a scrolling action occuring? */
+ if (!synaction->in_taphold && !synaction->in_vscroll) {
/*
- * The sensitivity level is higher the faster
- * the finger is moving. It also tends to be
- * higher in the middle of a touchpad motion
- * than on either end
- * Note - sensitivity gets to 0 when moving slowly -
- * so we add 1 to it to give it a meaningful value
- * in that case.
+ * A scrolling action must not conflict with a tap
+ * action. Here are the conditions to consider a
+ * scrolling action:
+ * - the action in a configurable area
+ * - one of the following:
+ * . the distance between the last packet and the
+ * first should be above a configurable minimum
+ * . tap timed out
*/
- sensitivity = (xsensitivity & ysensitivity) + 1;
+ dxp = abs(synaction->queue[synaction->queue_cursor].x -
+ synaction->start_x);
+ dyp = abs(synaction->queue[synaction->queue_cursor].y -
+ synaction->start_y);
+
+ if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, >) ||
+ dxp >= sc->syninfo.vscroll_min_delta ||
+ dyp >= sc->syninfo.vscroll_min_delta) {
+ /* Check for horizontal scrolling. */
+ if ((vscroll_hor_area > 0 &&
+ synaction->start_y <= vscroll_hor_area) ||
+ (vscroll_hor_area < 0 &&
+ synaction->start_y >=
+ 6143 + vscroll_hor_area))
+ synaction->in_vscroll += 2;
+
+ /* Check for vertical scrolling. */
+ if ((vscroll_ver_area > 0 &&
+ synaction->start_x <= vscroll_ver_area) ||
+ (vscroll_ver_area < 0 &&
+ synaction->start_x >=
+ 6143 + vscroll_ver_area))
+ synaction->in_vscroll += 1;
+
+ /* Avoid conflicts if area overlaps. */
+ if (synaction->in_vscroll == 3)
+ synaction->in_vscroll =
+ (dxp > dyp) ? 2 : 1;
+ }
+ VLOG(5, (LOG_DEBUG,
+ "synaptics: virtual scrolling: %s "
+ "(direction=%d, dxp=%d, dyp=%d)\n",
+ synaction->in_vscroll ? "YES" : "NO",
+ synaction->in_vscroll, dxp, dyp));
+ }
+
+ 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;
+ div_max_x = div_max_y = sc->syninfo.vscroll_div_max;
+ } else {
/*
- * If either our x or y change is greater than
- * our hi/low speed threshold - we do the high-speed
- * absolute to relative calculation otherwise
- * we do the low-speed calculation.
+ * There's a lot of noise in coordinates when
+ * the finger is on the touchpad's borders. When
+ * using this area, we apply a special weight and
+ * div.
*/
- if ((*x > sc->syninfo.low_speed_threshold ||
- *x < -sc->syninfo.low_speed_threshold) ||
- (*y > sc->syninfo.low_speed_threshold ||
- *y < -sc->syninfo.low_speed_threshold)) {
- x0 = (x0 + sc->xold * 3) / 4;
- y0 = (y0 + sc->yold * 3) / 4;
- *x = (x0 - sc->xold) * 10 / 85;
- *y = (y0 - sc->yold) * 10 / 85;
- } else {
- /*
- * This is the low speed calculation.
- * We simply check to see if our movement is
- * more than our minimum movement threshold
- * and if it is - set the movement to 1
- * in the correct direction.
- * NOTE - Normally this would result
- * in pointer movement that was WAY too fast.
- * This works due to the movement squelch
- * we do later.
- */
- if (*x < -sc->syninfo.min_movement)
- *x = -1;
- else if (*x > sc->syninfo.min_movement)
- *x = 1;
- else
- *x = 0;
- if (*y < -sc->syninfo.min_movement)
- *y = -1;
- else if (*y > sc->syninfo.min_movement)
- *y = 1;
- else
- *y = 0;
+ if (x0 <= na_left || x0 >= 6143 - na_right) {
+ weight_prev_x = sc->syninfo.weight_previous_na;
+ div_max_x = sc->syninfo.div_max_na;
+ }
+ if (y0 <= na_bottom || y0 >= 6143 - na_top) {
+ weight_prev_y = sc->syninfo.weight_previous_na;
+ div_max_y = sc->syninfo.div_max_na;
}
- } else
- sc->flags |= PSM_FLAGS_FINGERDOWN;
+ }
/*
- * The squelch process. Take our sensitivity value and
- * add it to the current squelch value - if squelch is
- * less than our squelch threshold we kill the movement,
- * otherwise we reset squelch and pass the movement through.
- * Since squelch is cumulative - when mouse movement is slow
- * (around sensitivity 1) the net result is that only 1
- * out of every squelch_level packets is delivered,
- * effectively slowing down the movement.
+ * Calculate weights for the average operands and
+ * the divisor. Both depend on the distance between
+ * the current packet and a previous one (based on the
+ * window width).
*/
- sc->squelch += sensitivity;
- if (sc->squelch < sc->syninfo.squelch_level) {
- *x = 0;
- *y = 0;
- } else
- sc->squelch = 0;
+ window = imin(synaction->queue_len, window_max);
+ peer = SYNAPTICS_QUEUE_CURSOR(cursor + window - 1);
+ dxp = abs(x0 - synaction->queue[peer].x) + 1;
+ dyp = abs(y0 - synaction->queue[peer].y) + 1;
+ len = (dxp * dxp) + (dyp * dyp);
+ weight_prev_x = imin(weight_prev_x,
+ weight_len_squared * weight_prev_x / len);
+ weight_prev_y = imin(weight_prev_y,
+ weight_len_squared * weight_prev_y / len);
+
+ len = (dxp + dyp) / 2;
+ div_x = div_len * div_max_x / len;
+ div_x = imin(div_max_x, div_x);
+ div_x = imax(div_min, div_x);
+ div_y = div_len * div_max_y / len;
+ div_y = imin(div_max_y, div_y);
+ div_y = imax(div_min, div_y);
+
+ VLOG(3, (LOG_DEBUG,
+ "synaptics: peer=%d, len=%d, weight=%d/%d, div=%d/%d\n",
+ peer, len, weight_prev_x, weight_prev_y, div_x, div_y));
+
+ /* Compute averages. */
+ synaction->avg_dx =
+ (weight_current * dx * multiplicator +
+ weight_prev_x * synaction->avg_dx) /
+ (weight_current + weight_prev_x);
+
+ synaction->avg_dy =
+ (weight_current * dy * multiplicator +
+ weight_prev_y * synaction->avg_dy) /
+ (weight_current + weight_prev_y);
+
+ VLOG(5, (LOG_DEBUG,
+ "synaptics: avg_dx~=%d, avg_dy~=%d\n",
+ synaction->avg_dx / multiplicator,
+ synaction->avg_dy / multiplicator));
+
+ /* Use these averages to calculate x & y. */
+ synaction->squelch_x += synaction->avg_dx;
+ *x = synaction->squelch_x / (div_x * multiplicator);
+ synaction->squelch_x = synaction->squelch_x %
+ (div_x * multiplicator);
+
+ synaction->squelch_y += synaction->avg_dy;
+ *y = synaction->squelch_y / (div_y * multiplicator);
+ synaction->squelch_y = synaction->squelch_y %
+ (div_y * multiplicator);
+
+ if (synaction->in_vscroll) {
+ switch(synaction->in_vscroll) {
+ case 1: /* Vertical scrolling. */
+ if (*y != 0)
+ ms->button |= (*y > 0) ?
+ MOUSE_BUTTON4DOWN :
+ MOUSE_BUTTON5DOWN;
+ break;
+ case 2: /* Horizontal scrolling. */
+ if (*x != 0)
+ ms->button |= (*x > 0) ?
+ MOUSE_BUTTON7DOWN :
+ MOUSE_BUTTON6DOWN;
+ break;
+ }
+
+ /* The pointer is not moved. */
+ *x = *y = 0;
+ } else {
+ VLOG(3, (LOG_DEBUG, "synaptics: [%d, %d] -> [%d, %d]\n",
+ dx, dy, *x, *y));
+ }
+ } else if (sc->flags & PSM_FLAGS_FINGERDOWN) {
+ /*
+ * An action is currently taking place but the pressure
+ * dropped under the minimum, putting an end to it.
+ */
+ synapticsaction_t *synaction;
+ int taphold_timeout, dx, dy, tap_max_delta;
+
+ synaction = &(sc->synaction);
+ dx = abs(synaction->queue[synaction->queue_cursor].x -
+ synaction->start_x);
+ dy = abs(synaction->queue[synaction->queue_cursor].y -
+ synaction->start_y);
+
+ /* Max delta is disabled for multi-fingers tap. */
+ if (synaction->fingers_nb > 1)
+ tap_max_delta = imax(dx, dy);
+ else
+ tap_max_delta = sc->syninfo.tap_max_delta;
- sc->xold = x0;
- sc->yold = y0;
- sc->zmax = imax(*z, sc->zmax);
- } else {
sc->flags &= ~PSM_FLAGS_FINGERDOWN;
- if (sc->zmax > tap_threshold &&
- timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
- if (w == 0)
- ms->button |= MOUSE_BUTTON3DOWN;
- else if (w == 1)
- ms->button |= MOUSE_BUTTON2DOWN;
- else
- ms->button |= MOUSE_BUTTON1DOWN;
- }
+ /* Check for tap. */
+ VLOG(3, (LOG_DEBUG,
+ "synaptics: zmax=%d, dx=%d, dy=%d, "
+ "delta=%d, fingers=%d, queue=%d\n",
+ sc->zmax, dx, dy, tap_max_delta, synaction->fingers_nb,
+ synaction->queue_len));
+ if (!synaction->in_vscroll && sc->zmax >= tap_threshold &&
+ timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=) &&
+ dx <= tap_max_delta && dy <= tap_max_delta &&
+ synaction->queue_len >= sc->syninfo.tap_min_queue) {
+ /*
+ * We have a tap if:
+ * - the maximum pressure went over tap_threshold
+ * - the action ended before tap_timeout
+ *
+ * To handle tap-hold, we must delay any button push to
+ * the next action.
+ */
+ if (synaction->in_taphold) {
+ /*
+ * This is the second and last tap of a
+ * double tap action, not a tap-hold.
+ */
+ synaction->in_taphold = 0;
- sc->zmax = 0;
- sc->taptimeout.tv_sec = tap_timeout / 1000000;
- sc->taptimeout.tv_usec = tap_timeout % 1000000;
- timevaladd(&sc->taptimeout, &sc->lastsoftintr);
+ /*
+ * For double-tap to work:
+ * - no button press is emitted (to
+ * simulate a button release)
+ * - PSM_FLAGS_FINGERDOWN is set to
+ * force the next packet to emit a
+ * button press)
+ */
+ VLOG(2, (LOG_DEBUG,
+ "synaptics: button RELEASE: %d\n",
+ synaction->tap_button));
+ sc->flags |= PSM_FLAGS_FINGERDOWN;
+ } else {
+ /*
+ * This is the first tap: we set the
+ * tap-hold state and notify the button
+ * down event.
+ */
+ synaction->in_taphold = 1;
+ taphold_timeout = sc->syninfo.taphold_timeout;
+ sc->taptimeout.tv_sec = taphold_timeout /
+ 1000000;
+ sc->taptimeout.tv_usec = taphold_timeout %
+ 1000000;
+ timevaladd(&sc->taptimeout, &sc->lastsoftintr);
+
+ switch (synaction->fingers_nb) {
+ case 3:
+ synaction->tap_button =
+ MOUSE_BUTTON2DOWN;
+ break;
+ case 2:
+ synaction->tap_button =
+ MOUSE_BUTTON3DOWN;
+ break;
+ default:
+ synaction->tap_button =
+ MOUSE_BUTTON1DOWN;
+ }
+ VLOG(2, (LOG_DEBUG,
+ "synaptics: button PRESS: %d\n",
+ synaction->tap_button));
+ ms->button |= synaction->tap_button;
+ }
+ } else {
+ /*
+ * Not enough pressure or timeout: reset
+ * tap-hold state.
+ */
+ if (synaction->in_taphold) {
+ VLOG(2, (LOG_DEBUG,
+ "synaptics: button RELEASE: %d\n",
+ synaction->tap_button));
+ synaction->in_taphold = 0;
+ } else {
+ VLOG(2, (LOG_DEBUG,
+ "synaptics: not a tap-hold\n"));
+ }
+ }
+ } else if (!(sc->flags & PSM_FLAGS_FINGERDOWN) &&
+ sc->synaction.in_taphold) {
+ /*
+ * For a tap-hold to work, the button must remain down at
+ * least until timeout (where the in_taphold flags will be
+ * cleared) or during the next action.
+ */
+ if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
+ ms->button |= sc->synaction.tap_button;
+ } else {
+ VLOG(2, (LOG_DEBUG,
+ "synaptics: button RELEASE: %d\n",
+ sc->synaction.tap_button));
+ sc->synaction.in_taphold = 0;
+ }
}
- /* Use the extra buttons as a scrollwheel */
- if (ms->button & MOUSE_BUTTON4DOWN)
+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)
+ ms->button &= ~MOUSE_BUTTON4DOWN;
+ } else if (ms->button & MOUSE_BUTTON5DOWN) {
*z = 1;
- else
+ ms->button &= ~MOUSE_BUTTON5DOWN;
+ } else if (ms->button & MOUSE_BUTTON6DOWN) {
+ *z = -2;
+ ms->button &= ~MOUSE_BUTTON6DOWN;
+ } else if (ms->button & MOUSE_BUTTON7DOWN) {
+ *z = 2;
+ ms->button &= ~MOUSE_BUTTON7DOWN;
+ } else
*z = 0;
return (0);
@@ -3400,13 +3874,87 @@ enable_4dplus(struct psm_softc *sc)
/* Synaptics Touchpad */
static int
-enable_synaptics(struct psm_softc *sc)
+synaptics_sysctl(SYSCTL_HANDLER_ARGS)
{
- int status[3];
- KBDC kbdc;
+ int error, arg;
- if (!synaptics_support)
- return (FALSE);
+ /* Read the current value. */
+ arg = *(int *)oidp->oid_arg1;
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ /* Sanity check. */
+ if (error || !req->newptr)
+ return (error);
+
+ /*
+ * Check that the new value is in the concerned node's range
+ * of values.
+ */
+ switch (oidp->oid_arg2) {
+ case SYNAPTICS_SYSCTL_MIN_PRESSURE:
+ case SYNAPTICS_SYSCTL_MAX_PRESSURE:
+ if (arg < 0 || arg > 255)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_MAX_WIDTH:
+ if (arg < 4 || arg > 15)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_MARGIN_TOP:
+ case SYNAPTICS_SYSCTL_MARGIN_RIGHT:
+ case SYNAPTICS_SYSCTL_MARGIN_BOTTOM:
+ case SYNAPTICS_SYSCTL_MARGIN_LEFT:
+ case SYNAPTICS_SYSCTL_NA_TOP:
+ case SYNAPTICS_SYSCTL_NA_RIGHT:
+ case SYNAPTICS_SYSCTL_NA_BOTTOM:
+ case SYNAPTICS_SYSCTL_NA_LEFT:
+ if (arg < 0 || arg > 6143)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_WINDOW_MIN:
+ case SYNAPTICS_SYSCTL_WINDOW_MAX:
+ case SYNAPTICS_SYSCTL_TAP_MIN_QUEUE:
+ if (arg < 1 || arg > SYNAPTICS_PACKETQUEUE)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_MULTIPLICATOR:
+ case SYNAPTICS_SYSCTL_WEIGHT_CURRENT:
+ case SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS:
+ case SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA:
+ case SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED:
+ case SYNAPTICS_SYSCTL_DIV_MIN:
+ case SYNAPTICS_SYSCTL_DIV_MAX:
+ case SYNAPTICS_SYSCTL_DIV_MAX_NA:
+ case SYNAPTICS_SYSCTL_DIV_LEN:
+ case SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN:
+ case SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX:
+ if (arg < 1)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_TAP_MAX_DELTA:
+ case SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT:
+ case SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA:
+ if (arg < 0)
+ return (EINVAL);
+ break;
+ case SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA:
+ case SYNAPTICS_SYSCTL_VSCROLL_VER_AREA:
+ if (arg < -6143 || arg > 6143)
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* Update. */
+ *(int *)oidp->oid_arg1 = arg;
+
+ return (error);
+}
+
+static void
+synaptics_sysctl_create_tree(struct psm_softc *sc)
+{
/* Attach extra synaptics sysctl nodes under hw.psm.synaptics */
sysctl_ctx_init(&sc->syninfo.sysctl_ctx);
@@ -3414,54 +3962,307 @@ enable_synaptics(struct psm_softc *sc)
SYSCTL_STATIC_CHILDREN(_hw_psm), OID_AUTO, "synaptics", CTLFLAG_RD,
0, "Synaptics TouchPad");
- /*
- * synaptics_directional_scrolls - if non-zero, the directional
- * pad scrolls, otherwise it registers as a middle-click.
- */
+ /* hw.psm.synaptics.directional_scrolls. */
sc->syninfo.directional_scrolls = 1;
SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
- "directional_scrolls", CTLFLAG_RW,
+ "directional_scrolls", CTLFLAG_RW|CTLFLAG_ANYBODY,
&sc->syninfo.directional_scrolls, 0,
- "directional pad scrolls (1=yes 0=3rd button)");
+ "Enable hardware scrolling pad (if non-zero) or register it as "
+ "a middle-click (if 0)");
- /*
- * Synaptics_low_speed_threshold - the number of touchpad units
- * below-which we go into low-speed tracking mode.
- */
- sc->syninfo.low_speed_threshold = 20;
- SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
+ /* hw.psm.synaptics.min_pressure. */
+ sc->syninfo.min_pressure = 16;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
- "low_speed_threshold", CTLFLAG_RW,
- &sc->syninfo.low_speed_threshold, 0,
- "threshold between low and hi speed positioning");
-
- /*
- * Synaptics_min_movement - the number of touchpad units below
- * which we ignore altogether.
- */
- sc->syninfo.min_movement = 2;
- SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
+ "min_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.min_pressure, SYNAPTICS_SYSCTL_MIN_PRESSURE,
+ synaptics_sysctl, "I",
+ "Minimum pressure required to start an action");
+
+ /* hw.psm.synaptics.max_pressure. */
+ sc->syninfo.max_pressure = 220;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
- "min_movement", CTLFLAG_RW,
- &sc->syninfo.min_movement, 0,
- "ignore touchpad movements less than this");
-
- /*
- * Synaptics_squelch_level - level at which we squelch movement
- * packets.
- *
- * This effectively sends 1 out of every synaptics_squelch_level
- * packets when * running in low-speed mode.
- */
- sc->syninfo.squelch_level=3;
- SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
+ "max_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.max_pressure, SYNAPTICS_SYSCTL_MAX_PRESSURE,
+ synaptics_sysctl, "I",
+ "Maximum pressure to detect palm");
+
+ /* hw.psm.synaptics.max_width. */
+ sc->syninfo.max_width = 10;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "max_width", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.max_width, SYNAPTICS_SYSCTL_MAX_WIDTH,
+ synaptics_sysctl, "I",
+ "Maximum finger width to detect palm");
+
+ /* hw.psm.synaptics.top_margin. */
+ sc->syninfo.margin_top = 200;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "margin_top", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.margin_top, SYNAPTICS_SYSCTL_MARGIN_TOP,
+ synaptics_sysctl, "I",
+ "Top margin");
+
+ /* hw.psm.synaptics.right_margin. */
+ sc->syninfo.margin_right = 200;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "margin_right", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.margin_right, SYNAPTICS_SYSCTL_MARGIN_RIGHT,
+ synaptics_sysctl, "I",
+ "Right margin");
+
+ /* hw.psm.synaptics.bottom_margin. */
+ sc->syninfo.margin_bottom = 200;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "margin_bottom", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.margin_bottom, SYNAPTICS_SYSCTL_MARGIN_BOTTOM,
+ synaptics_sysctl, "I",
+ "Bottom margin");
+
+ /* hw.psm.synaptics.left_margin. */
+ sc->syninfo.margin_left = 200;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "margin_left", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.margin_left, SYNAPTICS_SYSCTL_MARGIN_LEFT,
+ synaptics_sysctl, "I",
+ "Left margin");
+
+ /* hw.psm.synaptics.na_top. */
+ sc->syninfo.na_top = 1783;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "na_top", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.na_top, SYNAPTICS_SYSCTL_NA_TOP,
+ synaptics_sysctl, "I",
+ "Top noisy area, where weight_previous_na is used instead "
+ "of weight_previous");
+
+ /* hw.psm.synaptics.na_right. */
+ sc->syninfo.na_right = 563;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "na_right", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.na_right, SYNAPTICS_SYSCTL_NA_RIGHT,
+ synaptics_sysctl, "I",
+ "Right noisy area, where weight_previous_na is used instead "
+ "of weight_previous");
+
+ /* hw.psm.synaptics.na_bottom. */
+ sc->syninfo.na_bottom = 1408;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "na_bottom", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.na_bottom, SYNAPTICS_SYSCTL_NA_BOTTOM,
+ synaptics_sysctl, "I",
+ "Bottom noisy area, where weight_previous_na is used instead "
+ "of weight_previous");
+
+ /* hw.psm.synaptics.na_left. */
+ sc->syninfo.na_left = 1600;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "na_left", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.na_left, SYNAPTICS_SYSCTL_NA_LEFT,
+ synaptics_sysctl, "I",
+ "Left noisy area, where weight_previous_na is used instead "
+ "of weight_previous");
+
+ /* hw.psm.synaptics.window_min. */
+ sc->syninfo.window_min = 4;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "window_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.window_min, SYNAPTICS_SYSCTL_WINDOW_MIN,
+ synaptics_sysctl, "I",
+ "Minimum window size to start an action");
+
+ /* hw.psm.synaptics.window_max. */
+ sc->syninfo.window_max = 10;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "window_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.window_max, SYNAPTICS_SYSCTL_WINDOW_MAX,
+ synaptics_sysctl, "I",
+ "Maximum window size");
+
+ /* hw.psm.synaptics.multiplicator. */
+ sc->syninfo.multiplicator = 10000;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "multiplicator", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.multiplicator, SYNAPTICS_SYSCTL_MULTIPLICATOR,
+ synaptics_sysctl, "I",
+ "Multiplicator to increase precision in averages and divisions");
+
+ /* hw.psm.synaptics.weight_current. */
+ sc->syninfo.weight_current = 3;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "weight_current", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.weight_current, SYNAPTICS_SYSCTL_WEIGHT_CURRENT,
+ synaptics_sysctl, "I",
+ "Weight of the current movement in the new average");
+
+ /* hw.psm.synaptics.weight_previous. */
+ sc->syninfo.weight_previous = 6;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "weight_previous", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.weight_previous, SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS,
+ synaptics_sysctl, "I",
+ "Weight of the previous average");
+
+ /* hw.psm.synaptics.weight_previous_na. */
+ sc->syninfo.weight_previous_na = 20;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
- "squelch_level", CTLFLAG_RW,
- &sc->syninfo.squelch_level, 0,
- "squelch level for synaptics touchpads");
+ "weight_previous_na", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.weight_previous_na,
+ SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA,
+ synaptics_sysctl, "I",
+ "Weight of the previous average (inside the noisy area)");
+
+ /* hw.psm.synaptics.weight_len_squared. */
+ sc->syninfo.weight_len_squared = 2000;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "weight_len_squared", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.weight_len_squared,
+ SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED,
+ synaptics_sysctl, "I",
+ "Length (squared) of segments where weight_previous "
+ "starts to decrease");
+
+ /* hw.psm.synaptics.div_min. */
+ sc->syninfo.div_min = 9;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "div_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.div_min, SYNAPTICS_SYSCTL_DIV_MIN,
+ synaptics_sysctl, "I",
+ "Divisor for fast movements");
+
+ /* hw.psm.synaptics.div_max. */
+ sc->syninfo.div_max = 17;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "div_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.div_max, SYNAPTICS_SYSCTL_DIV_MAX,
+ synaptics_sysctl, "I",
+ "Divisor for slow movements");
+
+ /* hw.psm.synaptics.div_max_na. */
+ sc->syninfo.div_max_na = 30;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "div_max_na", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.div_max_na, SYNAPTICS_SYSCTL_DIV_MAX_NA,
+ synaptics_sysctl, "I",
+ "Divisor with slow movements (inside the noisy area)");
+
+ /* hw.psm.synaptics.div_len. */
+ sc->syninfo.div_len = 100;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "div_len", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.div_len, SYNAPTICS_SYSCTL_DIV_LEN,
+ synaptics_sysctl, "I",
+ "Length of segments where div_max starts to decrease");
+
+ /* hw.psm.synaptics.tap_max_delta. */
+ sc->syninfo.tap_max_delta = 80;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "tap_max_delta", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.tap_max_delta, SYNAPTICS_SYSCTL_TAP_MAX_DELTA,
+ synaptics_sysctl, "I",
+ "Length of segments above which a tap is ignored");
+
+ /* hw.psm.synaptics.tap_min_queue. */
+ sc->syninfo.tap_min_queue = 2;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "tap_min_queue", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.tap_min_queue, SYNAPTICS_SYSCTL_TAP_MIN_QUEUE,
+ synaptics_sysctl, "I",
+ "Number of packets required to consider a tap");
+
+ /* hw.psm.synaptics.taphold_timeout. */
+ sc->synaction.in_taphold = 0;
+ sc->syninfo.taphold_timeout = tap_timeout;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "taphold_timeout", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.taphold_timeout, SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT,
+ synaptics_sysctl, "I",
+ "Maximum elapsed time between two taps to consider a tap-hold "
+ "action");
+
+ /* hw.psm.synaptics.vscroll_hor_area. */
+ sc->syninfo.vscroll_hor_area = 0; /* 1300 */
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "vscroll_hor_area", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.vscroll_hor_area, SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA,
+ synaptics_sysctl, "I",
+ "Area reserved for horizontal virtual scrolling");
+
+ /* hw.psm.synaptics.vscroll_ver_area. */
+ sc->syninfo.vscroll_ver_area = -600;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "vscroll_ver_area", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.vscroll_ver_area, SYNAPTICS_SYSCTL_VSCROLL_VER_AREA,
+ synaptics_sysctl, "I",
+ "Area reserved for vertical virtual scrolling");
+
+ /* hw.psm.synaptics.vscroll_min_delta. */
+ sc->syninfo.vscroll_min_delta = 50;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "vscroll_min_delta", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.vscroll_min_delta,
+ SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA,
+ synaptics_sysctl, "I",
+ "Minimum movement to consider virtual scrolling");
+
+ /* hw.psm.synaptics.vscroll_div_min. */
+ sc->syninfo.vscroll_div_min = 100;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "vscroll_div_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.vscroll_div_min, SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN,
+ synaptics_sysctl, "I",
+ "Divisor for fast scrolling");
+
+ /* hw.psm.synaptics.vscroll_div_min. */
+ sc->syninfo.vscroll_div_max = 150;
+ SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
+ SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
+ "vscroll_div_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
+ &sc->syninfo.vscroll_div_max, SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX,
+ synaptics_sysctl, "I",
+ "Divisor for slow scrolling");
+}
+
+static int
+enable_synaptics(struct psm_softc *sc)
+{
+ int status[3];
+ KBDC kbdc;
+
+ if (!synaptics_support)
+ return (FALSE);
kbdc = sc->kbdc;
+ VLOG(3, (LOG_DEBUG, "synaptics: BEGIN init\n"));
disable_aux_dev(kbdc);
sc->hw.buttons = 3;
sc->squelch = 0;
@@ -3607,6 +4408,12 @@ enable_synaptics(struct psm_softc *sc)
if (sc->synhw.capExtended && sc->synhw.capFourButtons)
sc->hw.buttons = 4;
+ VLOG(3, (LOG_DEBUG, "synaptics: END init (%d buttons)\n",
+ sc->hw.buttons));
+
+ /* Create sysctl tree. */
+ synaptics_sysctl_create_tree(sc);
+
return (TRUE);
}
OpenPOWER on IntegriCloud