Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F147450264
D10207.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
65 KB
Referenced Files
None
Subscribers
None
D10207.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D10207: psm(4): evdev support + psm&atkbdc locking
Attached
Detach File
Event Timeline
Log In to Comment