Index: sys/dev/atkbdc/atkbd.c =================================================================== --- sys/dev/atkbdc/atkbd.c +++ sys/dev/atkbdc/atkbd.c @@ -161,7 +161,6 @@ { atkbd_state_t *state; keyboard_t *kbd; - int s; /* * The original text of the following comments are extracted @@ -188,19 +187,9 @@ * * The keyboard apparently unwedges the irq in most cases. */ - s = spltty(); kbd = (keyboard_t *)arg; - if (kbdd_lock(kbd, TRUE)) { - /* - * We have seen the lock flag is not set. Let's reset - * the flag early, otherwise the LED update routine fails - * which may want the lock during the interrupt routine. - */ - kbdd_lock(kbd, FALSE); - if (kbdd_check_char(kbd)) - kbdd_intr(kbd, NULL); - } - splx(s); + if (kbdd_check_char(kbd)) + kbdd_intr(kbd, NULL); state = (atkbd_state_t *)kbd->kb_data; callout_reset(&state->ks_timer, hz / 10, atkbd_timeout, arg); } @@ -572,16 +561,15 @@ atkbd_test_if(keyboard_t *kbd) { int error; - int s; error = 0; + kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc); empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); - s = spltty(); if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) error = EIO; else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) error = EIO; - splx(s); + kbdc_unlock(((atkbd_state_t *)kbd->kb_data)->kbdc); return error; } @@ -619,10 +607,12 @@ { int c; + kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc); if (wait) c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); + kbdc_unlock(((atkbd_state_t *)kbd->kb_data)->kbdc); if (c != -1) ++kbd->kb_count; return (KBD_IS_ACTIVE(kbd) ? c : -1); @@ -658,15 +648,19 @@ } /* see if there is something in the keyboard port */ + kbdc_lock(state->kbdc); if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); - if (scancode == -1) + if (scancode == -1) { + kbdc_unlock(state->kbdc); return NOKEY; + } } + kbdc_unlock(state->kbdc); ++kbd->kb_count; #if KBDIO_DEBUG >= 10 @@ -1086,7 +1080,7 @@ static int atkbd_lock(keyboard_t *kbd, int lock) { - return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); + return (1); } /* clear the internal state of the keyboard */ @@ -1163,17 +1157,21 @@ * disabling the interrupts doesn't cause real problems but the * responsiveness is a bit better when they are turned off. */ + kbdc_lock(kbdc); send_kbd_command(kbdc, KBDC_DISABLE_KBD); set_controller_command_byte(kbdc, KBD_AUX_CONTROL_BITS | KBD_KBD_CONTROL_BITS | KBD_TRANSLATION, KBD_DISABLE_AUX_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_KBD_PORT); send_kbd_command(kbdc, KBDC_ENABLE_KBD); + kbdc_unlock(kbdc); #endif } static int atkbd_reset(KBDC kbdc, int flags, int c) { + kbdc_lock_assert(kbdc); + /* reset keyboard hardware */ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { /* @@ -1225,6 +1223,8 @@ static int setup_kbd_port(KBDC kbdc, int port, int intr) { + kbdc_lock_assert(kbdc); + if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) @@ -1236,6 +1236,8 @@ static int get_kbd_echo(KBDC kbdc) { + kbdc_lock_assert(kbdc); + /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) /* CONTROLLER ERROR: there is very little we can do... */ @@ -1273,10 +1275,7 @@ int c; int m; - if (!kbdc_lock(kbdc, TRUE)) { - /* driver error? */ - return ENXIO; - } + kbdc_lock(kbdc); /* temporarily block data transmission from the keyboard */ write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT); @@ -1290,7 +1289,7 @@ if (c == -1) { /* CONTROLLER ERROR */ kbdc_set_device_mask(kbdc, m); - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return ENXIO; } @@ -1327,7 +1326,7 @@ } #endif - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return (HAS_QUIRK(kbdc, KBDC_QUIRK_IGNORE_PROBE_RESULT) ? 0 : err); } @@ -1338,10 +1337,7 @@ int id; int c; - if (!kbdc_lock(kbdc, TRUE)) { - /* driver error? */ - return EIO; - } + kbdc_lock(kbdc); /* temporarily block data transmission from the keyboard */ write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT); @@ -1351,7 +1347,7 @@ c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); printf("atkbd: unable to get the current command byte value.\n"); return EIO; } @@ -1367,13 +1363,13 @@ if (setup_kbd_port(kbdc, TRUE, FALSE)) { /* CONTROLLER ERROR: there is very little we can do... */ printf("atkbd: unable to set the command byte.\n"); - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return EIO; } if (HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) && atkbd_reset(kbdc, flags, c)) { - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return EIO; } @@ -1423,7 +1419,7 @@ if (!HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) && atkbd_reset(kbdc, flags, c)) { - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return EIO; } @@ -1445,7 +1441,7 @@ */ set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c); - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; } @@ -1483,26 +1479,20 @@ set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc) ? 0xff : (KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK), c); - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); printf("atkbd: unable to enable the keyboard port and intr.\n"); return EIO; } - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return 0; } static int write_kbd(KBDC kbdc, int command, int data) { - int s; - /* prevent the timeout routine from polling the keyboard */ - if (!kbdc_lock(kbdc, TRUE)) - return EBUSY; - - /* disable the keyboard and mouse interrupt */ - s = spltty(); + kbdc_lock(kbdc); #if 0 c = get_controller_command_byte(kbdc); if ((c == -1) @@ -1511,18 +1501,9 @@ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ - kbdc_lock(kbdc, FALSE); - splx(s); + kbdc_unlock(kbdc); return EIO; } - /* - * Now that the keyboard controller is told not to generate - * the keyboard and mouse interrupts, call `splx()' to allow - * the other tty interrupts. The clock interrupt may also occur, - * but the timeout routine (`scrn_timer()') will be blocked - * by the lock flag set via `kbdc_lock()' - */ - splx(s); #endif if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) send_kbd_command(kbdc, KBDC_ENABLE_KBD); @@ -1532,10 +1513,8 @@ c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } -#else - splx(s); #endif - kbdc_lock(kbdc, FALSE); + kbdc_unlock(kbdc); return 0; } @@ -1545,6 +1524,8 @@ { int id1, id2; + kbdc_lock_assert(kbdc); + empty_both_buffers(kbdc, 10); id1 = id2 = -1; if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) Index: sys/dev/atkbdc/atkbdc.c =================================================================== --- sys/dev/atkbdc/atkbdc.c +++ sys/dev/atkbdc/atkbdc.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #if defined(__amd64__) #include @@ -291,7 +293,7 @@ if (sc->ioh0 == 0) { /* XXX */ sc->command_byte = -1; sc->command_mask = 0; - sc->lock = FALSE; + mtx_init(&sc->lock, "atkbdc lock", NULL, MTX_DEF); sc->kbd.head = sc->kbd.tail = 0; sc->aux.head = sc->aux.tail = 0; #if KBDIO_DEBUG >= 2 @@ -349,56 +351,6 @@ return NULL; } -/* - * I/O access arbitration in `kbdio' - * - * The `kbdio' module uses a simplistic convention to arbitrate - * I/O access to the controller/keyboard/mouse. The convention requires - * close cooperation of the calling device driver. - * - * The device drivers which utilize the `kbdio' module are assumed to - * have the following set of routines. - * a. An interrupt handler (the bottom half of the driver). - * b. Timeout routines which may briefly poll the keyboard controller. - * c. Routines outside interrupt context (the top half of the driver). - * They should follow the rules below: - * 1. The interrupt handler may assume that it always has full access - * to the controller/keyboard/mouse. - * 2. The other routines must issue `spltty()' if they wish to - * prevent the interrupt handler from accessing - * the controller/keyboard/mouse. - * 3. The timeout routines and the top half routines of the device driver - * arbitrate I/O access by observing the lock flag in `kbdio'. - * The flag is manipulated via `kbdc_lock()'; when one wants to - * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if - * the call returns with TRUE. Otherwise the caller must back off. - * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion - * is finished. This mechanism does not prevent the interrupt - * handler from being invoked at any time and carrying out I/O. - * Therefore, `spltty()' must be strategically placed in the device - * driver code. Also note that the timeout routine may interrupt - * `kbdc_lock()' called by the top half of the driver, but this - * interruption is OK so long as the timeout routine observes - * rule 4 below. - * 4. The interrupt and timeout routines should not extend I/O operation - * across more than one interrupt or timeout; they must complete any - * necessary I/O operation within one invocation of the routine. - * This means that if the timeout routine acquires the lock flag, - * it must reset the flag to FALSE before it returns. - */ - -/* set/reset polling lock */ -int -kbdc_lock(KBDC p, int lock) -{ - int prevlock; - - prevlock = kbdcp(p)->lock; - kbdcp(p)->lock = lock; - - return (prevlock != lock); -} - /* check if any data is waiting to be processed */ int kbdc_data_ready(KBDC p) @@ -607,6 +559,8 @@ int write_controller_command(KBDC p, int c) { + kbdc_lock_assert(p); + if (!wait_while_controller_busy(kbdcp(p))) return FALSE; write_command(kbdcp(p), c); @@ -617,6 +571,8 @@ int write_controller_data(KBDC p, int c) { + kbdc_lock_assert(p); + if (!wait_while_controller_busy(kbdcp(p))) return FALSE; write_data(kbdcp(p), c); @@ -627,6 +583,8 @@ int write_kbd_command(KBDC p, int c) { + kbdc_lock_assert(p); + if (!wait_while_controller_busy(kbdcp(p))) return FALSE; write_data(kbdcp(p), c); @@ -637,6 +595,8 @@ int write_aux_command(KBDC p, int c) { + kbdc_lock_assert(p); + if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) return FALSE; return write_controller_data(p, c); @@ -649,6 +609,8 @@ int retry = KBD_MAXRETRY; int res = -1; + kbdc_lock_assert(p); + while (retry-- > 0) { if (!write_kbd_command(p, c)) continue; @@ -666,6 +628,8 @@ int retry = KBD_MAXRETRY; int res = -1; + kbdc_lock_assert(p); + while (retry-- > 0) { if (!write_aux_command(p, c)) continue; @@ -693,6 +657,8 @@ int retry; int res = -1; + kbdc_lock_assert(p); + for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_kbd_command(p, c)) continue; @@ -722,6 +688,8 @@ int retry; int res = -1; + kbdc_lock_assert(p); + for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_aux_command(p, c)) continue; @@ -752,6 +720,8 @@ int read_controller_data(KBDC p) { + kbdc_lock_assert(p); + if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (availq(&kbdcp(p)->aux)) @@ -779,6 +749,8 @@ } #endif + kbdc_lock_assert(p); + if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (!wait_for_kbd_data(kbdcp(p))) @@ -804,6 +776,8 @@ } #endif + kbdc_lock_assert(p); + if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); f = read_status(kbdcp(p)) & KBDS_BUFFER_FULL; @@ -823,6 +797,8 @@ int read_aux_data(KBDC p) { + kbdc_lock_assert(p); + if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); if (!wait_for_aux_data(kbdcp(p))) @@ -838,6 +814,8 @@ { int f; + kbdc_lock_assert(p); + if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); f = read_status(kbdcp(p)) & KBDS_BUFFER_FULL; @@ -866,6 +844,8 @@ #endif int delta = 2; + kbdc_lock_assert(p); + for (t = wait; t > 0; ) { if ((f = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); @@ -905,6 +885,8 @@ #endif int delta = 2; + kbdc_lock_assert(p); + for (t = wait; t > 0; ) { if ((f = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); @@ -944,6 +926,8 @@ #endif int delta = 2; + kbdc_lock_assert(p); + for (t = wait; t > 0; ) { if ((f = read_status(kbdcp(p))) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); @@ -991,6 +975,8 @@ int again = KBD_MAXWAIT; int c = KBD_RESEND; /* keep the compiler happy */ + kbdc_lock_assert(p); + while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_kbd_command(p, KBDC_RESET_KBD)) @@ -1029,6 +1015,8 @@ int again = KBD_MAXWAIT; int c = PSM_RESEND; /* keep the compiler happy */ + kbdc_lock_assert(p); + while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_aux_command(p, PSMC_RESET_DEV)) @@ -1077,6 +1065,8 @@ int again = KBD_MAXWAIT; int c = KBD_DIAG_FAIL; + kbdc_lock_assert(p); + while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_DIAGNOSE)) @@ -1105,6 +1095,8 @@ int again = KBD_MAXWAIT; int c = -1; + kbdc_lock_assert(p); + while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_KBD_PORT)) @@ -1131,6 +1123,8 @@ int again = KBD_MAXWAIT; int c = -1; + kbdc_lock_assert(p); + while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_AUX_PORT)) @@ -1167,6 +1161,8 @@ int get_controller_command_byte(KBDC p) { + kbdc_lock_assert(p); + if (kbdcp(p)->command_byte != -1) return kbdcp(p)->command_byte; if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE)) @@ -1179,6 +1175,8 @@ int set_controller_command_byte(KBDC p, int mask, int command) { + kbdc_lock_assert(p); + if (get_controller_command_byte(p) == -1) return FALSE; Index: sys/dev/atkbdc/atkbdcreg.h =================================================================== --- sys/dev/atkbdc/atkbdcreg.h +++ sys/dev/atkbdc/atkbdcreg.h @@ -198,7 +198,7 @@ bus_space_handle_t ioh1; int command_byte; /* current command byte value */ int command_mask; /* command byte mask bits for kbd/aux devices */ - int lock; /* FIXME: XXX not quite a semaphore... */ + struct mtx lock; kqueue kbd; /* keyboard data queue */ kqueue aux; /* auxiliary data queue */ int retry; @@ -221,6 +221,28 @@ #define KBDC_RID_KBD 0 #define KBDC_RID_AUX 1 +/* inlined functions */ +static __inline void +kbdc_lock(KBDC kbdc) +{ + + mtx_lock(&((atkbdc_softc_t *)kbdc)->lock); +} + +static __inline void +kbdc_unlock(KBDC kbdc) +{ + + mtx_unlock(&((atkbdc_softc_t *)kbdc)->lock); +} + +static __inline void +kbdc_lock_assert(KBDC kbdc) +{ + + mtx_assert(&((atkbdc_softc_t *)kbdc)->lock, MA_OWNED); +} + /* function prototypes */ atkbdc_softc_t *atkbdc_get_softc(int unit); @@ -230,7 +252,6 @@ int atkbdc_configure(void); KBDC atkbdc_open(int unit); -int kbdc_lock(KBDC kbdc, int lock); int kbdc_data_ready(KBDC kbdc); int write_controller_command(KBDC kbdc,int c); Index: sys/dev/atkbdc/psm.c =================================================================== --- sys/dev/atkbdc/psm.c +++ sys/dev/atkbdc/psm.c @@ -63,6 +63,7 @@ #include "opt_isa.h" #include "opt_psm.h" +#include "opt_evdev.h" #include #include @@ -81,7 +82,6 @@ #include #include #include -#include #include #include @@ -91,6 +91,11 @@ #include #endif +#ifdef EVDEV_SUPPORT +#include +#include +#endif + #include #include @@ -300,6 +305,8 @@ int dpmmy; int ntracesx; int ntracesy; + int dptracex; + int dptracey; int issemimt; int isclickpad; int hascrc; @@ -324,8 +331,10 @@ #define ELANTECH_REG_RDWR 0x00 #define ELANTECH_CUSTOM_CMD 0xf8 -#define ELANTECH_MAX_FINGERS PSM_FINGERS +#define ELANTECH_MAX_FINGERS 5 +#define ELANTECH_FINGER_MAX_P 255 +#define ELANTECH_FINGER_MAX_W 15 #define ELANTECH_FINGER_SET_XYP(pb) (finger_t) { \ .x = (((pb)->ipacket[1] & 0x0f) << 8) | (pb)->ipacket[2], \ .y = (((pb)->ipacket[4] & 0x0f) << 8) | (pb)->ipacket[5], \ @@ -366,11 +375,13 @@ typedef struct elantechaction { finger_t fingers[ELANTECH_MAX_FINGERS]; int mask; + int mask_v4wait; } elantechaction_t; /* driver control block */ struct psm_softc { /* Driver status information */ int unit; + struct mtx mtx; /* Driver`s mutex */ struct selinfo rsel; /* Process selecting for Input */ u_char state; /* Mouse driver state */ int config; /* driver configuration flags */ @@ -416,6 +427,10 @@ int cmdcount; struct sigio *async; /* Processes waiting for SIGIO */ int extended_buttons; +#ifdef EVDEV_SUPPORT + struct evdev_dev *evdev_a; /* Absolute reporting device */ + struct evdev_dev *evdev_r; /* Relative reporting device */ +#endif }; static devclass_t psm_devclass; @@ -425,6 +440,8 @@ #define PSM_ASLP 2 /* Waiting for mouse data */ #define PSM_SOFTARMED 4 /* Software interrupt armed */ #define PSM_NEED_SYNCBITS 8 /* Set syncbits using next data pkt */ +#define PSM_EV_OPEN_R 0x10 /* Relative evdev device is open */ +#define PSM_EV_OPEN_A 0x20 /* Absolute evdev device is open */ /* driver configuration flags (config) */ #define PSM_CONFIG_RESOLUTION 0x000f /* resolution */ @@ -530,13 +547,23 @@ static int psmdetach(device_t); static int psmresume(device_t); -static d_open_t psmopen; -static d_close_t psmclose; +static d_open_t psm_cdev_open; +static d_close_t psm_cdev_close; static d_read_t psmread; static d_write_t psmwrite; static d_ioctl_t psmioctl; static d_poll_t psmpoll; +static int psmopen(struct psm_softc *); +static int psmclose(struct psm_softc *); + +#ifdef EVDEV_SUPPORT +static evdev_open_t psm_ev_open_r; +static evdev_close_t psm_ev_close_r; +static evdev_open_t psm_ev_open_a; +static evdev_close_t psm_ev_close_a; +#endif + static int enable_aux_dev(KBDC); static int disable_aux_dev(KBDC); static int get_mouse_status(KBDC, int *, int, int); @@ -553,8 +580,12 @@ static int doopen(struct psm_softc *, int); static int reinitialize(struct psm_softc *, int); static char *model_name(int); +static void psm_lock(struct psm_softc *); +static void psm_unlock(struct psm_softc *); +static void psm_lock_assert(struct psm_softc *); static void psmsoftintr(void *); static void psmsoftintridle(void *); +static void psmintr_locked(void *); static void psmintr(void *); static void psmtimeout(void *); static int timeelapsed(const struct timeval *, int, int, @@ -665,9 +696,8 @@ static struct cdevsw psm_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, - .d_open = psmopen, - .d_close = psmclose, + .d_open = psm_cdev_open, + .d_close = psm_cdev_close, .d_read = psmread, .d_write = psmwrite, .d_ioctl = psmioctl, @@ -675,12 +705,25 @@ .d_name = PSM_DRIVER_NAME, }; +#ifdef EVDEV_SUPPORT +static const struct evdev_methods psm_ev_methods_r = { + .ev_open = psm_ev_open_r, + .ev_close = psm_ev_close_r, +}; +static const struct evdev_methods psm_ev_methods_a = { + .ev_open = psm_ev_open_a, + .ev_close = psm_ev_close_a, +}; +#endif + /* device I/O routines */ static int enable_aux_dev(KBDC kbdc) { int res; + kbdc_lock_assert(kbdc); + res = send_aux_command(kbdc, PSMC_ENABLE_DEV); VLOG(2, (LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res)); @@ -692,6 +735,8 @@ { int res; + kbdc_lock_assert(kbdc); + res = send_aux_command(kbdc, PSMC_DISABLE_DEV); VLOG(2, (LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res)); @@ -705,6 +750,8 @@ int res; int i; + kbdc_lock_assert(kbdc); + switch (flag) { case 0: default: @@ -739,6 +786,8 @@ int res; int id; + kbdc_lock_assert(kbdc); + empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, PSMC_SEND_DEV_ID); VLOG(2, (LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res)); @@ -759,6 +808,8 @@ { int res; + kbdc_lock_assert(kbdc); + res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate); VLOG(2, (LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res)); @@ -770,6 +821,8 @@ { int res; + kbdc_lock_assert(kbdc); + switch (scale) { case 1: default: @@ -792,6 +845,8 @@ { int res; + kbdc_lock_assert(kbdc); + res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val); VLOG(2, (LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res)); @@ -807,6 +862,8 @@ { int res; + kbdc_lock_assert(kbdc); + res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE); VLOG(2, (LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res)); @@ -819,6 +876,8 @@ int c = 2; /* assume two buttons by default */ int status[3]; + kbdc_lock_assert(kbdc); + /* * NOTE: a special sequence to obtain Logitech Mouse specific * information: set resolution to 25 ppi, set scaling to 1:1, set @@ -896,6 +955,8 @@ static void recover_from_error(KBDC kbdc) { + kbdc_lock_assert(kbdc); + /* discard anything left in the output buffer */ empty_both_buffers(kbdc, 10); @@ -921,6 +982,8 @@ static int restore_controller(KBDC kbdc, int command_byte) { + kbdc_lock_assert(kbdc); + empty_both_buffers(kbdc, 10); if (!set_controller_command_byte(kbdc, 0xff, command_byte)) { @@ -948,6 +1011,8 @@ int stat[3]; int i; + kbdc_lock_assert(kbdc); + switch((i = test_aux_port(kbdc))) { case 1: /* ignore these errors */ case 2: @@ -1031,6 +1096,9 @@ { int stat[3]; + psm_lock_assert(sc); + kbdc_lock_assert(sc->kbdc); + /* * FIXME: Synaptics TouchPad seems to go back to Relative Mode with * no obvious reason. Thus we check the current mode and restore the @@ -1133,12 +1201,11 @@ { int err; int c; - int s; + + psm_lock_assert(sc); /* don't let anybody mess with the aux device */ - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); - s = spltty(); + kbdc_lock(sc->kbdc); /* block our watchdog timer */ sc->watchdog = FALSE; @@ -1157,8 +1224,7 @@ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ - splx(s); - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); log(LOG_ERR, "psm%d: unable to set the command byte (reinitialize).\n", sc->unit); @@ -1192,10 +1258,10 @@ err = ENXIO; } } - splx(s); /* restore the driver state */ - if ((sc->state & PSM_OPEN) && (err == 0)) { + if ((sc->state & (PSM_OPEN | PSM_EV_OPEN_R | PSM_EV_OPEN_A)) && + (err == 0)) { /* enable the aux device and the port again */ err = doopen(sc, c); if (err != 0) @@ -1214,10 +1280,31 @@ } } - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); return (err); } +static void +psm_lock(struct psm_softc *sc) +{ + + mtx_lock(&sc->mtx); +} + +static void +psm_unlock(struct psm_softc *sc) +{ + + mtx_unlock(&sc->mtx); +} + +static void +psm_lock_assert(struct psm_softc *sc) +{ + + mtx_assert(&sc->mtx, MA_OWNED); +} + /* psm driver entry points */ static void @@ -1259,7 +1346,7 @@ if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ - kbdc_lock(sc->kbdc, FALSE); \ + kbdc_unlock(sc->kbdc); \ return (v); \ } while (0) @@ -1306,12 +1393,7 @@ device_set_desc(dev, "PS/2 Mouse"); - if (!kbdc_lock(sc->kbdc, TRUE)) { - printf("psm%d: unable to lock the controller.\n", unit); - if (bootverbose) - --verbose; - return (ENXIO); - } + kbdc_lock(sc->kbdc); /* * NOTE: two bits in the command byte controls the operation of the @@ -1572,10 +1654,232 @@ /* done */ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); return (0); } +#ifdef EVDEV_SUPPORT +/* Values are taken from Linux drivers */ +#define PS2_MOUSE_VENDOR 0x0002 +#define PS2_MOUSE_GENERIC_PRODUCT 0x0001 +#define PS2_MOUSE_SYNAPTICS_PRODUCT 0x0009 +#define PS2_MOUSE_ELANTECH_PRODUCT 0x000E + +#define ABSINFO_END { ABS_CNT, 0, 0, 0 } + +static void +psm_support_abs_bulk(struct evdev_dev *evdev, const uint16_t info[][4]) +{ + size_t i; + + for (i = 0; info[i][0] != ABS_CNT; i++) + evdev_support_abs(evdev, info[i][0], 0, info[i][1], info[i][2], + 0, 0, info[i][3]); +} + +static void +psm_push_mt_finger(struct psm_softc *sc, int id, const finger_t *f) +{ + int y = sc->synhw.minimumYCoord + sc->synhw.maximumYCoord - f->y; + + evdev_push_abs(sc->evdev_a, ABS_MT_SLOT, id); + evdev_push_abs(sc->evdev_a, ABS_MT_TRACKING_ID, id); + evdev_push_abs(sc->evdev_a, ABS_MT_POSITION_X, f->x); + evdev_push_abs(sc->evdev_a, ABS_MT_POSITION_Y, y); + evdev_push_abs(sc->evdev_a, ABS_MT_PRESSURE, f->p); +} + +static void +psm_push_st_finger(struct psm_softc *sc, const finger_t *f) +{ + int y = sc->synhw.minimumYCoord + sc->synhw.maximumYCoord - f->y; + + evdev_push_abs(sc->evdev_a, ABS_X, f->x); + evdev_push_abs(sc->evdev_a, ABS_Y, y); + evdev_push_abs(sc->evdev_a, ABS_PRESSURE, f->p); + if (sc->synhw.capPalmDetect) + evdev_push_abs(sc->evdev_a, ABS_TOOL_WIDTH, f->w); +} + +static void +psm_release_mt_slot(struct evdev_dev *evdev, int32_t slot) +{ + + evdev_push_abs(evdev, ABS_MT_SLOT, slot); + evdev_push_abs(evdev, ABS_MT_TRACKING_ID, -1); +} + +static int +psm_register(device_t dev, const char *name, int nbuttons, int nwheels) +{ + struct psm_softc *sc = device_get_softc(dev); + struct evdev_dev *evdev_r; + int error, i; + + evdev_r = evdev_alloc(); + evdev_set_name(evdev_r, name); + evdev_set_phys(evdev_r, device_get_nameunit(dev)); + evdev_set_id(evdev_r, BUS_I8042, PS2_MOUSE_VENDOR, + PS2_MOUSE_GENERIC_PRODUCT, 0); + evdev_set_methods(evdev_r, sc, &psm_ev_methods_r); + + evdev_support_prop(evdev_r, INPUT_PROP_POINTER); + evdev_support_event(evdev_r, EV_SYN); + evdev_support_event(evdev_r, EV_KEY); + evdev_support_event(evdev_r, EV_REL); + evdev_support_rel(evdev_r, REL_X); + evdev_support_rel(evdev_r, REL_Y); + switch (nwheels) { + case 2: + evdev_support_rel(evdev_r, REL_HWHEEL); + /* FALLTHROUGH */ + case 1: + evdev_support_rel(evdev_r, REL_WHEEL); + } + for (i = 0; i < nbuttons; i++) + evdev_support_key(evdev_r, BTN_MOUSE + i); + + error = evdev_register_mtx(evdev_r, &sc->mtx); + if (error) + evdev_free(evdev_r); + else + sc->evdev_r = evdev_r; + return (error); +} + +static int +psm_register_synaptics(device_t dev) +{ + struct psm_softc *sc = device_get_softc(dev); + const uint16_t synaptics_absinfo_st[][4] = { + { ABS_X, sc->synhw.minimumXCoord, + sc->synhw.maximumXCoord, sc->synhw.infoXupmm }, + { ABS_Y, sc->synhw.minimumYCoord, + sc->synhw.maximumYCoord, sc->synhw.infoYupmm }, + { ABS_PRESSURE, 0, ELANTECH_FINGER_MAX_P, 0 }, + ABSINFO_END, + }; + const uint16_t synaptics_absinfo_mt[][4] = { + { ABS_MT_SLOT, 0, PSM_FINGERS-1, 0}, + { ABS_MT_TRACKING_ID, -1, PSM_FINGERS-1, 0}, + { ABS_MT_POSITION_X, sc->synhw.minimumXCoord, + sc->synhw.maximumXCoord, sc->synhw.infoXupmm }, + { ABS_MT_POSITION_Y, sc->synhw.minimumYCoord, + sc->synhw.maximumYCoord, sc->synhw.infoYupmm }, + { ABS_MT_PRESSURE, 0, ELANTECH_FINGER_MAX_P, 0 }, + ABSINFO_END, + }; + struct evdev_dev *evdev_a; + const char *guest_name; + int error, i; + + evdev_a = evdev_alloc(); + evdev_set_name(evdev_a, model_name(sc->hw.model)); + evdev_set_phys(evdev_a, device_get_nameunit(dev)); + evdev_set_id(evdev_a, BUS_I8042, PS2_MOUSE_VENDOR, + PS2_MOUSE_SYNAPTICS_PRODUCT, 0); + evdev_set_methods(evdev_a, sc, &psm_ev_methods_a); + + evdev_support_event(evdev_a, EV_SYN); + evdev_support_event(evdev_a, EV_KEY); + evdev_support_event(evdev_a, EV_ABS); + evdev_support_prop(evdev_a, INPUT_PROP_POINTER); + if (sc->synhw.capAdvancedGestures) + evdev_support_prop(evdev_a, INPUT_PROP_SEMI_MT); + if (sc->synhw.capClickPad) + evdev_support_prop(evdev_a, INPUT_PROP_BUTTONPAD); + evdev_support_key(evdev_a, BTN_TOUCH); + evdev_support_nfingers(evdev_a, 3); + psm_support_abs_bulk(evdev_a, synaptics_absinfo_st); + if (sc->synhw.capAdvancedGestures || sc->synhw.capReportsV) + psm_support_abs_bulk(evdev_a, synaptics_absinfo_mt); + if (sc->synhw.capPalmDetect) + evdev_support_abs(evdev_a, ABS_TOOL_WIDTH, 0, 0, 15, 0, 0, 0); + evdev_support_key(evdev_a, BTN_LEFT); + if (!sc->synhw.capClickPad) { + evdev_support_key(evdev_a, BTN_RIGHT); + if (sc->synhw.capExtended && sc->synhw.capMiddle) + evdev_support_key(evdev_a, BTN_MIDDLE); + } + if (sc->synhw.capExtended && sc->synhw.capFourButtons) { + evdev_support_key(evdev_a, BTN_BACK); + evdev_support_key(evdev_a, BTN_FORWARD); + } + if (sc->synhw.capExtended && (sc->synhw.nExtendedButtons > 0)) + for (i = 0; i < sc->synhw.nExtendedButtons; i++) + evdev_support_key(evdev_a, BTN_0 + i); + + error = evdev_register_mtx(evdev_a, &sc->mtx); + if (!error && sc->synhw.capPassthrough) { + guest_name = sc->tpinfo.sysctl_tree != NULL ? + "IBM/Lenovo TrackPoint" : "Synaptics guest"; + error = psm_register(dev, guest_name, 3, 0); + } + if (error) + evdev_free(evdev_a); + else + sc->evdev_a = evdev_a; + return (error); +} + +static int +psm_register_elantech(device_t dev) +{ + struct psm_softc *sc = device_get_softc(dev); + const uint16_t elantech_absinfo[][4] = { + { ABS_X, 0, sc->elanhw.sizex, + sc->elanhw.dpmmx }, + { ABS_Y, 0, sc->elanhw.sizey, + sc->elanhw.dpmmy }, + { ABS_PRESSURE, 0, ELANTECH_FINGER_MAX_P, 0 }, + { ABS_TOOL_WIDTH, 0, ELANTECH_FINGER_MAX_W, 0 }, + { ABS_MT_SLOT, 0, ELANTECH_MAX_FINGERS - 1, 0 }, + { ABS_MT_TRACKING_ID, -1, ELANTECH_MAX_FINGERS - 1, 0 }, + { ABS_MT_POSITION_X, 0, sc->elanhw.sizex, + sc->elanhw.dpmmx }, + { ABS_MT_POSITION_Y, 0, sc->elanhw.sizey, + sc->elanhw.dpmmy }, + { ABS_MT_PRESSURE, 0, ELANTECH_FINGER_MAX_P, 0 }, + { ABS_MT_TOUCH_MAJOR, 0, ELANTECH_FINGER_MAX_W * + sc->elanhw.dptracex, 0 }, + ABSINFO_END, + }; + struct evdev_dev *evdev_a; + int error; + + evdev_a = evdev_alloc(); + evdev_set_name(evdev_a, model_name(sc->hw.model)); + evdev_set_phys(evdev_a, device_get_nameunit(dev)); + evdev_set_id(evdev_a, BUS_I8042, PS2_MOUSE_VENDOR, + PS2_MOUSE_ELANTECH_PRODUCT, 0); + evdev_set_methods(evdev_a, sc, &psm_ev_methods_a); + + evdev_support_event(evdev_a, EV_SYN); + evdev_support_event(evdev_a, EV_KEY); + evdev_support_event(evdev_a, EV_ABS); + evdev_support_prop(evdev_a, INPUT_PROP_POINTER); + if (sc->elanhw.issemimt) + evdev_support_prop(evdev_a, INPUT_PROP_SEMI_MT); + if (sc->elanhw.isclickpad) + evdev_support_prop(evdev_a, INPUT_PROP_BUTTONPAD); + evdev_support_key(evdev_a, BTN_TOUCH); + evdev_support_nfingers(evdev_a, ELANTECH_MAX_FINGERS); + evdev_support_key(evdev_a, BTN_LEFT); + if (!sc->elanhw.isclickpad) + evdev_support_key(evdev_a, BTN_RIGHT); + psm_support_abs_bulk(evdev_a, elantech_absinfo); + + error = evdev_register_mtx(evdev_a, &sc->mtx); + if (!error && sc->elanhw.hastrackpoint) + error = psm_register(dev, "Elantech trackpoint", 3, 0); + if (error) + evdev_free(evdev_a); + else + sc->evdev_a = evdev_a; + return (error); +} +#endif + static int psmattach(device_t dev) { @@ -1586,16 +1890,17 @@ /* Setup initial state */ sc->state = PSM_VALID; - callout_init(&sc->callout, 0); - callout_init(&sc->softcallout, 0); + mtx_init(&sc->mtx, "psm lock", NULL, MTX_DEF); + callout_init_mtx(&sc->callout, &sc->mtx, 0); + callout_init_mtx(&sc->softcallout, &sc->mtx, 0); /* Setup our interrupt handler */ rid = KBDC_RID_AUX; sc->intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->intr == NULL) return (ENXIO); - error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, NULL, psmintr, sc, - &sc->ih); + error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY | INTR_MPSAFE, + NULL, psmintr, sc, &sc->ih); if (error) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr); return (error); @@ -1607,6 +1912,40 @@ sc->bdev = make_dev(&psm_cdevsw, 0, 0, 0, 0666, "bpsm%d", unit); sc->bdev->si_drv1 = sc; +#ifdef EVDEV_SUPPORT + switch (sc->hw.model) { + case MOUSE_MODEL_SYNAPTICS: + error = psm_register_synaptics(dev); + break; + + case MOUSE_MODEL_ELANTECH: + error = psm_register_elantech(dev); + break; + + case MOUSE_MODEL_MOUSEMANPLUS: + case MOUSE_MODEL_4D: + error = psm_register(dev, model_name(sc->hw.model), + sc->hw.buttons, 2); + break; + + case MOUSE_MODEL_EXPLORER: + case MOUSE_MODEL_INTELLI: + case MOUSE_MODEL_NET: + case MOUSE_MODEL_NETSCROLL: + case MOUSE_MODEL_4DPLUS: + error = psm_register(dev, model_name(sc->hw.model), + sc->hw.buttons, 1); + break; + + default: + error = psm_register(dev, model_name(sc->hw.model), + sc->hw.buttons, 0); + } + + if (error) + return (error); +#endif + /* Some touchpad devices need full reinitialization after suspend. */ switch (sc->hw.model) { case MOUSE_MODEL_SYNAPTICS: @@ -1655,6 +1994,11 @@ if (sc->state & PSM_OPEN) return (EBUSY); +#ifdef EVDEV_SUPPORT + evdev_free(sc->evdev_r); + evdev_free(sc->evdev_a); +#endif + rid = KBDC_RID_AUX; bus_teardown_intr(dev, sc->intr, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr); @@ -1662,19 +2006,100 @@ destroy_dev(sc->dev); destroy_dev(sc->bdev); - callout_drain(&sc->callout); - callout_drain(&sc->softcallout); + psm_lock(sc); + callout_stop(&sc->callout); + callout_stop(&sc->softcallout); + psm_unlock(sc); + mtx_destroy(&sc->mtx); return (0); } +#ifdef EVDEV_SUPPORT +static int +psm_ev_open_r(struct evdev_dev *evdev, void *ev_softc) +{ + struct psm_softc *sc = (struct psm_softc *)ev_softc; + int err = 0; + + psm_lock_assert(sc); + + /* Get device data */ + if ((sc->state & PSM_VALID) == 0) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + if (!(sc->state & (PSM_OPEN | PSM_EV_OPEN_A))) + err = psmopen(sc); + + if (err == 0) + sc->state |= PSM_EV_OPEN_R; + + return (err); +} + +static void +psm_ev_close_r(struct evdev_dev *evdev, void *ev_softc) +{ + struct psm_softc *sc = (struct psm_softc *)ev_softc; + + psm_lock_assert(sc); + + sc->state &= ~PSM_EV_OPEN_R; + + if (sc->state & (PSM_OPEN | PSM_EV_OPEN_A)) + return; + + if (sc->state & PSM_VALID) + psmclose(sc); +} + +static int +psm_ev_open_a(struct evdev_dev *evdev, void *ev_softc) +{ + struct psm_softc *sc = (struct psm_softc *)ev_softc; + int err = 0; + + psm_lock_assert(sc); + + /* Get device data */ + if ((sc->state & PSM_VALID) == 0) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + if (!(sc->state & (PSM_OPEN | PSM_EV_OPEN_R))) + err = psmopen(sc); + + if (err == 0) + sc->state |= PSM_EV_OPEN_A; + + return (err); +} + +static void +psm_ev_close_a(struct evdev_dev *evdev, void *ev_softc) +{ + struct psm_softc *sc = (struct psm_softc *)ev_softc; + + psm_lock_assert(sc); + + sc->state &= ~PSM_EV_OPEN_A; + + if (sc->state & (PSM_OPEN | PSM_EV_OPEN_R)) + return; + + if (sc->state & PSM_VALID) + psmclose(sc); +} +#endif + static int -psmopen(struct cdev *dev, int flag, int fmt, struct thread *td) +psm_cdev_open(struct cdev *dev, int flag, int fmt, struct thread *td) { struct psm_softc *sc; - int command_byte; - int err; - int s; + int err = 0; /* Get device data */ sc = dev->si_drv1; @@ -1683,12 +2108,74 @@ return (ENXIO); } + psm_lock(sc); + /* Disallow multiple opens */ - if (sc->state & PSM_OPEN) + if (sc->state & PSM_OPEN) { + psm_unlock(sc); return (EBUSY); + } device_busy(devclass_get_device(psm_devclass, sc->unit)); +#ifdef EVDEV_SUPPORT + /* Already opened by evdev */ + if (!(sc->state & (PSM_EV_OPEN_R | PSM_EV_OPEN_A))) +#endif + err = psmopen(sc); + + if (err == 0) + sc->state |= PSM_OPEN; + else + device_unbusy(devclass_get_device(psm_devclass, sc->unit)); + + psm_unlock(sc); + return (err); +} + +static int +psm_cdev_close(struct cdev *dev, int flag, int fmt, struct thread *td) +{ + struct psm_softc *sc; + int err = 0; + + /* Get device data */ + sc = dev->si_drv1; + if ((sc == NULL) || (sc->state & PSM_VALID) == 0) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + psm_lock(sc); + +#ifdef EVDEV_SUPPORT + /* Still opened by evdev */ + if (!(sc->state & (PSM_EV_OPEN_R | PSM_EV_OPEN_A))) +#endif + err = psmclose(sc); + + if (err == 0) { + sc->state &= ~PSM_OPEN; + /* clean up and sigio requests */ + if (sc->async != NULL) { + funsetown(&sc->async); + sc->async = NULL; + } + device_unbusy(devclass_get_device(psm_devclass, sc->unit)); + } + + psm_unlock(sc); + return (err); +} + +static int +psmopen(struct psm_softc *sc) +{ + int command_byte; + int err; + + psm_lock_assert(sc); + /* Initialize state */ sc->mode.level = sc->dflt_mode.level; sc->mode.protocol = sc->dflt_mode.protocol; @@ -1715,11 +2202,9 @@ sc->pkterrors = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); + kbdc_lock(sc->kbdc); /* save the current controller command byte */ - s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); /* enable the aux port and temporalily disable the keyboard */ @@ -1728,50 +2213,35 @@ KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ - kbdc_lock(sc->kbdc, FALSE); - splx(s); + kbdc_unlock(sc->kbdc); log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n", sc->unit); return (EIO); } - /* - * Now that the keyboard controller is told not to generate - * the keyboard and mouse interrupts, call `splx()' to allow - * the other tty interrupts. The clock interrupt may also occur, - * but timeout routines will be blocked by the poll flag set - * via `kbdc_lock()' - */ - splx(s); /* enable the mouse device */ err = doopen(sc, command_byte); - /* done */ - if (err == 0) - sc->state |= PSM_OPEN; - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); return (err); } static int -psmclose(struct cdev *dev, int flag, int fmt, struct thread *td) +psmclose(struct psm_softc *sc) { - struct psm_softc *sc = dev->si_drv1; int stat[3]; int command_byte; - int s; + + psm_lock_assert(sc); /* don't let timeout routines in the keyboard driver to poll the kbdc */ - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); + kbdc_lock(sc->kbdc); /* save the current controller command byte */ - s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); if (command_byte == -1) { - kbdc_lock(sc->kbdc, FALSE); - splx(s); + kbdc_unlock(sc->kbdc); return (EIO); } @@ -1790,7 +2260,6 @@ * so long as the mouse will accept the DISABLE command. */ } - splx(s); /* stop the watchdog timer */ callout_stop(&sc->callout); @@ -1834,16 +2303,8 @@ /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); - /* clean up and sigio requests */ - if (sc->async != NULL) { - funsetown(&sc->async); - sc->async = NULL; - } - /* close is almost always successful */ - sc->state &= ~PSM_OPEN; - kbdc_lock(sc->kbdc, FALSE); - device_unbusy(devclass_get_device(psm_devclass, sc->unit)); + kbdc_unlock(sc->kbdc); return (0); } @@ -1915,36 +2376,33 @@ struct psm_softc *sc = dev->si_drv1; u_char buf[PSM_SMALLBUFSIZE]; int error = 0; - int s; int l; if ((sc->state & PSM_VALID) == 0) return (EIO); /* block until mouse activity occurred */ - s = spltty(); + psm_lock(sc); while (sc->queue.count <= 0) { if (dev != sc->bdev) { - splx(s); + psm_unlock(sc); return (EWOULDBLOCK); } sc->state |= PSM_ASLP; - error = tsleep(sc, PZERO | PCATCH, "psmrea", 0); + error = mtx_sleep(sc, &sc->mtx, PZERO | PCATCH, "psmrea", 0); sc->state &= ~PSM_ASLP; if (error) { - splx(s); + psm_unlock(sc); return (error); } else if ((sc->state & PSM_VALID) == 0) { /* the device disappeared! */ - splx(s); + psm_unlock(sc); return (EIO); } } - splx(s); /* copy data to the user land */ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) { - s = spltty(); l = imin(sc->queue.count, uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); @@ -1958,11 +2416,13 @@ bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l); sc->queue.count -= l; sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf); - splx(s); + psm_unlock(sc); error = uiomove(buf, l, uio); + psm_lock(sc); if (error) break; } + psm_unlock(sc); return (error); } @@ -1970,20 +2430,18 @@ static int block_mouse_data(struct psm_softc *sc, int *c) { - int s; - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); + psm_lock(sc); + kbdc_lock(sc->kbdc); - s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ - splx(s); - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); + psm_unlock(sc); return (EIO); } @@ -1996,13 +2454,12 @@ * output buffer; throw it away. Note that the second argument * to `empty_aux_buffer()' is zero, so that the call will just * flush the internal queue. - * `psmintr()' will be invoked after `splx()' if an interrupt is - * pending; it will see no data and returns immediately. + * `psmintr()' will be invoked after `kbdc_unlock()' if an interrupt + * is pending; it will see no data and returns immediately. */ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */ flushpackets(sc); - splx(s); return (0); } @@ -2011,6 +2468,8 @@ dropqueue(struct psm_softc *sc) { + psm_lock_assert(sc); + sc->queue.count = 0; sc->queue.head = 0; sc->queue.tail = 0; @@ -2025,6 +2484,8 @@ flushpackets(struct psm_softc *sc) { + psm_lock_assert(sc); + dropqueue(sc); bzero(&sc->pqueue, sizeof(sc->pqueue)); } @@ -2052,7 +2513,8 @@ error = EIO; } - kbdc_lock(sc->kbdc, FALSE); + kbdc_unlock(sc->kbdc); + psm_unlock(sc); return (error); } @@ -2075,14 +2537,20 @@ error = uiomove(buf, l, uio); if (error) break; + kbdc_lock(sc->kbdc); for (i = 0; i < l; i++) { VLOG(4, (LOG_DEBUG, "psm: cmd 0x%x\n", buf[i])); if (!write_aux_command(sc->kbdc, buf[i])) { + kbdc_unlock(sc->kbdc); VLOG(2, (LOG_DEBUG, "psm: cmd 0x%x failed.\n", buf[i])); - return (reinitialize(sc, FALSE)); + psm_lock(sc); + error = reinitialize(sc, FALSE); + psm_unlock(sc); + return (error); } } + kbdc_unlock(sc->kbdc); } return (error); @@ -2102,39 +2570,38 @@ int stat[3]; int command_byte; int error = 0; - int s; /* Perform IOCTL command */ switch (cmd) { case OLD_MOUSE_GETHWINFO: - s = spltty(); + psm_lock(sc); ((old_mousehw_t *)addr)->buttons = sc->hw.buttons; ((old_mousehw_t *)addr)->iftype = sc->hw.iftype; ((old_mousehw_t *)addr)->type = sc->hw.type; ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff; - splx(s); + psm_unlock(sc); break; case MOUSE_GETHWINFO: - s = spltty(); + psm_lock(sc); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == PSM_LEVEL_BASE) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; - splx(s); + psm_unlock(sc); break; case MOUSE_SYN_GETHWINFO: - s = spltty(); + psm_lock(sc); if (sc->synhw.infoMajor >= 4) *(synapticshw_t *)addr = sc->synhw; else error = EINVAL; - splx(s); + psm_unlock(sc); break; case OLD_MOUSE_GETMODE: - s = spltty(); + psm_lock(sc); switch (sc->mode.level) { case PSM_LEVEL_BASE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; @@ -2150,11 +2617,11 @@ ((old_mousemode_t *)addr)->rate = sc->mode.rate; ((old_mousemode_t *)addr)->resolution = sc->mode.resolution; ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor; - splx(s); + psm_unlock(sc); break; case MOUSE_GETMODE: - s = spltty(); + psm_lock(sc); *(mousemode_t *)addr = sc->mode; if ((sc->flags & PSM_NEED_SYNCBITS) != 0) { ((mousemode_t *)addr)->syncmask[0] = 0; @@ -2180,7 +2647,7 @@ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } - splx(s); + psm_unlock(sc); break; case OLD_MOUSE_SETMODE: @@ -2260,12 +2727,10 @@ set_mouse_scaling(sc->kbdc, 1); get_mouse_status(sc->kbdc, stat, 0, 3); - s = spltty(); sc->mode.rate = mode.rate; sc->mode.resolution = mode.resolution; sc->mode.accelfactor = mode.accelfactor; sc->mode.level = mode.level; - splx(s); unblock_mouse_data(sc, command_byte); break; @@ -2282,7 +2747,7 @@ break; case MOUSE_GETSTATUS: - s = spltty(); + psm_lock(sc); status = sc->status; sc->status.flags = 0; sc->status.obutton = sc->status.button; @@ -2290,7 +2755,7 @@ sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; - splx(s); + psm_unlock(sc); *(mousestatus_t *)addr = status; break; @@ -2298,11 +2763,11 @@ case MOUSE_GETVARS: var = (mousevar_t *)addr; bzero(var, sizeof(*var)); - s = spltty(); + psm_lock(sc); var->var[0] = MOUSE_VARS_PS2_SIG; var->var[1] = sc->config; var->var[2] = sc->flags; - splx(s); + psm_unlock(sc); break; case MOUSE_SETVARS: @@ -2421,17 +2886,13 @@ psmtimeout(void *arg) { struct psm_softc *sc; - int s; sc = (struct psm_softc *)arg; - s = spltty(); - if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) { + if (sc->watchdog) { VLOG(4, (LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit)); - psmintr(sc); - kbdc_lock(sc->kbdc, FALSE); + psmintr_locked(sc); } sc->watchdog = TRUE; - splx(s); callout_reset(&sc->callout, hz, psmtimeout, sc); } @@ -2480,21 +2941,33 @@ SYSCTL_INT(_hw_psm, OID_AUTO, elantech_support, CTLFLAG_RDTUN, &elantech_support, 0, "Enable support for Elantech touchpads"); +static int +psm_read_aux_data(KBDC kbdc) +{ + int c; + + kbdc_lock(kbdc); + c = read_aux_data_no_wait(kbdc); + kbdc_unlock(kbdc); + return (c); +} + static void -psmintr(void *arg) +psmintr_locked(void *arg) { struct psm_softc *sc = arg; struct timeval now; int c; packetbuf_t *pb; + psm_lock_assert(sc); /* read until there is nothing to read */ - while((c = read_aux_data_no_wait(sc->kbdc)) != -1) { + while((c = psm_read_aux_data(sc->kbdc)) != -1) { pb = &sc->pqueue[sc->pqueue_end]; /* discard the byte if the device is not open */ - if ((sc->state & PSM_OPEN) == 0) + if (!(sc->state & (PSM_OPEN | PSM_EV_OPEN_R | PSM_EV_OPEN_A))) continue; getmicrouptime(&now); @@ -2574,8 +3047,10 @@ VLOG(3, (LOG_DEBUG, "psmintr: re-enable the mouse.\n")); pb->inputbytes = 0; + kbdc_lock(sc->kbdc); disable_aux_dev(sc->kbdc); enable_aux_dev(sc->kbdc); + kbdc_unlock(sc->kbdc); } else { VLOG(3, (LOG_DEBUG, "psmintr: discard a byte (%d)\n", @@ -2636,6 +3111,16 @@ } static void +psmintr(void *arg) +{ + struct psm_softc *sc = arg; + + psm_lock(sc); + psmintr_locked(sc); + psm_unlock(sc); +} + +static void proc_mmanplus(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms, int *x, int *y, int *z) { @@ -2852,7 +3337,15 @@ guest_buttons |= MOUSE_BUTTON2DOWN; if (pb->ipacket[1] & 0x02) guest_buttons |= MOUSE_BUTTON3DOWN; - +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + evdev_push_rel(sc->evdev_r, REL_X, *x); + evdev_push_rel(sc->evdev_r, REL_Y, -*y); + evdev_push_mouse_btn(sc->evdev_r, + guest_buttons); + evdev_sync(sc->evdev_r); + } +#endif ms->button = touchpad_buttons | guest_buttons | sc->extended_buttons; } @@ -2963,6 +3456,24 @@ int mask = 0; maskedbits = (sc->synhw.nExtendedButtons + 1) >> 1; mask = (1 << maskedbits) - 1; +#ifdef EVDEV_SUPPORT + int i; + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + if (sc->synhw.capPassthrough) { + evdev_push_mouse_btn(sc->evdev_r, + extended_buttons); + evdev_sync(sc->evdev_r); + } + for (i = 0; i < maskedbits; i++) { + evdev_push_key(sc->evdev_a, + BTN_0 + i * 2, + pb->ipacket[4] & (1 << i)); + evdev_push_key(sc->evdev_a, + BTN_0 + i * 2 + 1, + pb->ipacket[5] & (1 << i)); + } + } +#endif pb->ipacket[4] &= ~(mask); pb->ipacket[5] &= ~(mask); } else if (!sc->syninfo.directional_scrolls && @@ -3014,15 +3525,42 @@ if (id >= nfingers) PSM_FINGER_RESET(f[id]); +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + for (id = 0; id < PSM_FINGERS; id++) { + if (PSM_FINGER_IS_SET(f[id])) + psm_push_mt_finger(sc, id, &f[id]); + else + psm_release_mt_slot(sc->evdev_a, id); + } + evdev_push_key(sc->evdev_a, BTN_TOUCH, nfingers > 0); + evdev_push_nfingers(sc->evdev_a, nfingers); + if (nfingers > 0) + psm_push_st_finger(sc, &f[0]); + else + evdev_push_abs(sc->evdev_a, ABS_PRESSURE, 0); + evdev_push_mouse_btn(sc->evdev_a, touchpad_buttons); + if (sc->synhw.capExtended && sc->synhw.capFourButtons) { + evdev_push_key(sc->evdev_a, BTN_FORWARD, + touchpad_buttons & MOUSE_BUTTON4DOWN); + evdev_push_key(sc->evdev_a, BTN_BACK, + touchpad_buttons & MOUSE_BUTTON5DOWN); + } + evdev_sync(sc->evdev_a); + } +#endif + ms->button = touchpad_buttons; + psmgestures(sc, &f[0], nfingers, ms); + for (id = 0; id < PSM_FINGERS; id++) + psmsmoother(sc, &f[id], id, ms, x, y); + /* Palm detection doesn't terminate the current action. */ - if (!psmpalmdetect(sc, &f[0], nfingers)) { - psmgestures(sc, &f[0], nfingers, ms); - for (id = 0; id < PSM_FINGERS; id++) - psmsmoother(sc, &f[id], id, ms, x, y); - } else { - VLOG(2, (LOG_DEBUG, "synaptics: palm detected! (%d)\n", f[0].w)); + if (psmpalmdetect(sc, &f[0], nfingers)) { + *x = *y = *z = 0; + ms->button = ms->obutton; + return (0); } ms->button |= extended_buttons | guest_buttons; @@ -3062,8 +3600,9 @@ psmpalmdetect(struct psm_softc *sc, finger_t *f, int nfingers) { if (!( - ((sc->synhw.capMultiFinger || - sc->synhw.capAdvancedGestures) && nfingers > 1) || + ((sc->synhw.capMultiFinger || sc->synhw.capAdvancedGestures) && + !sc->synhw.capReportsV && nfingers > 1) || + (sc->synhw.capReportsV && nfingers > 2) || (sc->synhw.capPalmDetect && f->w <= sc->syninfo.max_width) || (!sc->synhw.capPalmDetect && f->p <= sc->syninfo.max_pressure) || (sc->synhw.capPen && f->flags & PSM_FINGER_IS_PEN))) { @@ -3076,6 +3615,7 @@ * [min_pressure; max_pressure] * - pen aren't supported but PSM_FINGER_IS_PEN is set */ + VLOG(2, (LOG_DEBUG, "synaptics: palm detected! (%d)\n", f->w)); return (1); } return (0); @@ -3767,27 +4307,30 @@ nfingers = (pb->ipacket[0] & 0xc0) >> 6; if (nfingers == 3 && (pb->ipacket[3] & 0x80)) nfingers = 4; - mask = (1 << nfingers) - 1; - fn = ELANTECH_FINGER_SET_XYP(pb); + if (nfingers == 0) { + mask = (1 << nfingers) - 1; /* = 0x00 */ + break; + } + + /* Map 3-rd and 4-th fingers to first finger */ + mask = (1 << 1) - 1; /* = 0x01 */ + f[0] = ELANTECH_FINGER_SET_XYP(pb); if (sc->elanhw.haspressure) { - fn.w = ((pb->ipacket[0] & 0x30) >> 2) | + f[0].w = ((pb->ipacket[0] & 0x30) >> 2) | ((pb->ipacket[3] & 0x30) >> 4); } else { - fn.p = PSM_FINGER_DEFAULT_P; - fn.w = PSM_FINGER_DEFAULT_W; + f[0].p = PSM_FINGER_DEFAULT_P; + f[0].w = PSM_FINGER_DEFAULT_W; } /* * HW v2 dont report exact finger positions when 3 or more - * fingers are on touchpad. Use reported value as fingers - * position as it is required for tap detection + * fingers are on touchpad. */ if (nfingers > 2) - fn.flags = PSM_FINGER_FUZZY; + f[0].flags = PSM_FINGER_FUZZY; - for (id = 0; id < imin(nfingers, ELANTECH_MAX_FINGERS); id++) - f[id] = fn; break; case ELANTECH_PKT_V2_2FINGER: /*HW V2. Two finger touch */ @@ -3833,8 +4376,12 @@ * ------------------------------------------- */ nfingers = (pb->ipacket[0] & 0xc0) >> 6; - mask = (1 << nfingers) - 1; - id = nfingers - 1; + /* Map 3-rd finger to first finger */ + id = nfingers > 2 ? 0 : nfingers - 1; + mask = (1 << (id + 1)) - 1; + + if (nfingers == 0) + break; fn = ELANTECH_FINGER_SET_XYP(pb); fn.w = ((pb->ipacket[0] & 0x30) >> 2) | @@ -3842,15 +4389,11 @@ /* * HW v3 dont report exact finger positions when 3 or more - * fingers are on touchpad. Use reported value as fingers - * position as it is required for tap detection + * fingers are on touchpad. */ if (nfingers > 1) fn.flags = PSM_FINGER_FUZZY; - for (id = 0; id < imin(nfingers, ELANTECH_MAX_FINGERS); id++) - f[id] = fn; - if (nfingers == 2) { if (ELANTECH_PKT_IS_V3_HEAD(pb, sc->elanhw.hascrc)) { sc->elanaction.fingers[0] = fn; @@ -3858,6 +4401,7 @@ } else f[0] = sc->elanaction.fingers[0]; } + f[id] = fn; break; case ELANTECH_PKT_V4_STATUS: /* HW Version 4. Status packet */ @@ -3879,9 +4423,15 @@ mask = pb->ipacket[1] & 0x1f; nfingers = bitcount(mask); + if (sc->elanaction.mask_v4wait != 0) + VLOG(3, (LOG_DEBUG, "elantech: HW v4 status packet" + " when not all previous head packets received\n")); + + /* Bitmap of fingers to receive before gesture processing */ + sc->elanaction.mask_v4wait = mask & ~sc->elanaction.mask; + /* Skip "new finger is on touchpad" packets */ - if ((sc->elanaction.mask & mask) == sc->elanaction.mask && - (mask & ~sc->elanaction.mask)) { + if (sc->elanaction.mask_v4wait) { sc->elanaction.mask = mask; return (0); } @@ -3906,11 +4456,33 @@ mask = sc->elanaction.mask; nfingers = bitcount(mask); id = ((pb->ipacket[3] & 0xe0) >> 5) - 1; + fn = ELANTECH_FINGER_SET_XYP(pb); + fn.w =(pb->ipacket[0] & 0xf0) >> 4; - if (id >= 0 && id < ELANTECH_MAX_FINGERS) { - f[id] = ELANTECH_FINGER_SET_XYP(pb); - f[id].w = (pb->ipacket[0] & 0xf0) >> 4; + if (id < 0) + return (0); + + /* Packet is finger position update. Report it */ + if (sc->elanaction.mask_v4wait == 0) { + if (id < ELANTECH_MAX_FINGERS) + f[id] = fn; + break; } + + /* Remove finger from waiting bitmap and store into context */ + sc->elanaction.mask_v4wait &= ~(1 << id); + if (id < ELANTECH_MAX_FINGERS) + sc->elanaction.fingers[id] = fn; + + /* Wait for other fingers if needed */ + if (sc->elanaction.mask_v4wait != 0) + return (0); + + /* All new fingers are received. Report them from context */ + for (id = 0; id < ELANTECH_MAX_FINGERS; id++) + if (sc->elanaction.mask & (1 << id)) + f[id] = sc->elanaction.fingers[id]; + break; case ELANTECH_PKT_V4_MOTION: /* HW Version 4. Motion packet */ @@ -3977,7 +4549,12 @@ ((pb->ipacket[0] & 0x01) ? MOUSE_BUTTON1DOWN : 0) | ((pb->ipacket[0] & 0x02) ? MOUSE_BUTTON3DOWN : 0) | ((pb->ipacket[0] & 0x04) ? MOUSE_BUTTON2DOWN : 0); - +#ifdef EVDEV_SUPPORT + evdev_push_rel(sc->evdev_r, REL_X, *x); + evdev_push_rel(sc->evdev_r, REL_Y, -*y); + evdev_push_mouse_btn(sc->evdev_r, trackpoint_button); + evdev_sync(sc->evdev_r); +#endif ms->button = touchpad_button | trackpoint_button; return (0); @@ -4004,22 +4581,41 @@ ((pb->ipacket[0] & 0x02) ? MOUSE_BUTTON3DOWN : 0); } +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + for (id = 0; id < ELANTECH_MAX_FINGERS; id++) { + if (PSM_FINGER_IS_SET(f[id])) { + psm_push_mt_finger(sc, id, &f[id]); + /* Convert touch width to surface units */ + evdev_push_abs(sc->evdev_a, ABS_MT_TOUCH_MAJOR, + f[id].w * sc->elanhw.dptracex); + } + if (sc->elanaction.mask & (1 << id) && + !(mask & (1 << id))) + psm_release_mt_slot(sc->evdev_a, id); + } + evdev_push_key(sc->evdev_a, BTN_TOUCH, nfingers > 0); + evdev_push_nfingers(sc->evdev_a, nfingers); + if (nfingers > 0) { + if (PSM_FINGER_IS_SET(f[0])) + psm_push_st_finger(sc, &f[0]); + } else + evdev_push_abs(sc->evdev_a, ABS_PRESSURE, 0); + evdev_push_mouse_btn(sc->evdev_a, touchpad_button); + evdev_sync(sc->evdev_a); + } +#endif + ms->button = touchpad_button | trackpoint_button; - /* Palm detection doesn't terminate the current action. */ - if (!psmpalmdetect(sc, &f[0], nfingers)) { - /* Send finger 1 position to gesture processor */ - if (PSM_FINGER_IS_SET(f[0]) || PSM_FINGER_IS_SET(f[1]) || - nfingers == 0) - psmgestures(sc, &f[0], imin(nfingers, 3), ms); - /* Send fingers positions to movement smoothers */ - for (id = 0; id < PSM_FINGERS; id++) - if (PSM_FINGER_IS_SET(f[id]) || !(mask & (1 << id))) - psmsmoother(sc, &f[id], id, ms, x, y); - } else { - VLOG(2, (LOG_DEBUG, "elantech: palm detected! (%d)\n", - f[0].w)); - } + /* Send finger 1 position to gesture processor */ + if (PSM_FINGER_IS_SET(f[0]) || PSM_FINGER_IS_SET(f[1]) || + nfingers == 0) + psmgestures(sc, &f[0], imin(nfingers, 3), ms); + /* Send fingers positions to movement smoothers */ + for (id = 0; id < PSM_FINGERS; id++) + if (PSM_FINGER_IS_SET(f[id]) || !(mask & (1 << id))) + psmsmoother(sc, &f[id], id, ms, x, y); /* Store current finger positions in action context */ for (id = 0; id < ELANTECH_MAX_FINGERS; id++) { @@ -4030,6 +4626,13 @@ } sc->elanaction.mask = mask; + /* Palm detection doesn't terminate the current action. */ + if (psmpalmdetect(sc, &f[0], nfingers)) { + *x = *y = *z = 0; + ms->button = ms->obutton; + return (0); + } + /* Use the extra buttons as a scrollwheel */ if (ms->button & MOUSE_BUTTON4DOWN) *z = -1; @@ -4120,6 +4723,8 @@ struct psm_softc *sc = arg; packetbuf_t *pb; + psm_lock_assert(sc); + /* Invoke soft handler only when pqueue is empty. Otherwise it will be * invoked from psmintr soon with pqueue filled with real data */ if (sc->pqueue_start == sc->pqueue_end && @@ -4159,11 +4764,11 @@ struct psm_softc *sc = arg; mousestatus_t ms; packetbuf_t *pb; - int x, y, z, c, l, s; + int x, y, z, c, l; - getmicrouptime(&sc->lastsoftintr); + psm_lock_assert(sc); - s = spltty(); + getmicrouptime(&sc->lastsoftintr); do { pb = &sc->pqueue[sc->pqueue_start]; @@ -4343,6 +4948,44 @@ break; } +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE && + sc->hw.model != MOUSE_MODEL_ELANTECH && + sc->hw.model != MOUSE_MODEL_SYNAPTICS) { + if (x != 0) + evdev_push_rel(sc->evdev_r, EV_REL, x); + if (y != 0) + evdev_push_rel(sc->evdev_r, EV_REL, -y); + + switch (sc->hw.model) { + case MOUSE_MODEL_EXPLORER: + case MOUSE_MODEL_INTELLI: + case MOUSE_MODEL_NET: + case MOUSE_MODEL_NETSCROLL: + case MOUSE_MODEL_4DPLUS: + if (z != 0) + evdev_push_rel(sc->evdev_r, REL_WHEEL, -z); + break; + case MOUSE_MODEL_MOUSEMANPLUS: + case MOUSE_MODEL_4D: + switch (z) { + case 1: + case -1: + evdev_push_rel(sc->evdev_r, REL_WHEEL, -z); + break; + case 2: + case -2: + evdev_push_rel(sc->evdev_r, REL_HWHEEL, z / 2); + break; + } + break; + } + + evdev_push_mouse_btn(sc->evdev_r, ms.button); + evdev_sync(sc->evdev_r); + } +#endif + /* scale values */ if (sc->mode.accelfactor >= 1) { if (x != 0) { @@ -4421,25 +5064,23 @@ VLOG(2, (LOG_DEBUG, "softintr: callout set: %d ticks\n", tvtohz(&sc->idletimeout))); } - splx(s); } static int psmpoll(struct cdev *dev, int events, struct thread *td) { struct psm_softc *sc = dev->si_drv1; - int s; int revents = 0; /* Return true if a mouse event available */ - s = spltty(); + psm_lock(sc); if (events & (POLLIN | POLLRDNORM)) { if (sc->queue.count > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(td, &sc->rsel); } - splx(s); + psm_unlock(sc); return (revents); } @@ -5717,8 +6358,10 @@ if (arg == PROBE) { /* Create sysctl tree. */ + kbdc_unlock(sc->kbdc); synaptics_sysctl_create_tree(sc, "synaptics", "Synaptics TouchPad"); + kbdc_lock(sc->kbdc); sc->hw.buttons = buttons; } @@ -5991,7 +6634,9 @@ synaptics_passthrough_off(sc); if (arg == PROBE) { + kbdc_unlock(sc->kbdc); trackpoint_sysctl_create_tree(sc); + kbdc_lock(sc->kbdc); /* * Don't overwrite hwid and buttons when we are * a guest device. @@ -6155,7 +6800,10 @@ sc->synhw.capPassthrough = sc->elanhw.hastrackpoint; sc->synhw.capClickPad = sc->elanhw.isclickpad; sc->synhw.capMultiFinger = 1; - sc->synhw.capAdvancedGestures = 1; + if (sc->elanhw.issemimt) + sc->synhw.capAdvancedGestures = 1; + else + sc->synhw.capReportsV = 1; sc->synhw.capPalmDetect = 1; sc->synhw.capPen = 0; sc->synhw.capReportsMax = 1; @@ -6189,6 +6837,12 @@ /* Disable finger detection pressure threshold */ sc->syninfo.min_pressure = 1; + /* Adjust palm width to nearly match synaptics w=10 */ + sc->syninfo.max_width = 7; + + /* Elans often report double & triple taps as single event */ + sc->syninfo.tap_min_queue = 1; + /* Use full area of touchpad */ sc->syninfo.margin_top = 0; sc->syninfo.margin_right = 0; @@ -6233,8 +6887,17 @@ static const int ic2hw[] = /*IC: 0 1 2 3 4 5 6 7 8 9 a b c d e f */ { 0, 0, 2, 0, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0 }; + static const int fw_sizes[][3] = { + /* FW.vers MaxX MaxY */ + { 0x020030, 1152, 768 }, + { 0x020800, 1152, 768 }, + { 0x020b00, 1152, 768 }, + { 0x040215, 900, 500 }, + { 0x040216, 819, 405 }, + { 0x040219, 900, 500 }, + }; elantechhw_t elanhw; - int icversion, hwversion, dptracex, dptracey, id, resp[3], dpix, dpiy; + int icversion, hwversion, xtr, i, id, resp[3], dpix, dpiy; KBDC kbdc = sc->kbdc; VLOG(3, (LOG_DEBUG, "elantech: BEGIN init\n")); @@ -6285,8 +6948,8 @@ return (FALSE); } - elanhw.ntracesx = resp[1] - 1; - elanhw.ntracesy = resp[2] - 1; + elanhw.ntracesx = imax(resp[1], 3); + elanhw.ntracesy = imax(resp[2], 3); elanhw.hastrackpoint = (resp[0] & 0x80) != 0; /* Get the touchpad resolution */ @@ -6320,24 +6983,35 @@ * On HW v.3 touchpads it should be done after switching hardware * to real resolution mode (by setting bit 3 of reg10) */ + elanhw.dptracex = elanhw.dptracey = 64; + for (i = 0; i < nitems(fw_sizes); i++) { + if (elanhw.fwversion == fw_sizes[i][0]) { + elanhw.sizex = fw_sizes[i][1]; + elanhw.sizey = fw_sizes[i][2]; + goto found; + } + } if (elantech_cmd(kbdc, hwversion, ELANTECH_FW_ID, resp) != 0) { printf(" Failed to read touchpad size\n"); elanhw.sizex = 10000; /* Arbitrary high values to */ elanhw.sizey = 10000; /* prevent clipping in smoother */ } else if (hwversion == 2) { - dptracex = dptracey = 64; if ((elanhw.fwversion >> 16) == 0x14 && (resp[1] & 0x10) && !elantech_cmd(kbdc, hwversion, ELANTECH_SAMPLE, resp)) { - dptracex = resp[1] / 2; - dptracey = resp[2] / 2; + elanhw.dptracex = resp[1] / 2; + elanhw.dptracey = resp[2] / 2; } - elanhw.sizex = (elanhw.ntracesx - 1) * dptracex; - elanhw.sizey = (elanhw.ntracesy - 1) * dptracey; + xtr = ((elanhw.fwversion >> 8) == 0x0208) ? 1 : 2; + elanhw.sizex = (elanhw.ntracesx - xtr) * elanhw.dptracex; + elanhw.sizey = (elanhw.ntracesy - xtr) * elanhw.dptracey; } else { elanhw.sizex = (resp[0] & 0x0f) << 8 | resp[1]; elanhw.sizey = (resp[0] & 0xf0) << 4 | resp[2]; + xtr = (elanhw.sizex % (elanhw.ntracesx - 2) == 0) ? 2 : 1; + elanhw.dptracex = elanhw.sizex / (elanhw.ntracesx - xtr); + elanhw.dptracey = elanhw.sizey / (elanhw.ntracesy - xtr); } - +found: if (verbose >= 2) { printf(" Model information:\n"); printf(" MaxX: %d\n", elanhw.sizex); @@ -6346,6 +7020,8 @@ printf(" DpmmY: %d\n", elanhw.dpmmy); printf(" TracesX: %d\n", elanhw.ntracesx); printf(" TracesY: %d\n", elanhw.ntracesy); + printf(" DptraceX: %d\n", elanhw.dptracex); + printf(" DptraceY: %d\n", elanhw.dptracey); printf(" SemiMT: %d\n", elanhw.issemimt); printf(" Clickpad: %d\n", elanhw.isclickpad); printf(" Trackpoint: %d\n", elanhw.hastrackpoint); @@ -6360,7 +7036,9 @@ sc->hw.buttons = 3; /* Initialize synaptics movement smoother */ + kbdc_unlock(sc->kbdc); elantech_init_synaptics(sc); + kbdc_lock(sc->kbdc); for (id = 0; id < ELANTECH_MAX_FINGERS; id++) PSM_FINGER_RESET(sc->elanaction.fingers[id]); @@ -6424,6 +7102,9 @@ } DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0); +#ifdef EVDEV_SUPPORT +MODULE_DEPEND(psm, evdev, 1, 1, 1); +#endif #ifdef DEV_ISA