Page MenuHomeFreeBSD

D10207.id.diff
No OneTemporary

D10207.id.diff

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 <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#if defined(__amd64__)
#include <machine/clock.h>
@@ -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 <sys/param.h>
#include <sys/systm.h>
@@ -81,7 +82,6 @@
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/uio.h>
-#include <sys/libkern.h>
#include <sys/limits.h>
#include <sys/mouse.h>
@@ -91,6 +91,11 @@
#include <isa/isavar.h>
#endif
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/evdev.h>
+#include <dev/evdev/input.h>
+#endif
+
#include <dev/atkbdc/atkbdcreg.h>
#include <dev/atkbdc/psm.h>
@@ -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,274 @@
/* 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 for userland software compatibility */
+#define PS2_MOUSE_VENDOR 0x0002
+#define PS2_MOUSE_GENERIC_PRODUCT 0x0001
+#define PS2_MOUSE_SYNAPTICS_NAME "SynPS/2 Synaptics TouchPad"
+#define PS2_MOUSE_SYNAPTICS_PRODUCT 0x0007
+#define PS2_MOUSE_TRACKPOINT_NAME "TPPS/2 IBM TrackPoint"
+#define PS2_MOUSE_TRACKPOINT_PRODUCT 0x000A
+#define PS2_MOUSE_ELANTECH_NAME "ETPS/2 Elantech Touchpad"
+#define PS2_MOUSE_ELANTECH_ST_NAME "ETPS/2 Elantech TrackPoint"
+#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, int model_code)
+{
+ struct psm_softc *sc = device_get_softc(dev);
+ struct evdev_dev *evdev_r;
+ int error, i, nbuttons, nwheels, product;
+ bool is_pointing_stick;
+ const char *name;
+
+ name = model_name(model_code);
+ nbuttons = sc->hw.buttons;
+ product = PS2_MOUSE_GENERIC_PRODUCT;
+ nwheels = 0;
+ is_pointing_stick = false;
+
+ switch (model_code) {
+ case MOUSE_MODEL_TRACKPOINT:
+ name = PS2_MOUSE_TRACKPOINT_NAME;
+ product = PS2_MOUSE_TRACKPOINT_PRODUCT;
+ nbuttons = 3;
+ is_pointing_stick = true;
+ break;
+
+ case MOUSE_MODEL_ELANTECH:
+ name = PS2_MOUSE_ELANTECH_ST_NAME;
+ product = PS2_MOUSE_ELANTECH_PRODUCT;
+ nbuttons = 3;
+ is_pointing_stick = true;
+ break;
+
+ case MOUSE_MODEL_MOUSEMANPLUS:
+ case MOUSE_MODEL_4D:
+ nwheels = 2;
+ break;
+
+ case MOUSE_MODEL_EXPLORER:
+ case MOUSE_MODEL_INTELLI:
+ case MOUSE_MODEL_NET:
+ case MOUSE_MODEL_NETSCROLL:
+ case MOUSE_MODEL_4DPLUS:
+ nwheels = 1;
+ break;
+ }
+
+ 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, product, 0);
+ evdev_set_methods(evdev_r, sc, &psm_ev_methods_r);
+
+ evdev_support_prop(evdev_r, INPUT_PROP_POINTER);
+ if (is_pointing_stick)
+ evdev_support_prop(evdev_r, INPUT_PROP_POINTING_STICK);
+ 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;
+ int error, i, guest_model;
+
+ evdev_a = evdev_alloc();
+ evdev_set_name(evdev_a, PS2_MOUSE_SYNAPTICS_NAME);
+ 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_model = sc->tpinfo.sysctl_tree != NULL ?
+ MOUSE_MODEL_TRACKPOINT : MOUSE_MODEL_GENERIC;
+ error = psm_register(dev, guest_model);
+ }
+ 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, PS2_MOUSE_ELANTECH_NAME);
+ 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, MOUSE_MODEL_ELANTECH);
+ if (error)
+ evdev_free(evdev_a);
+ else
+ sc->evdev_a = evdev_a;
+ return (error);
+}
+#endif
+
static int
psmattach(device_t dev)
{
@@ -1586,16 +1932,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 +1954,24 @@
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;
+
+ default:
+ error = psm_register(dev, sc->hw.model);
+ }
+
+ if (error)
+ return (error);
+#endif
+
/* Some touchpad devices need full reinitialization after suspend. */
switch (sc->hw.model) {
case MOUSE_MODEL_SYNAPTICS:
@@ -1655,6 +2020,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 +2032,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 +2134,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 +2228,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 +2239,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 +2286,6 @@
* so long as the mouse will accept the DISABLE command.
*/
}
- splx(s);
/* stop the watchdog timer */
callout_stop(&sc->callout);
@@ -1834,16 +2329,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 +2402,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 +2442,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 +2456,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 +2480,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 +2494,8 @@
dropqueue(struct psm_softc *sc)
{
+ psm_lock_assert(sc);
+
sc->queue.count = 0;
sc->queue.head = 0;
sc->queue.tail = 0;
@@ -2025,6 +2510,8 @@
flushpackets(struct psm_softc *sc)
{
+ psm_lock_assert(sc);
+
dropqueue(sc);
bzero(&sc->pqueue, sizeof(sc->pqueue));
}
@@ -2052,7 +2539,8 @@
error = EIO;
}
- kbdc_lock(sc->kbdc, FALSE);
+ kbdc_unlock(sc->kbdc);
+ psm_unlock(sc);
return (error);
}
@@ -2075,14 +2563,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 +2596,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 +2643,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 +2673,7 @@
((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
break;
}
- splx(s);
+ psm_unlock(sc);
break;
case OLD_MOUSE_SETMODE:
@@ -2260,12 +2753,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 +2773,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 +2781,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 +2789,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 +2912,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 +2967,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 +3073,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 +3137,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 +3363,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 +3482,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 +3551,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 +3626,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 +3641,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 +4333,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 +4402,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 +4415,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 +4427,7 @@
} else
f[0] = sc->elanaction.fingers[0];
}
+ f[id] = fn;
break;
case ELANTECH_PKT_V4_STATUS: /* HW Version 4. Status packet */
@@ -3879,9 +4449,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 +4482,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 +4575,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 +4607,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 +4652,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 +4749,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 +4790,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 +4974,41 @@
break;
}
+#ifdef EVDEV_SUPPORT
+ if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE &&
+ sc->hw.model != MOUSE_MODEL_ELANTECH &&
+ sc->hw.model != MOUSE_MODEL_SYNAPTICS) {
+ evdev_push_rel(sc->evdev_r, EV_REL, x);
+ 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:
+ 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 +5087,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);
}
@@ -5054,7 +5718,7 @@
"Enable two finger scrolling");
/* hw.psm.synaptics.min_pressure. */
- sc->syninfo.min_pressure = 16;
+ sc->syninfo.min_pressure = 32;
SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
"min_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
@@ -5717,8 +6381,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 +6657,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 +6823,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 +6860,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 +6910,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 +6971,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 +7006,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 +7043,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 +7059,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 +7125,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

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 12, 3:13 AM (14 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29562054
Default Alt Text
D10207.id.diff (65 KB)

Event Timeline