Add support for USB Microsoft Intellimouse

PR:		kern/70607
Submitted by:	Matt Wright <matt@consultmatt.co.uk>
MFC after:	1 week
This commit is contained in:
Julian Elischer 2004-12-12 05:34:20 +00:00
parent db4b31b04c
commit c29b0a0f7e
3 changed files with 52 additions and 21 deletions

View File

@ -104,7 +104,7 @@ struct ums_softc {
u_char *sc_ibuf;
u_int8_t sc_iid;
int sc_isize;
struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t;
struct hid_location *sc_loc_btn;
usb_callout_t callout_handle; /* for spurious button ups */
@ -114,6 +114,7 @@ struct ums_softc {
int flags; /* device configuration */
#define UMS_Z 0x01 /* z direction available */
#define UMS_T 0x02 /* aa direction available (tilt) */
#define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
int nbuttons;
#define MAX_BUTTONS 7 /* chosen because sc_buttons is u_char */
@ -140,7 +141,7 @@ Static void ums_intr(usbd_xfer_handle xfer,
usbd_private_handle priv, usbd_status status);
Static void ums_add_to_queue(struct ums_softc *sc,
int dx, int dy, int dz, int buttons);
int dx, int dy, int dz, int dt, int buttons);
Static void ums_add_to_queue_timeout(void *priv);
Static int ums_enable(void *);
@ -269,6 +270,8 @@ USB_ATTACH(ums)
if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
hid_input, &sc->sc_loc_z, &flags) ||
hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
hid_input, &sc->sc_loc_z, &flags) ||
hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
hid_input, &sc->sc_loc_z, &flags)) {
if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
@ -277,6 +280,17 @@ USB_ATTACH(ums)
}
}
/* The Microsoft Wireless Intellimouse 2.0 reports it's wheel
* using 0x0048 (i've called it HUG_TWHEEL) and seems to expect
* you to know that the byte after the wheel is the tilt axis.
* There are no other HID axis descriptors other than X,Y and
* TWHEEL */
if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
hid_input, &sc->sc_loc_t, &flags)) {
sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8;
sc->flags |= UMS_T;
}
/* figure out the number of buttons */
for (i = 1; i <= MAX_BUTTONS; i++)
if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
@ -290,8 +304,9 @@ USB_ATTACH(ums)
USB_ATTACH_ERROR_RETURN;
}
printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev),
sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : "");
printf("%s: %d buttons%s%s.\n", USBDEVNAME(sc->sc_dev),
sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "",
sc->flags & UMS_T?" and a TILT dir": "");
for (i = 1; i <= sc->nbuttons; i++)
hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
@ -408,15 +423,15 @@ ums_intr(xfer, addr, status)
{
struct ums_softc *sc = addr;
u_char *ibuf;
int dx, dy, dz;
int dx, dy, dz, dt;
u_char buttons = 0;
int i;
#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
DPRINTFN(5, ("ums_intr: data = %02x %02x %02x %02x %02x %02x\n",
sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2], sc->sc_ibuf[3], sc->sc_ibuf[4], sc->sc_ibuf[5]));
if (status == USBD_CANCELLED)
return;
@ -425,32 +440,44 @@ ums_intr(xfer, addr, status)
DPRINTF(("ums_intr: status=%d\n", status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
return;
if(status != USBD_IOERROR)
return;
}
ibuf = sc->sc_ibuf;
if (sc->sc_iid) {
if (*ibuf++ != sc->sc_iid)
return;
ibuf++;
}
/* The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of
* data compared to most USB mice. This byte frequently switches
* from 0x01 (usual state) to 0x02. I assume it is to allow
* extra, non-standard, reporting (say battery-life). However
* at the same time it generates a left-click message on the button
* byte which causes spurious left-click's where there shouldn't be.
* This should sort that. */
if ((sc->sc_ibuf != ibuf) && (sc->sc_ibuf[0] == 0x02))
return;
dx = hid_get_data(ibuf, &sc->sc_loc_x);
dy = -hid_get_data(ibuf, &sc->sc_loc_y);
dz = -hid_get_data(ibuf, &sc->sc_loc_z);
dt = -hid_get_data(ibuf, &sc->sc_loc_t);
for (i = 0; i < sc->nbuttons; i++)
if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
buttons |= (1 << UMS_BUT(i));
if (dx || dy || dz || (sc->flags & UMS_Z)
if (dx || dy || dz || dt || (sc->flags & UMS_Z)
|| buttons != sc->status.button) {
DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
dx, dy, dz, buttons));
DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d aa:%d buttons:0x%x\n",
dx, dy, dz, dt, buttons));
sc->status.button = buttons;
sc->status.dx += dx;
sc->status.dy += dy;
sc->status.dz += dz;
sc->status.dt += dt;
/* Discard data in case of full buffer */
if (sc->qcount == sizeof(sc->qbuf)) {
DPRINTF(("Buffer full, discarded packet"));
@ -466,13 +493,13 @@ ums_intr(xfer, addr, status)
* In any other case we delete the timeout event.
*/
if (sc->flags & UMS_SPUR_BUT_UP &&
dx == 0 && dy == 0 && dz == 0 && buttons == 0) {
dx == 0 && dy == 0 && dz == 0 && dt == 0 && buttons == 0) {
usb_callout(sc->callout_handle, MS_TO_TICKS(50 /*msecs*/),
ums_add_to_queue_timeout, (void *) sc);
} else {
usb_uncallout(sc->callout_handle,
ums_add_to_queue_timeout, (void *) sc);
ums_add_to_queue(sc, dx, dy, dz, buttons);
ums_add_to_queue(sc, dx, dy, dz, dt, buttons);
}
}
}
@ -484,12 +511,12 @@ ums_add_to_queue_timeout(void *priv)
int s;
s = splusb();
ums_add_to_queue(sc, 0, 0, 0, 0);
ums_add_to_queue(sc, 0, 0, 0, 0, 0);
splx(s);
}
Static void
ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons)
{
/* Discard data in case of full buffer */
if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
@ -503,6 +530,8 @@ ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
if (dy < -256) dy = -256;
if (dz > 126) dz = 126;
if (dz < -128) dz = -128;
if (dt > 126) dt = 126;
if (dt < -128) dt = -128;
sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
@ -550,7 +579,7 @@ ums_enable(v)
sc->qhead = sc->qtail = 0;
sc->status.flags = 0;
sc->status.button = sc->status.obutton = 0;
sc->status.dx = sc->status.dy = sc->status.dz = 0;
sc->status.dx = sc->status.dy = sc->status.dz = sc->status.dt = 0;
callout_handle_init((struct callout_handle *)&sc->callout_handle);
@ -807,10 +836,10 @@ ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
*status = sc->status;
sc->status.obutton = sc->status.button;
sc->status.button = 0;
sc->status.dx = sc->status.dy = sc->status.dz = 0;
sc->status.dx = sc->status.dy = sc->status.dz = sc->status.dt = 0;
splx(s);
if (status->dx || status->dy || status->dz)
if (status->dx || status->dy || status->dz || status->dt)
status->flags |= MOUSE_POSCHANGED;
if (status->button != status->obutton)
status->flags |= MOUSE_BUTTONSCHANGED;

View File

@ -123,6 +123,7 @@ typedef struct usb_hid_descriptor {
#define HUG_VBRY 0x0044
#define HUG_VBRZ 0x0045
#define HUG_VNO 0x0046
#define HUG_TWHEEL 0x0048 // M$ Wireless Intellimouse Wheel
#define HUG_SYSTEM_CONTROL 0x0080
#define HUG_SYSTEM_POWER_DOWN 0x0081
#define HUG_SYSTEM_SLEEP 0x0082

View File

@ -58,6 +58,7 @@ typedef struct mousestatus {
int dx; /* x movement */
int dy; /* y movement */
int dz; /* z movement */
int dt; /* left right tilt axis */
} mousestatus_t;
/* button */