#include <sys/param.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/uhidev.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#define UTPMS_DATA_LEN 81
#define UTPMS_X_SENSORS 26
#define UTPMS_Y_SENSORS 16
#define UTPMS_SENSORS (UTPMS_X_SENSORS + UTPMS_Y_SENSORS)
struct utpms_dev {
int type;
#define FOUNTAIN 0x00
#define GEYSER1 0x01
#define GEYSER2 0x02
int noise;
int threshold;
int x_factor;
int x_sensors;
int y_factor;
int y_sensors;
uint16_t product;
uint16_t vendor;
};
static struct utpms_dev utpms_devices[] = {
#define UTPMS_TOUCHPAD(ttype, prod, x_fact, x_sens, y_fact) \
{ \
.type = (ttype), \
.vendor = USB_VENDOR_APPLE, \
.product = (prod), \
.noise = 16, \
.threshold = 5, \
.x_factor = (x_fact), \
.x_sensors = (x_sens), \
.y_factor = (y_fact), \
.y_sensors = 16 \
}
UTPMS_TOUCHPAD(FOUNTAIN, 0x030a, 69, 16, 52),
UTPMS_TOUCHPAD(GEYSER1, 0x030b, 69, 16, 52),
UTPMS_TOUCHPAD(FOUNTAIN, 0x020e, 85, 16, 57),
UTPMS_TOUCHPAD(FOUNTAIN, 0x020f, 85, 16, 57),
UTPMS_TOUCHPAD(GEYSER2, 0x0214, 90, 15, 107),
UTPMS_TOUCHPAD(GEYSER2, 0x0215, 90, 15, 107),
UTPMS_TOUCHPAD(GEYSER2, 0x0216, 90, 15, 107),
UTPMS_TOUCHPAD(FOUNTAIN, 0x020d, 71, 26, 68),
#undef UTPMS_TOUCHPAD
};
struct utpms_softc {
struct uhidev sc_hdev;
int sc_type;
int sc_datalen;
int sc_acc[UTPMS_SENSORS];
unsigned char sc_prev[UTPMS_SENSORS];
unsigned char sc_sample[UTPMS_SENSORS];
struct device *sc_wsmousedev;
int sc_noise;
int sc_threshold;
int sc_x;
int sc_x_factor;
int sc_x_raw;
int sc_x_sensors;
int sc_y;
int sc_y_factor;
int sc_y_raw;
int sc_y_sensors;
uint32_t sc_buttons;
uint32_t sc_status;
#define UTPMS_ENABLED 1
#define UTPMS_VALID 4
};
void utpms_intr(struct uhidev *, void *, unsigned int);
int utpms_enable(void *);
void utpms_disable(void *);
int utpms_ioctl(void *, unsigned long, caddr_t, int, struct proc *);
void reorder_sample(struct utpms_softc*, unsigned char *, unsigned char *);
int compute_delta(struct utpms_softc *, int *, int *, int *, uint32_t *);
int detect_pos(int *, int, int, int, int *, int *);
int smooth_pos(int, int, int);
const struct wsmouse_accessops utpms_accessops = {
utpms_enable,
utpms_ioctl,
utpms_disable,
};
int utpms_match(struct device *, void *, void *);
void utpms_attach(struct device *, struct device *, void *);
int utpms_detach(struct device *, int);
int utpms_activate(struct device *, int);
struct cfdriver utpms_cd = {
NULL, "utpms", DV_DULL
};
const struct cfattach utpms_ca = {
sizeof(struct utpms_softc), utpms_match, utpms_attach, utpms_detach,
utpms_activate,
};
int
utpms_match(struct device *parent, void *match, void *aux)
{
struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
usb_interface_descriptor_t *id;
int i;
if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
return (UMATCH_NONE);
id = usbd_get_interface_descriptor(uha->uaa->iface);
if (id == NULL ||
id->bInterfaceSubClass != UISUBCLASS_BOOT ||
id->bInterfaceProtocol != UIPROTO_BOOT_MOUSE)
return (UMATCH_NONE);
for (i = 0; i < nitems(utpms_devices); i++) {
if (uha->uaa->vendor == utpms_devices[i].vendor &&
uha->uaa->product == utpms_devices[i].product)
return (UMATCH_IFACECLASS);
}
return (UMATCH_NONE);
}
void
utpms_attach(struct device *parent, struct device *self, void *aux)
{
struct utpms_softc *sc = (struct utpms_softc *)self;
struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
struct wsmousedev_attach_args a;
struct utpms_dev *pd;
usb_device_descriptor_t *udd;
int i;
uint16_t vendor, product;
sc->sc_datalen = UTPMS_DATA_LEN;
sc->sc_hdev.sc_udev = uha->uaa->device;
usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0);
if ((udd = usbd_get_device_descriptor(uha->parent->sc_udev)) != NULL) {
product = UGETW(udd->idProduct);
vendor = UGETW(udd->idVendor);
for (i = 0; i < nitems(utpms_devices); i++) {
pd = &utpms_devices[i];
if (product == pd->product && vendor == pd->vendor) {
sc->sc_noise = pd->noise;
sc->sc_threshold = pd->threshold;
sc->sc_x_factor = pd->x_factor;
sc->sc_x_sensors = pd->x_sensors;
sc->sc_y_factor = pd->y_factor;
sc->sc_y_sensors = pd->y_sensors;
switch (pd->type) {
case FOUNTAIN:
printf(": Fountain");
break;
case GEYSER1:
printf(": Geyser");
break;
case GEYSER2:
sc->sc_type = GEYSER2;
sc->sc_datalen = 64;
sc->sc_y_sensors = 9;
printf(": Geyser 2");
break;
}
printf(" Trackpad\n");
break;
}
}
}
if (sc->sc_x_sensors <= 0 || sc->sc_x_sensors > UTPMS_X_SENSORS ||
sc->sc_y_sensors <= 0 || sc->sc_y_sensors > UTPMS_Y_SENSORS) {
printf(": unexpected sensors configuration (%d:%d)\n",
sc->sc_x_sensors, sc->sc_y_sensors);
return;
}
sc->sc_hdev.sc_intr = utpms_intr;
sc->sc_hdev.sc_parent = uha->parent;
sc->sc_hdev.sc_report_id = uha->reportid;
sc->sc_status = 0;
a.accessops = &utpms_accessops;
a.accesscookie = sc;
sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
}
int
utpms_detach(struct device *self, int flags)
{
struct utpms_softc *sc = (struct utpms_softc *)self;
int ret = 0;
if (sc->sc_wsmousedev != NULL)
ret = config_detach(sc->sc_wsmousedev, flags);
return (ret);
}
int
utpms_activate(struct device *self, int act)
{
struct utpms_softc *sc = (struct utpms_softc *)self;
int rv = 0;
if (act == DVACT_DEACTIVATE) {
if (sc->sc_wsmousedev != NULL)
rv = config_deactivate(sc->sc_wsmousedev);
}
return (rv);
}
int
utpms_enable(void *v)
{
struct utpms_softc *sc = v;
if (sc->sc_status & usbd_is_dying(sc->sc_hdev.sc_udev))
return (EIO);
if (sc->sc_status & UTPMS_ENABLED)
return (EBUSY);
sc->sc_status |= UTPMS_ENABLED;
sc->sc_status &= ~UTPMS_VALID;
sc->sc_buttons = 0;
bzero(sc->sc_sample, sizeof(sc->sc_sample));
return (uhidev_open(&sc->sc_hdev));
}
void
utpms_disable(void *v)
{
struct utpms_softc *sc = v;
if (!(sc->sc_status & UTPMS_ENABLED))
return;
sc->sc_status &= ~UTPMS_ENABLED;
uhidev_close(&sc->sc_hdev);
}
int
utpms_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p)
{
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = WSMOUSE_TYPE_USB;
return (0);
}
return (-1);
}
void
utpms_intr(struct uhidev *addr, void *ibuf, unsigned int len)
{
struct utpms_softc *sc = (struct utpms_softc *)addr;
unsigned char *data;
int dx, dy, dz, i, s;
uint32_t buttons;
if (len != sc->sc_datalen)
return;
data = ibuf;
buttons = !!data[sc->sc_datalen - 1];
reorder_sample(sc, sc->sc_sample, data);
if (!(sc->sc_status & UTPMS_VALID)) {
sc->sc_status |= UTPMS_VALID;
sc->sc_x = sc->sc_y = -1;
sc->sc_x_raw = sc->sc_y_raw = -1;
memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev));
bzero(sc->sc_acc, sizeof(sc->sc_acc));
return;
}
for (i = 0; i < UTPMS_SENSORS; i++) {
sc->sc_acc[i] +=
(signed char)(sc->sc_sample[i] - sc->sc_prev[i]);
if (sc->sc_acc[i] < 0)
sc->sc_acc[i] = 0;
}
memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev));
dx = dy = dz = 0;
if (!compute_delta(sc, &dx, &dy, &dz, &buttons))
return;
if ((dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) &&
sc->sc_wsmousedev != NULL) {
s = spltty();
WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, -dy, dz, 0);
splx(s);
}
sc->sc_buttons = buttons;
}
void
reorder_sample(struct utpms_softc *sc, unsigned char *to, unsigned char *from)
{
int i;
if (sc->sc_type == GEYSER2) {
int j;
bzero(to, UTPMS_SENSORS);
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
to[i] = from[j];
to[i + 1] = from[j + 1];
}
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
to[UTPMS_X_SENSORS + i] = from[j];
to[UTPMS_X_SENSORS + i + 1] = from[j + 1];
}
} else {
for (i = 0; i < 8; i++) {
to[i] = from[5 * i + 2];
to[i + 8] = from[5 * i + 4];
to[i + 16] = from[5 * i + 42];
#if 0
if (i < 2)
to[i + 24] = from[5 * i + 44];
#endif
to[i + 26] = from[5 * i + 1];
to[i + 34] = from[5 * i + 3];
}
}
}
int
compute_delta(struct utpms_softc *sc, int *dx, int *dy, int *dz,
uint32_t * buttons)
{
int x_det, y_det, x_raw, y_raw, x_fingers, y_fingers, fingers, x, y;
x_det = detect_pos(sc->sc_acc, sc->sc_x_sensors, sc->sc_threshold,
sc->sc_x_factor, &x_raw, &x_fingers);
y_det = detect_pos(sc->sc_acc + UTPMS_X_SENSORS, sc->sc_y_sensors,
sc->sc_threshold, sc->sc_y_factor,
&y_raw, &y_fingers);
fingers = max(x_fingers, y_fingers);
if (x_det == 0 && y_det == 0) {
bzero(sc->sc_acc, sizeof(sc->sc_acc));
sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1;
} else if (x_det > 0 && y_det > 0) {
switch (fingers) {
case 1:
if (sc->sc_x_raw >= 0) {
sc->sc_x_raw = (3 * sc->sc_x_raw + x_raw) / 4;
sc->sc_y_raw = (3 * sc->sc_y_raw + y_raw) / 4;
if (sc->sc_x >= 0) {
x = smooth_pos(sc->sc_x, sc->sc_x_raw,
sc->sc_noise);
y = smooth_pos(sc->sc_y, sc->sc_y_raw,
sc->sc_noise);
*dx = x - sc->sc_x;
*dy = y - sc->sc_y;
sc->sc_x = x;
sc->sc_y = y;
} else {
sc->sc_x = sc->sc_x_raw;
sc->sc_y = sc->sc_y_raw;
}
} else {
sc->sc_x_raw = x_raw;
sc->sc_y_raw = y_raw;
}
break;
case 2:
if (*buttons == 1)
*buttons = 4;
break;
case 3:
if (*buttons == 1)
*buttons = 2;
break;
}
}
return (1);
}
int
smooth_pos(int pos_old, int pos_raw, int noise)
{
int ad, delta;
delta = pos_raw - pos_old;
ad = abs(delta);
if (ad < noise / 2)
delta = 0;
else if (ad < noise)
delta /= 4;
else if (ad < 2 * noise)
delta /= 2;
return (pos_old + delta);
}
int
detect_pos(int *sensors, int n_sensors, int threshold, int fact,
int *pos_ret, int *fingers_ret)
{
int i, w, s;
*fingers_ret = 0;
w = s = 0;
for (i = 0; i < n_sensors; i++) {
if (sensors[i] >= threshold) {
if (i == 0 || sensors[i - 1] < threshold)
*fingers_ret += 1;
s += sensors[i] - threshold;
w += (sensors[i] - threshold) * i;
}
}
if (s > 0)
*pos_ret = w * fact / s;
return (s);
}