Index: sys/dev/atkbdc/psm.c =================================================================== --- sys/dev/atkbdc/psm.c (revision 183643) +++ sys/dev/atkbdc/psm.c (working copy) @@ -163,15 +163,99 @@ #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_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 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; + double avg_dx; + double avg_dy; + double squelch_x; + double 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 +269,7 @@ 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 +866,35 @@ { 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 +1386,6 @@ 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 +2503,7 @@ { 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 +2523,7 @@ * U: up button * W: "wrist" value * X: x position - * Y: x position + * Y: y position * Z: pressure * * Absolute reportable limits: 0 - 6143. @@ -2438,22 +2543,44 @@ (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 +2597,7 @@ guest_buttons |= MOUSE_BUTTON3DOWN; ms->button = touchpad_buttons | guest_buttons; - return (0); + goto SYNAPTICS_END; } /* Button presses */ @@ -2492,6 +2619,7 @@ * 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 +2635,503 @@ 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 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; + 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); + /* + * 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")); + + /* 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; + + /* Reset pressure peak. */ + sc->zmax = 0; + + /* Reset fingers count. */ + synaction->fingers_nb = 0; + + /* 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. + */ + +#if 0 + /* We ignore packets inside margins. */ + if (x0 <= margin_left || x0 >= 6143 - margin_right || + y0 <= margin_bottom || y0 >= 6143 - margin_top) { + /* XXX Should we do this after the queueing? */ + VLOG(3, (LOG_DEBUG, + "synaptics: [%d, %d] outside bounds\n", x0, y0)); + goto SYNAPTICS_END; + } +#endif + + /* + * 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) { /* - * we compute averages of x and y - * movement + * 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 */ - if (sc->xaverage == 0) - sc->xaverage = *x; + dxp = abs(synaction->queue[synaction->queue_cursor].x - + synaction->start_x); + dyp = abs(synaction->queue[synaction->queue_cursor].y - + synaction->start_y); - if (sc->yaverage == 0) - sc->yaverage = *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; - xavg = sc->xaverage; - yavg = sc->yaverage; + /* 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; - sc->xaverage = (xavg + *x) >> 1; - sc->yaverage = (yavg + *y) >> 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 { /* - * then use the averages to compute - * a sensitivity level in each dimension + * 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. */ - xsensitivity = (sc->xaverage - xavg); - if (xsensitivity < 0) - xsensitivity = -xsensitivity; + if (x0 <= na_left || x0 >= 6143 - na_right) { + weight_prev_x = sc->syninfo.weight_previous_na; + div_max_x = sc->syninfo.div_max_na; + } - ysensitivity = (sc->yaverage - yavg); - if (ysensitivity < 0) - ysensitivity = -ysensitivity; + if (y0 <= na_bottom || y0 >= 6143 - na_top) { + weight_prev_y = sc->syninfo.weight_previous_na; + div_max_y = sc->syninfo.div_max_na; + } + } + /* + * 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). + */ + 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 + + weight_prev_x * synaction->avg_dx) / + (weight_current + weight_prev_x); + + synaction->avg_dy = + (weight_current * dy + + weight_prev_y * synaction->avg_dy) / + (weight_current + weight_prev_y); + + VLOG(5, (LOG_DEBUG, + "synaptics: avg_dx~=%d, avg_dy~=%d\n", + (int)synaction->avg_dx, (int)synaction->avg_dy)); + + /* Use these averages to calculate x & y. */ + synaction->squelch_x += synaction->avg_dx; + *x = (int)synaction->squelch_x / div_x; + synaction->squelch_x -= (int)synaction->squelch_x / + div_x * div_x; + + synaction->squelch_y += synaction->avg_dy; + *y = (int)synaction->squelch_y / div_y; + synaction->squelch_y -= (int)synaction->squelch_y / + div_y * div_y; + + 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->flags &= ~PSM_FLAGS_FINGERDOWN; + + /* 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) { /* - * 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. + * 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. */ - sensitivity = (xsensitivity & ysensitivity) + 1; + if (synaction->in_taphold) { + /* + * This is the second and last tap of a + * double tap action, not a tap-hold. + */ + synaction->in_taphold = 0; - /* - * 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. - */ - 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; + /* + * 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 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. + * This is the first tap: we set the + * tap-hold state and notify the button + * down event. */ - 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; + 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 - sc->flags |= PSM_FLAGS_FINGERDOWN; - + } 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) { /* - * 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. + * 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. */ - sc->squelch += sensitivity; - if (sc->squelch < sc->syninfo.squelch_level) { - *x = 0; - *y = 0; - } else - sc->squelch = 0; - - 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; + 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; } - - sc->zmax = 0; - sc->taptimeout.tv_sec = tap_timeout / 1000000; - sc->taptimeout.tv_usec = tap_timeout % 1000000; - timevaladd(&sc->taptimeout, &sc->lastsoftintr); } - /* 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,68 +3880,386 @@ /* 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_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); sc->syninfo.sysctl_tree = SYSCTL_ADD_NODE(&sc->syninfo.sysctl_ctx, 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"); + "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"); - /* - * 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, + /* 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"); + "max_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY, + &sc->syninfo.max_pressure, SYNAPTICS_SYSCTL_MAX_PRESSURE, + synaptics_sysctl, "I", + "Maximum pressure to detect palm"); - /* - * 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, + /* 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, - "squelch_level", CTLFLAG_RW, - &sc->syninfo.squelch_level, 0, - "squelch level for synaptics touchpads"); + "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.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, + "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 (squared) 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 the segment 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 +4405,12 @@ 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); }