diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2000-09-07 21:58:51 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2000-09-07 21:58:51 +0000 |
commit | 216146b8941991902a5e4257103c3a1076144783 (patch) | |
tree | 5dd14cf3e3cb8a118c098b6452c4a14bfadeebee | |
parent | f630c2ec20179ef4ce7ef3fd5f885a2942243259 (diff) |
add ums mouse support from NetBSD
-rw-r--r-- | sys/dev/usb/files.usb | 7 | ||||
-rw-r--r-- | sys/dev/usb/ums.c | 468 |
2 files changed, 474 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 7b6f36f6e46..d13cc0e7b96 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.13 2000/09/06 22:46:01 rahnds Exp $ +# $OpenBSD: files.usb,v 1.14 2000/09/07 21:58:50 matthieu Exp $ # $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -67,6 +67,11 @@ device umodem: ucombus attach umodem at uhub file dev/usb/umodem.c umodem +# Mice +device ums: wsmousedev +attach ums at uhub +file dev/usb/ums.c ums + # Diamond Multimedia Rio 500 device urio attach urio at uhub diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c new file mode 100644 index 00000000000..52e46da3987 --- /dev/null +++ b/sys/dev/usb/ums.c @@ -0,0 +1,468 @@ +/* $NetBSD: ums.c,v 1.44 2000/06/01 14:29:01 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * HID spec: http://www.usb.org/developers/data/usbhid10.pdf + */ + +/* XXX complete SPUR_UP change */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.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/usbdevs.h> +#include <dev/usb/usb_quirks.h> +#include <dev/usb/hid.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (umsdebug) logprintf x +#define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x +int umsdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) + +#define UMSUNIT(s) (minor(s)) + +#define PS2LBUTMASK x01 +#define PS2RBUTMASK x02 +#define PS2MBUTMASK x04 +#define PS2BUTMASK 0x0f + +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 */ + struct device *sc_wsmousedev; + + char sc_dying; +}; + +#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) +#define MOUSE_FLAGS (HIO_RELATIVE) + +Static void ums_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); + +Static int ums_enable(void *); +Static void ums_disable(void *); +Static int ums_ioctl(void *, u_long, caddr_t, int, struct proc *); + +const struct wsmouse_accessops ums_accessops = { + ums_enable, + ums_ioctl, + ums_disable, +}; + +USB_DECLARE_DRIVER(ums); + +USB_MATCH(ums) +{ + USB_MATCH_START(ums, uaa); + usb_interface_descriptor_t *id; + int size, ret; + void *desc; + usbd_status err; + + if (uaa->iface == NULL) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (id == NULL || id->bInterfaceClass != UICLASS_HID) + return (UMATCH_NONE); + + err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + if (err) + return (UMATCH_NONE); + + if (hid_is_collection(desc, size, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) + ret = UMATCH_IFACECLASS; + else + ret = UMATCH_NONE; + + free(desc, M_TEMP); + return (ret); +} + +USB_ATTACH(ums) +{ + USB_ATTACH_START(ums, sc, uaa); + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + struct wsmousedev_attach_args a; + 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; + printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), + devinfo, id->bInterfaceClass, id->bInterfaceSubClass); + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (ed == NULL) { + printf("%s: could not read endpoint descriptor\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + + DPRINTFN(10,("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) { + printf("%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_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + if (err) + USB_ATTACH_ERROR_RETURN; + + if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, &sc->sc_loc_x, &flags)) { + printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + printf("%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), + hid_input, &sc->sc_loc_y, &flags)) { + printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + printf("%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), + hid_input, &sc->sc_loc_z, &flags) || + (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_WHEEL), + 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), + hid_input, &loc_btn, 0)) + break; + sc->nbuttons = i - 1; + sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, + M_USBDEV, M_NOWAIT); + if (!sc->sc_loc_btn) { + printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + + printf("%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), + hid_input, &sc->sc_loc_btn[i-1], 0); + + sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); + sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_NOWAIT); + if (sc->sc_ibuf == NULL) { + printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); + free(sc->sc_loc_btn, M_USBDEV); + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_ep_addr = ed->bEndpointAddress; + free(desc, M_TEMP); + +#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 + + a.accessops = &ums_accessops; + a.accesscookie = sc; + + sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; +} + +int +ums_activate(device_ptr_t self, enum devact act) +{ + struct ums_softc *sc = (struct ums_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + if (sc->sc_wsmousedev != NULL) + rv = config_deactivate(sc->sc_wsmousedev); + sc->sc_dying = 1; + break; + } + return (rv); +} + +USB_DETACH(ums) +{ + USB_DETACH_START(ums, sc); + int rv = 0; + + DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags)); + + /* No need to do reference counting of ums, wsmouse has all the goo. */ + if (sc->sc_wsmousedev != NULL) + rv = config_detach(sc->sc_wsmousedev, flags); + if (rv == 0) { + free(sc->sc_loc_btn, M_USBDEV); + free(sc->sc_ibuf, M_USBDEV); + } + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (rv); +} + +void +ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) +{ + struct ums_softc *sc = addr; + u_char *ibuf; + int dx, dy, dz; + u_int32_t buttons = 0; + int i; + int s; + + 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])); + + if (status == USBD_CANCELLED) + return; + + if (status) { + DPRINTF(("ums_intr: status=%d\n", status)); + usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + return; + } + + ibuf = sc->sc_ibuf; + if (sc->sc_iid != 0) { + if (*ibuf++ != sc->sc_iid) + 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; + for (i = 0; i < sc->nbuttons; i++) + if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) + buttons |= (1 << UMS_BUT(i)); + + if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) { + DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", + dx, dy, dz, buttons)); + sc->sc_buttons = buttons; + if (sc->sc_wsmousedev != NULL) { + s = spltty(); + wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, + WSMOUSE_INPUT_DELTA); + splx(s); + } + } +} + +Static int +ums_enable(void *v) +{ + struct ums_softc *sc = v; + + usbd_status err; + + DPRINTFN(1,("ums_enable: sc=%p\n", sc)); + + if (sc->sc_dying) + return (EIO); + + if (sc->sc_enabled) + return (EBUSY); + + sc->sc_enabled = 1; + sc->sc_buttons = 0; + + /* Set up interrupt pipe. */ + 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; + return (EIO); + } + return (0); +} + +Static void +ums_disable(void *v) +{ + struct ums_softc *sc = v; + + DPRINTFN(1,("ums_disable: sc=%p\n", sc)); +#ifdef DIAGNOSTIC + if (!sc->sc_enabled) { + printf("ums_disable: not enabled\n"); + return; + } +#endif + + /* Disable interrupts. */ + usbd_abort_pipe(sc->sc_intrpipe); + usbd_close_pipe(sc->sc_intrpipe); + + sc->sc_enabled = 0; +} + +Static int +ums_ioctl(void *v, u_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); +} |