#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/hid/hid.h>
#include <dev/hid/hidmsvar.h>
#ifdef HIDMS_DEBUG
#define DPRINTF(x) do { if (hidmsdebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (hidmsdebug>(n)) printf x; } while (0)
int hidmsdebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define HIDMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
#define MOUSE_FLAGS_MASK (HIO_CONST | HIO_RELATIVE)
#define NOTMOUSE(f) (((f) & MOUSE_FLAGS_MASK) != HIO_RELATIVE)
void
hidms_stylus_hid_parse(struct hidms *ms, struct hid_data *d,
struct hid_location *loc_stylus_btn)
{
struct hid_item h;
while (hid_get_item(d, &h)) {
if (h.kind == hid_endcollection)
break;
if (h.kind != hid_input || (h.flags & HIO_CONST) != 0)
continue;
#ifdef HIDMS_DEBUG
printf("stylus usage: 0x%x\n", h.usage);
#endif
switch (h.usage) {
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_SWITCH):
DPRINTF(("Stylus usage tip set\n"));
if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
break;
loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
ms->sc_flags |= HIDMS_TIP;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_BARREL_SWITCH):
DPRINTF(("Stylus usage barrel set\n"));
if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
break;
loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
ms->sc_flags |= HIDMS_BARREL;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS,
HUD_SECONDARY_BARREL_SWITCH):
DPRINTF(("Stylus usage secondary barrel set\n"));
if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
break;
loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
ms->sc_flags |= HIDMS_SEC_BARREL;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_IN_RANGE):
DPRINTF(("Stylus usage in range set\n"));
if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
break;
loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_QUALITY):
DPRINTF(("Stylus usage quality set\n"));
if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
break;
loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_X):
DPRINTF(("Stylus usage x set\n"));
ms->sc_loc_x = h.loc;
ms->sc_tsscale.minx = h.logical_minimum;
ms->sc_tsscale.maxx = h.logical_maximum;
ms->sc_flags |= HIDMS_ABSX;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_Y):
DPRINTF(("Stylus usage y set\n"));
ms->sc_loc_y = h.loc;
ms->sc_tsscale.miny = h.logical_minimum;
ms->sc_tsscale.maxy = h.logical_maximum;
ms->sc_flags |= HIDMS_ABSY;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_PRESSURE):
DPRINTF(("Stylus usage pressure set\n"));
ms->sc_loc_z = h.loc;
ms->sc_tsscale.minz = h.logical_minimum;
ms->sc_tsscale.maxz = h.logical_maximum;
ms->sc_flags |= HIDMS_Z;
break;
case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_DISTANCE):
DPRINTF(("Stylus usage distance set\n"));
ms->sc_loc_w = h.loc;
ms->sc_tsscale.minw = h.logical_minimum;
ms->sc_tsscale.maxw = h.logical_maximum;
ms->sc_flags |= HIDMS_W;
break;
default:
#ifdef HIDMS_DEBUG
printf("Unknown stylus usage: 0x%x\n",
h.usage);
#endif
break;
}
}
}
void
hidms_pad_buttons_hid_parse(struct hidms *ms, struct hid_data *d,
struct hid_location *loc_pad_btn)
{
struct hid_item h;
while (hid_get_item(d, &h)) {
if (h.kind == hid_endcollection)
break;
if (h.kind == hid_input && (h.flags & HIO_CONST) != 0 &&
h.usage == HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS,
HUD_WACOM_PAD_BUTTONS00 | ms->sc_num_pad_buttons)) {
if (ms->sc_num_pad_buttons >= MAX_BUTTONS)
break;
loc_pad_btn[ms->sc_num_pad_buttons++] = h.loc;
}
}
}
int
hidms_wacom_setup(struct device *self, struct hidms *ms, void *desc, int dlen)
{
struct hid_data *hd;
int i;
struct hid_location loc_pad_btn[MAX_BUTTONS];
struct hid_location loc_stylus_btn[MAX_BUTTONS];
ms->sc_flags = 0;
ms->sc_loc_x.size = 0;
ms->sc_loc_y.size = 0;
ms->sc_loc_z.size = 0;
ms->sc_loc_w.size = 0;
if ((hd = hid_get_collection_data(desc, dlen,
HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER),
HCOLL_APPLICATION))) {
DPRINTF(("found the global collection\n"));
hid_end_parse(hd);
if ((hd = hid_get_collection_data(desc, dlen,
HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_STYLUS),
HCOLL_PHYSICAL))) {
DPRINTF(("found stylus collection\n"));
hidms_stylus_hid_parse(ms, hd, loc_stylus_btn);
hid_end_parse(hd);
}
if ((hd = hid_get_collection_data(desc, dlen,
HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TABLET_FKEYS),
HCOLL_PHYSICAL))) {
DPRINTF(("found tablet keys collection\n"));
hidms_pad_buttons_hid_parse(ms, hd, loc_pad_btn);
hid_end_parse(hd);
}
#ifdef notyet
if ((hd = hid_get_collection_data(desc, dlen,
HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_BATTERY),
HCOLL_PHYSICAL))) {
DPRINTF(("found battery collection\n"));
hid_end_parse(hd);
}
#endif
}
for (i = 0; i < ms->sc_num_stylus_buttons; i++)
memcpy(&ms->sc_loc_btn[i], &loc_stylus_btn[i],
sizeof(struct hid_location));
if (ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons >= MAX_BUTTONS)
ms->sc_num_pad_buttons =
MAX_BUTTONS - ms->sc_num_stylus_buttons;
for (; i < ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons; i++)
memcpy(&ms->sc_loc_btn[i], &loc_pad_btn[i],
sizeof(struct hid_location));
ms->sc_num_buttons = i;
DPRINTF(("Button information\n"));
#ifdef HIDMS_DEBUG
for (i = 0; i < ms->sc_num_buttons; i++)
printf("size: 0x%x, pos: 0x%x, count: 0x%x\n",
ms->sc_loc_btn[i].size, ms->sc_loc_btn[i].pos,
ms->sc_loc_btn[i].count);
#endif
return 0;
}
int
hidms_setup(struct device *self, struct hidms *ms, uint32_t quirks,
int id, void *desc, int dlen)
{
struct hid_item h;
struct hid_data *d;
uint32_t flags;
int i, wheel, twheel;
ms->sc_device = self;
ms->sc_rawmode = 1;
ms->sc_flags = quirks;
if (quirks & HIDMS_WACOM_SETUP)
return hidms_wacom_setup(self, ms, desc, dlen);
if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), id,
hid_input, &ms->sc_loc_x, &flags))
ms->sc_loc_x.size = 0;
switch (flags & MOUSE_FLAGS_MASK) {
case 0:
ms->sc_flags |= HIDMS_ABSX;
break;
case HIO_RELATIVE:
break;
default:
printf("\n%s: X report 0x%04x not supported\n",
self->dv_xname, flags);
return ENXIO;
}
if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), id,
hid_input, &ms->sc_loc_y, &flags))
ms->sc_loc_y.size = 0;
switch(flags & MOUSE_FLAGS_MASK) {
case 0:
ms->sc_flags |= HIDMS_ABSY;
break;
case HIO_RELATIVE:
break;
default:
printf("\n%s: Y report 0x%04x not supported\n",
self->dv_xname, flags);
return ENXIO;
}
wheel = hid_locate(desc, dlen,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), id,
hid_input, &ms->sc_loc_z, &flags);
if (wheel == 0)
twheel = hid_locate(desc, dlen,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), id,
hid_input, &ms->sc_loc_z, &flags);
else
twheel = 0;
if (wheel || twheel) {
if (NOTMOUSE(flags)) {
DPRINTF(("\n%s: Wheel report 0x%04x not supported\n",
self->dv_xname, flags));
ms->sc_loc_z.size = 0;
} else {
ms->sc_flags |= HIDMS_Z;
ms->sc_flags ^= HIDMS_REVZ;
}
if (hid_locate(desc, dlen,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
hid_input, &ms->sc_loc_w, &flags)) {
if (NOTMOUSE(flags)) {
DPRINTF(("\n%s: Z report 0x%04x not supported\n",
self->dv_xname, flags));
ms->sc_loc_w.size = 0;
}
else
ms->sc_flags |= HIDMS_W;
} else if (hid_locate(desc, dlen,
HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), id, hid_input,
&ms->sc_loc_w, &flags)) {
ms->sc_flags |= HIDMS_W;
}
} else if (hid_locate(desc, dlen,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
hid_input, &ms->sc_loc_z, &flags)) {
if (NOTMOUSE(flags)) {
DPRINTF(("\n%s: Z report 0x%04x not supported\n",
self->dv_xname, flags));
ms->sc_loc_z.size = 0;
} else {
ms->sc_flags |= HIDMS_Z;
}
}
if (twheel) {
ms->sc_loc_w = ms->sc_loc_z;
ms->sc_loc_w.pos = ms->sc_loc_w.pos + 8;
ms->sc_flags |= HIDMS_W | HIDMS_LEADINGBYTE;
ms->sc_flags ^= HIDMS_REVW;
}
for (i = 1; i <= MAX_BUTTONS; i++)
if (!hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, i), id,
hid_input, &ms->sc_loc_btn[i - 1], NULL))
break;
ms->sc_num_buttons = i - 1;
if (ms->sc_flags & HIDMS_VENDOR_BUTTONS) {
for (i = 1; ms->sc_num_buttons < MAX_BUTTONS; i++) {
if (!hid_locate(desc, dlen,
HID_USAGE2(HUP_MICROSOFT, i), id, hid_input,
&ms->sc_loc_btn[ms->sc_num_buttons], NULL))
break;
ms->sc_num_buttons++;
}
}
if (ms->sc_num_buttons < MAX_BUTTONS &&
hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
HUD_TIP_SWITCH), id, hid_input,
&ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
ms->sc_flags |= HIDMS_TIP;
ms->sc_num_buttons++;
}
if (ms->sc_num_buttons < MAX_BUTTONS &&
hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
HUD_ERASER), id, hid_input,
&ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
ms->sc_flags |= HIDMS_ERASER;
ms->sc_num_buttons++;
}
if (ms->sc_num_buttons < MAX_BUTTONS &&
hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
HUD_BARREL_SWITCH), id, hid_input,
&ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
ms->sc_flags |= HIDMS_BARREL;
ms->sc_num_buttons++;
}
if (ms->sc_flags & HIDMS_MS_BAD_CLASS) {
ms->sc_flags = HIDMS_Z | HIDMS_SPUR_BUT_UP;
ms->sc_num_buttons = 3;
ms->sc_loc_x.pos = 16;
ms->sc_loc_y.pos = 24;
ms->sc_loc_z.pos = 32;
ms->sc_loc_btn[0].pos = 8;
ms->sc_loc_btn[1].pos = 9;
ms->sc_loc_btn[2].pos = 10;
}
d = hid_start_parse(desc, dlen, hid_input);
while (hid_get_item(d, &h)) {
if (h.kind != hid_input ||
HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP)
continue;
DPRINTF(("hidms: usage=0x%x range %d..%d\n",
h.usage, h.logical_minimum, h.logical_maximum));
switch (HID_GET_USAGE(h.usage)) {
case HUG_X:
if (ms->sc_flags & HIDMS_ABSX) {
ms->sc_tsscale.minx = h.logical_minimum;
ms->sc_tsscale.maxx = h.logical_maximum;
}
break;
case HUG_Y:
if (ms->sc_flags & HIDMS_ABSY) {
ms->sc_tsscale.miny = h.logical_minimum;
ms->sc_tsscale.maxy = h.logical_maximum;
}
break;
}
}
hid_end_parse(d);
return 0;
}
void
hidms_attach(struct hidms *ms, const struct wsmouse_accessops *ops)
{
struct wsmousedev_attach_args a;
#ifdef HIDMS_DEBUG
int i;
#endif
printf(": %d button%s",
ms->sc_num_buttons, ms->sc_num_buttons == 1 ? "" : "s");
switch (ms->sc_flags & (HIDMS_Z | HIDMS_W)) {
case HIDMS_Z:
printf(", Z dir");
break;
case HIDMS_W:
printf(", W dir");
break;
case HIDMS_Z | HIDMS_W:
printf(", Z and W dir");
break;
}
if (ms->sc_flags & HIDMS_TIP)
printf(", tip");
if (ms->sc_flags & HIDMS_BARREL)
printf(", barrel");
if (ms->sc_flags & HIDMS_ERASER)
printf(", eraser");
printf("\n");
#ifdef HIDMS_DEBUG
DPRINTF(("hidms_attach: ms=%p\n", ms));
DPRINTF(("hidms_attach: X\t%d/%d\n",
ms->sc_loc_x.pos, ms->sc_loc_x.size));
DPRINTF(("hidms_attach: Y\t%d/%d\n",
ms->sc_loc_y.pos, ms->sc_loc_y.size));
if (ms->sc_flags & HIDMS_Z)
DPRINTF(("hidms_attach: Z\t%d/%d\n",
ms->sc_loc_z.pos, ms->sc_loc_z.size));
if (ms->sc_flags & HIDMS_W)
DPRINTF(("hidms_attach: W\t%d/%d\n",
ms->sc_loc_w.pos, ms->sc_loc_w.size));
for (i = 1; i <= ms->sc_num_buttons; i++) {
DPRINTF(("hidms_attach: B%d\t%d/%d\n",
i, ms->sc_loc_btn[i - 1].pos, ms->sc_loc_btn[i - 1].size));
}
#endif
a.accessops = ops;
a.accesscookie = ms->sc_device;
ms->sc_wsmousedev = config_found(ms->sc_device, &a, wsmousedevprint);
}
int
hidms_detach(struct hidms *ms, int flags)
{
int rv = 0;
DPRINTF(("hidms_detach: ms=%p flags=%d\n", ms, flags));
if (ms->sc_wsmousedev != NULL)
rv = config_detach(ms->sc_wsmousedev, flags);
return (rv);
}
void
hidms_input(struct hidms *ms, uint8_t *data, u_int len)
{
int dx, dy, dz, dw;
u_int32_t buttons = 0;
int i, s;
DPRINTFN(5,("hidms_input: len=%d\n", len));
if (ms->sc_flags & HIDMS_LEADINGBYTE) {
if (*data++ == 0x02)
return;
} else if (ms->sc_flags & HIDMS_SPUR_BUT_UP) {
if (*data == 0x14 || *data == 0x15)
return;
}
dx = hid_get_data(data, len, &ms->sc_loc_x);
dy = -hid_get_data(data, len, &ms->sc_loc_y);
dz = hid_get_data(data, len, &ms->sc_loc_z);
dw = hid_get_data(data, len, &ms->sc_loc_w);
if (ms->sc_flags & HIDMS_ABSY)
dy = -dy;
if (ms->sc_flags & HIDMS_REVZ)
dz = -dz;
if (ms->sc_flags & HIDMS_REVW)
dw = -dw;
if (ms->sc_tsscale.swapxy && !ms->sc_rawmode) {
int tmp = dx;
dx = dy;
dy = tmp;
}
if (!ms->sc_rawmode &&
(ms->sc_tsscale.maxx - ms->sc_tsscale.minx) != 0 &&
(ms->sc_tsscale.maxy - ms->sc_tsscale.miny) != 0) {
dx = ((dx - ms->sc_tsscale.minx) * ms->sc_tsscale.resx) /
(ms->sc_tsscale.maxx - ms->sc_tsscale.minx);
dy = ((dy - ms->sc_tsscale.miny) * ms->sc_tsscale.resy) /
(ms->sc_tsscale.maxy - ms->sc_tsscale.miny);
}
for (i = 0; i < ms->sc_num_buttons; i++)
if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
buttons |= (1 << HIDMS_BUT(i));
if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
buttons != ms->sc_buttons) {
DPRINTFN(10, ("hidms_input: x:%d y:%d z:%d w:%d buttons:0x%x\n",
dx, dy, dz, dw, buttons));
ms->sc_buttons = buttons;
if (ms->sc_wsmousedev != NULL) {
s = spltty();
if (ms->sc_flags & HIDMS_ABSX) {
wsmouse_set(ms->sc_wsmousedev,
WSMOUSE_ABS_X, dx, 0);
dx = 0;
}
if (ms->sc_flags & HIDMS_ABSY) {
wsmouse_set(ms->sc_wsmousedev,
WSMOUSE_ABS_Y, dy, 0);
dy = 0;
}
WSMOUSE_INPUT(ms->sc_wsmousedev,
buttons, dx, dy, dz, dw);
splx(s);
}
}
}
int
hidms_enable(struct hidms *ms)
{
if (ms->sc_enabled)
return EBUSY;
ms->sc_enabled = 1;
ms->sc_buttons = 0;
return 0;
}
int
hidms_ioctl(struct hidms *ms, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
switch (cmd) {
case WSMOUSEIO_SCALIBCOORDS:
if (!(wsmc->minx >= -32768 && wsmc->maxx >= -32768 &&
wsmc->miny >= -32768 && wsmc->maxy >= -32768 &&
wsmc->resx >= 0 && wsmc->resy >= 0 &&
wsmc->minx < 32768 && wsmc->maxx < 32768 &&
wsmc->miny < 32768 && wsmc->maxy < 32768 &&
(wsmc->maxx - wsmc->minx) != 0 &&
(wsmc->maxy - wsmc->miny) != 0 &&
wsmc->resx < 32768 && wsmc->resy < 32768 &&
wsmc->swapxy >= 0 && wsmc->swapxy <= 1 &&
wsmc->samplelen >= 0 && wsmc->samplelen <= 1))
return (EINVAL);
ms->sc_tsscale.minx = wsmc->minx;
ms->sc_tsscale.maxx = wsmc->maxx;
ms->sc_tsscale.miny = wsmc->miny;
ms->sc_tsscale.maxy = wsmc->maxy;
ms->sc_tsscale.swapxy = wsmc->swapxy;
ms->sc_tsscale.resx = wsmc->resx;
ms->sc_tsscale.resy = wsmc->resy;
ms->sc_rawmode = wsmc->samplelen;
return 0;
case WSMOUSEIO_GCALIBCOORDS:
wsmc->minx = ms->sc_tsscale.minx;
wsmc->maxx = ms->sc_tsscale.maxx;
wsmc->miny = ms->sc_tsscale.miny;
wsmc->maxy = ms->sc_tsscale.maxy;
wsmc->swapxy = ms->sc_tsscale.swapxy;
wsmc->resx = ms->sc_tsscale.resx;
wsmc->resy = ms->sc_tsscale.resy;
wsmc->samplelen = ms->sc_rawmode;
return 0;
case WSMOUSEIO_GTYPE:
if (ms->sc_flags & HIDMS_ABSX && ms->sc_flags & HIDMS_ABSY) {
*(u_int *)data = WSMOUSE_TYPE_TPANEL;
return 0;
}
default:
return -1;
}
}
void
hidms_disable(struct hidms *ms)
{
ms->sc_enabled = 0;
}