/* Copyright 2003 Tematic Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* mouse interface */ #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include "Global/RISCOS.h" #include "Global/Keyboard.h" #include "Global/Pointer.h" #include "usbmodhead.h" #include "swis.h" #include "debuglib/debuglib.h" #include "callx/callx.h" #include <sys/callout.h> #include <sys/ioctl.h> #include "dev/usb/usb.h" #include "dev/usb/usbhid.h" #include "dev/usb/usbdi.h" #include "dev/usb/usbdi_util.h" #include "dev/usb/usbdivar.h" #include <dev/usb/usbdevs.h> #include <dev/usb/usb_quirks.h> #include <dev/usb/hid.h> #include "usbmouse.h" #include "wimplib.h" extern void ums_enable (void*); extern void ums_disable (void*); extern int umsdebug = 0; extern struct messages mod_messages; extern struct cfattach ums_ca; static int relx = 0, rely = 0, relz = 0; static bool enabled = false; #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) void ums_intr (usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); struct ums_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; 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_btn; int sc_enabled; int flags; /* device configuration */ #define UMS_Z 0x01 /* z direction available */ #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */ #define UMS_REVZ 0x04 /* Z-axis is reversed */ int nbuttons; #define MAX_BUTTONS 31 /* chosen because sc_buttons is u_int32_t */ u_int32_t sc_buttons; /* mouse button status */ char sc_dying; /* list of ukbd softcs */ TAILQ_ENTRY(ums_softc) link_ms; }; TAILQ_HEAD(umslist, ums_softc) allums = TAILQ_HEAD_INITIALIZER(allums); extern void remove_all_mice (void) { struct ums_softc* sc; TAILQ_FOREACH(sc, &allums, link_ms) { detach_mouse ((struct device*) sc); } } static int match_mouse (struct usb_attach_arg *uaa) { usb_interface_descriptor_t *id; int size, ret; void *desc; usbd_status err; dprintf (("", "Trying ums attach\n")); if (uaa->iface == NULL) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL || id->bInterfaceClass != UICLASS_HID) { dprintf (("", "failed class match: id == %p\n", id)); return (UMATCH_NONE); } err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV); if (err) { dprintf (("", "failed to get report\n")); return (UMATCH_NONE); } if (hid_is_collection(desc, size, 0, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) ret = UMATCH_IFACECLASS; else ret = UMATCH_NONE; free(desc); dprintf (("", "ums attach returning: %d\n", ret)); return (ret); } static void do_attach_mouse (struct ums_softc* sc, struct usb_attach_arg *uaa) { usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; usbd_status err; char devinfo[1024]; u_int32_t flags, quirks; int i, wheel; struct hid_location loc_btn; sc->sc_udev = uaa->device; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; dprintf(("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass)); ed = usbd_interface2endpoint_descriptor(iface, 0); if (ed == NULL) { logprintf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } dprintf(("", "ums_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", ed->bmAttributes & UE_XFERTYPE, UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { logprintf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } quirks = usbd_get_quirks(uaa->device)->uq_flags; if (quirks & UQ_MS_REVZ) sc->flags |= UMS_REVZ; if (quirks & UQ_SPUR_BUT_UP) sc->flags |= UMS_SPUR_BUT_UP; err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV); if (err) USB_ATTACH_ERROR_RETURN; if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 0, hid_input, &sc->sc_loc_x, &flags)) { logprintf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { logprintf("%s: X report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 0, hid_input, &sc->sc_loc_y, &flags)) { logprintf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { logprintf("%s: Y report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } /* Try to guess the Z activator: first check Z, then WHEEL. */ wheel = 0; if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 0, hid_input, &sc->sc_loc_z, &flags) || (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 0, 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 */ } else { sc->flags |= UMS_Z; /* Wheels need the Z axis reversed. */ if (wheel) sc->flags ^= UMS_REVZ; } } /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 0, hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons); if (!sc->sc_loc_btn) { logprintf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } dprintf(("", "%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev), sc->nbuttons, sc->flags & UMS_Z ? " and Z dir." : "")); for (i = 1; i <= sc->nbuttons; i++) hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 0, hid_input, &sc->sc_loc_btn[i-1], 0); sc->sc_isize = hid_report_size(desc, size, hid_input, 0); sc->sc_ibuf = malloc(sc->sc_isize); if (sc->sc_ibuf == NULL) { logprintf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); free(sc->sc_loc_btn); USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; free(desc); #ifdef USB_DEBUG dprintf(("", "ums_attach: sc=%p\n", sc)); dprintf(("", "ums_attach: X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); dprintf(("", "ums_attach: Y\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); if (sc->flags & UMS_Z) dprintf(("", "ums_attach: Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size)); for (i = 1; i <= sc->nbuttons; i++) { dprintf(("", "ums_attach: B%d\t%d/%d\n", i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } dprintf(("", "ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); #endif usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); USB_ATTACH_SUCCESS_RETURN; } struct device* attach_mouse (struct device* parent, void* aux) { struct device* softc; struct ums_softc* sc; dprintf (("", "Trying match on usb mouse\n")); /* First see if we match */ if (match_mouse (aux) == UMATCH_NONE) { dprintf (("", "Failed to match\n")); return NULL; } /* If so, allocate memory for the device and attach ourselves. */ softc = malloc (sizeof *sc); if (softc == 0) { dprintf (("", "Couldn't allocate memory for mouse device\n")); return NULL; } memset (softc, 0, sizeof *sc); strcpy (softc->dv_xname, "USBMouse"Module_VersionString); softc->dv_cfdata = (void*) 3; // mouse /* enable */ sc = (struct ums_softc*) softc; do_attach_mouse (sc, aux); dprintf (("", "Matched mouse\n")); sc->sc_enabled = 1; sc->sc_buttons = 0; /* set idle rate to 0 */ usbd_set_idle (sc->sc_iface, 0, 0); int err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); if (err) { dprintf(("", "ums_enable: usbd_open_pipe_intr failed, error=%d\n", err)); sc->sc_enabled = 0; } else _swix (OS_Pointer, _INR(0,1), 1, PointerDevice_USB); TAILQ_INSERT_TAIL (&allums, sc, link_ms); return softc; } int detach_mouse (struct device* ms) { struct ums_softc* sc= (struct ums_softc*) ms; int rv; usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); dprintf (("", "detaching mouse, buttons = %x", sc->sc_buttons)); if (sc->sc_buttons & 1) { dprintf (("", "releaseing 1\n")); _swix (OS_CallAVector, _INR(0,1) | _IN(9), KeyV_KeyUp, KeyNo_LeftMouse, KEYV); } if (sc->sc_buttons & 2) { dprintf (("", "releaseing 2\n")); _swix (OS_CallAVector, _INR(0,1) | _IN(9), KeyV_KeyUp, KeyNo_RightMouse, KEYV); } if (sc->sc_buttons & 4) { dprintf (("", "releaseing 4\n")); _swix (OS_CallAVector, _INR(0,1) | _IN(9), KeyV_KeyUp, KeyNo_CentreMouse, KEYV); } TAILQ_REMOVE (&allums, sc, link_ms); if (rv == 0) { free(sc->sc_loc_btn); free(sc->sc_ibuf); } free (ms); return 0; } int pointerv (_kernel_swi_regs* r, void* pw) { _kernel_oserror* e; (void) pw; switch (r->r[0]) { case PointerReason_Request: if (r->r[1] == PointerDevice_USB) { /* Turn off interrupts while updating */ _kernel_irqs_off (); r->r[2] = relx; r->r[3] = rely; relx = rely = 0; _kernel_irqs_on (); } break; #define RECORD "\x0\x0\x0\x0\x0\x0\x0\x0\x7USB Mouse" case PointerReason_Identify: { struct pointer_device { struct pointer_device *next; uint32_t flags; char typenname[32]; } *p; e = _swix (OS_Module, _IN(0) | _IN(3) | _OUT(2), 6, sizeof *p, &p); if (!e) { p->next = (struct pointer_device *) r->r[1]; p->flags = 0; p->typenname[0] = PointerDevice_USB; _swix (MessageTrans_Lookup, _INR(0,3), &mod_messages, "Mouse:USB mouse", &p->typenname[1], 31); r->r[1] = (int) p; } break; } case PointerReason_Selected: if (r->r[1] == PointerDevice_USB) { dprintf (("", "USB mouse enabled\n")); enabled = true; } else { dprintf (("", "USB mouse disabled\n")); enabled = false; } break; } return 1; } static _kernel_oserror *zscroll_handler ( _kernel_swi_regs * r, void * pw, void * h ) { (void) r; (void) pw; (void) h; while (relz != 0) { int b[64]; _swix (Wimp_GetPointerInfo, _IN(1), b); b[0] = b[3]; _swix (Wimp_GetWindowState, _IN(1), b); b[8] = 0; b[9] = relz > 0? -1: 1; relz += relz > 0? -1: 1; _swix (Wimp_SendMessage, _INR(0,2), 10, b, b[0]); } return NULL; } void ums_intr ( usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status ) { struct ums_softc *sc = addr; u_char *ibuf = sc->sc_ibuf; int dx, dy, dz, i, b = 0; uint8_t change; if (status == USBD_CANCELLED) return; if (status) { dprintf(("", "ums_intr: status=%d\n", status)); usbd_clear_endpoint_stall_async(sc->sc_intrpipe); 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); if (sc->flags & UMS_REVZ) dz = -dz; relx += dx; rely += dy; relz += 5 * dz; /* 5 makes things move a bit faster */ if (enabled) { _swix(OS_CallAVector, _INR(0,3) | _IN(9), PointerReason_Report, PointerDevice_USB, dx, dy, PointerV); if (relz != 0) { callx_add_callback (zscroll_handler, 0); } } for (i = 0; i < sc->nbuttons; i++) if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) b |= (1 << i); #ifdef USB_DEBUG if (umsdebug > 5) dprintf(("", "data.relx = %d, data.rely = %d, relx = %d, rely = %d, buttons = %x\n", dx, dy, relx, rely, b)); #endif if ((change = sc->sc_buttons ^ b) != 0) { sc->sc_buttons = b; #ifdef USB_DEBUG if (umsdebug > 5) dprintf (("", "change = %x, enabled = %d\n", change, enabled)); #endif if (enabled) { if (change & 1) _swix (OS_CallAVector, _INR(0,1) | _IN(9), (b & 1)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_LeftMouse, KEYV); if (change & 2) _swix (OS_CallAVector, _INR(0,1) | _IN(9), (b & 2)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_RightMouse, KEYV); if (change & 4) _swix (OS_CallAVector, _INR(0,1) | _IN(9), (b & 4)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_CentreMouse, KEYV); } } } int wsmousedevprint (void * v, const char * c) { (void) v; (void) c; return 0; }