ums.c revision 1.16
11.16Saugustss/*	$NetBSD: ums.c,v 1.16 1998/12/26 12:53:03 augustss Exp $	*/
21.1Saugustss
31.1Saugustss/*
41.1Saugustss * Copyright (c) 1998 The NetBSD Foundation, Inc.
51.1Saugustss * All rights reserved.
61.1Saugustss *
71.11Saugustss * This code is derived from software contributed to The NetBSD Foundation
81.11Saugustss * by Lennart Augustsson (augustss@carlstedt.se) at
91.11Saugustss * Carlstedt Research & Technology.
101.1Saugustss *
111.1Saugustss * Redistribution and use in source and binary forms, with or without
121.1Saugustss * modification, are permitted provided that the following conditions
131.1Saugustss * are met:
141.1Saugustss * 1. Redistributions of source code must retain the above copyright
151.1Saugustss *    notice, this list of conditions and the following disclaimer.
161.1Saugustss * 2. Redistributions in binary form must reproduce the above copyright
171.1Saugustss *    notice, this list of conditions and the following disclaimer in the
181.1Saugustss *    documentation and/or other materials provided with the distribution.
191.1Saugustss * 3. All advertising materials mentioning features or use of this software
201.1Saugustss *    must display the following acknowledgement:
211.1Saugustss *        This product includes software developed by the NetBSD
221.1Saugustss *        Foundation, Inc. and its contributors.
231.1Saugustss * 4. Neither the name of The NetBSD Foundation nor the names of its
241.1Saugustss *    contributors may be used to endorse or promote products derived
251.1Saugustss *    from this software without specific prior written permission.
261.1Saugustss *
271.1Saugustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
281.1Saugustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
291.1Saugustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
301.1Saugustss * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
311.1Saugustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
321.1Saugustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
331.1Saugustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
341.1Saugustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
351.1Saugustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
361.1Saugustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
371.1Saugustss * POSSIBILITY OF SUCH DAMAGE.
381.1Saugustss */
391.1Saugustss
401.1Saugustss#include <sys/param.h>
411.1Saugustss#include <sys/systm.h>
421.1Saugustss#include <sys/kernel.h>
431.1Saugustss#include <sys/malloc.h>
441.16Saugustss#if defined(__NetBSD__)
451.1Saugustss#include <sys/device.h>
461.1Saugustss#include <sys/ioctl.h>
471.16Saugustss#elif defined(__FreeBSD__)
481.16Saugustss#include <sys/module.h>
491.16Saugustss#include <sys/bus.h>
501.16Saugustss#include <sys/ioccom.h>
511.16Saugustss#include <sys/conf.h>
521.16Saugustss#endif
531.1Saugustss#include <sys/tty.h>
541.1Saugustss#include <sys/file.h>
551.1Saugustss#include <sys/select.h>
561.1Saugustss#include <sys/proc.h>
571.1Saugustss#include <sys/vnode.h>
581.1Saugustss#include <sys/poll.h>
591.1Saugustss
601.1Saugustss#include <dev/usb/usb.h>
611.1Saugustss#include <dev/usb/usbhid.h>
621.1Saugustss
631.1Saugustss#include <dev/usb/usbdi.h>
641.16Saugustss#include <dev/usb/usbdivar.h>
651.1Saugustss#include <dev/usb/usbdi_util.h>
661.1Saugustss#include <dev/usb/usbdevs.h>
671.1Saugustss#include <dev/usb/usb_quirks.h>
681.1Saugustss#include <dev/usb/hid.h>
691.1Saugustss
701.16Saugustss#if defined(__NetBSD__)
711.3Saugustss#include <dev/wscons/wsconsio.h>
721.3Saugustss#include <dev/wscons/wsmousevar.h>
731.16Saugustss#elif defined(__FreeBSD__)
741.16Saugustss#include <machine/mouse.h>
751.16Saugustss#endif
761.3Saugustss
771.1Saugustss#ifdef USB_DEBUG
781.1Saugustss#define DPRINTF(x)	if (umsdebug) printf x
791.1Saugustss#define DPRINTFN(n,x)	if (umsdebug>(n)) printf x
801.1Saugustssint	umsdebug = 0;
811.1Saugustss#else
821.1Saugustss#define DPRINTF(x)
831.1Saugustss#define DPRINTFN(n,x)
841.1Saugustss#endif
851.1Saugustss
861.16Saugustss#define UMSUNIT(s)	(minor(s)&0x1f)
871.16Saugustss
881.16Saugustss#define PS2LBUTMASK	x01
891.16Saugustss#define PS2RBUTMASK	x02
901.16Saugustss#define PS2MBUTMASK	x04
911.1Saugustss#define PS2BUTMASK 0x0f
921.1Saugustss
931.16Saugustss#define QUEUE_BUFSIZE	240	/* MUST be divisible by 3 _and_ 4 */
941.16Saugustss
951.1Saugustssstruct ums_softc {
961.16Saugustss	bdevice sc_dev;			/* base device */
971.1Saugustss	usbd_interface_handle sc_iface;	/* interface */
981.1Saugustss	usbd_pipe_handle sc_intrpipe;	/* interrupt pipe */
991.1Saugustss	int sc_ep_addr;
1001.1Saugustss
1011.1Saugustss	u_char *sc_ibuf;
1021.1Saugustss	u_int8_t sc_iid;
1031.1Saugustss	int sc_isize;
1041.16Saugustss	struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
1051.16Saugustss	struct hid_location *sc_loc_btn;
1061.1Saugustss
1071.16Saugustss	int sc_enabled;
1081.1Saugustss	int sc_disconnected;	/* device is gone */
1091.3Saugustss
1101.16Saugustss	int flags;		/* device configuration */
1111.16Saugustss#define UMS_Z		0x01	/* z direction available */
1121.16Saugustss	int nbuttons;
1131.16Saugustss
1141.16Saugustss#if defined(__NetBSD__)
1151.16Saugustss	u_char sc_buttons;	/* mouse button status */
1161.3Saugustss	struct device *sc_wsmousedev;
1171.16Saugustss#elif defined(__FreeBSD__)
1181.16Saugustss	u_char		qbuf[QUEUE_BUFSIZE];
1191.16Saugustss	u_char		dummy[100];		/* just for safety and for now */
1201.16Saugustss	int		qcount, qhead, qtail;
1211.16Saugustss	mousehw_t	hw;
1221.16Saugustss	mousemode_t	mode;
1231.16Saugustss	mousestatus_t	status;
1241.16Saugustss
1251.16Saugustss	int		state;
1261.16Saugustss#	  define	UMS_ASLEEP	0x01	/* readFromDevice is waiting */
1271.16Saugustss#	  define	UMS_SELECT	0x02	/* select is waiting */
1281.16Saugustss	struct selinfo	rsel;		/* process waiting in select */
1291.16Saugustss#endif
1301.1Saugustss};
1311.1Saugustss
1321.2Saugustss#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
1331.2Saugustss#define MOUSE_FLAGS (HIO_RELATIVE)
1341.2Saugustss
1351.1Saugustssvoid ums_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
1361.1Saugustssvoid ums_disco __P((void *));
1371.1Saugustss
1381.16Saugustssstatic int	ums_enable __P((void *));
1391.16Saugustssstatic void	ums_disable __P((void *));
1401.16Saugustss
1411.16Saugustss#if defined(__NetBSD__)
1421.16Saugustssstatic int	ums_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
1431.3Saugustss
1441.3Saugustssconst struct wsmouse_accessops ums_accessops = {
1451.3Saugustss	ums_enable,
1461.3Saugustss	ums_ioctl,
1471.3Saugustss	ums_disable,
1481.3Saugustss};
1491.3Saugustss
1501.16Saugustss#elif defined(__FreeBSD__)
1511.16Saugustssstatic d_open_t ums_open;
1521.16Saugustssstatic d_close_t ums_close;
1531.16Saugustssstatic d_read_t ums_read;
1541.16Saugustssstatic d_ioctl_t ums_ioctl;
1551.16Saugustssstatic d_poll_t ums_poll;
1561.16Saugustss
1571.16Saugustss#define UMS_CDEV_MAJOR	138	/* XXX NWH should be requested */
1581.16Saugustss
1591.16Saugustssstatic struct  cdevsw ums_cdevsw = {
1601.16Saugustss	ums_open,	ums_close,	ums_read,	nowrite,
1611.16Saugustss	ums_ioctl,	nostop,		nullreset,	nodevtotty,
1621.16Saugustss	ums_poll,	nommap,
1631.16Saugustss	NULL,		"ums_",		NULL,		-1
1641.16Saugustss};
1651.16Saugustss#endif
1661.1Saugustss
1671.16SaugustssUSB_DECLARE_DRIVER(ums);
1681.1Saugustss
1691.16SaugustssUSB_MATCH(ums)
1701.1Saugustss{
1711.16Saugustss	USB_MATCH_START(ums, uaa);
1721.1Saugustss	usb_interface_descriptor_t *id;
1731.1Saugustss	int size, ret;
1741.1Saugustss	void *desc;
1751.1Saugustss	usbd_status r;
1761.1Saugustss
1771.1Saugustss	if (!uaa->iface)
1781.1Saugustss		return (UMATCH_NONE);
1791.1Saugustss	id = usbd_get_interface_descriptor(uaa->iface);
1801.14Saugustss	if (!id || id->bInterfaceClass != UCLASS_HID)
1811.1Saugustss		return (UMATCH_NONE);
1821.1Saugustss
1831.1Saugustss	r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
1841.1Saugustss	if (r != USBD_NORMAL_COMPLETION)
1851.1Saugustss		return (UMATCH_NONE);
1861.1Saugustss
1871.1Saugustss	if (hid_is_collection(desc, size,
1881.1Saugustss			      HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
1891.1Saugustss		ret = UMATCH_IFACECLASS;
1901.1Saugustss	else
1911.1Saugustss		ret = UMATCH_NONE;
1921.1Saugustss
1931.1Saugustss	free(desc, M_TEMP);
1941.1Saugustss	return (ret);
1951.1Saugustss}
1961.1Saugustss
1971.16SaugustssUSB_ATTACH(ums)
1981.1Saugustss{
1991.16Saugustss	USB_ATTACH_START(ums, sc, uaa);
2001.1Saugustss	usbd_interface_handle iface = uaa->iface;
2011.1Saugustss	usb_interface_descriptor_t *id;
2021.1Saugustss	usb_endpoint_descriptor_t *ed;
2031.3Saugustss	struct wsmousedev_attach_args a;
2041.1Saugustss	int size;
2051.1Saugustss	void *desc;
2061.1Saugustss	usbd_status r;
2071.1Saugustss	char devinfo[1024];
2081.2Saugustss	u_int32_t flags;
2091.16Saugustss	int i;
2101.16Saugustss	struct hid_location loc_btn;
2111.1Saugustss
2121.1Saugustss	sc->sc_disconnected = 1;
2131.1Saugustss	sc->sc_iface = iface;
2141.1Saugustss	id = usbd_get_interface_descriptor(iface);
2151.1Saugustss	usbd_devinfo(uaa->device, 0, devinfo);
2161.16Saugustss	USB_ATTACH_SETUP;
2171.16Saugustss	printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
2181.12Saugustss	       devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
2191.1Saugustss	ed = usbd_interface2endpoint_descriptor(iface, 0);
2201.1Saugustss	if (!ed) {
2211.1Saugustss		printf("%s: could not read endpoint descriptor\n",
2221.16Saugustss		       USBDEVNAME(sc->sc_dev));
2231.16Saugustss		USB_ATTACH_ERROR_RETURN;
2241.1Saugustss	}
2251.1Saugustss
2261.15Saugustss	DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
2271.15Saugustss		     "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
2281.15Saugustss		     " bInterval=%d\n",
2291.15Saugustss		     ed->bLength, ed->bDescriptorType,
2301.15Saugustss		     ed->bEndpointAddress & UE_ADDR,
2311.15Saugustss		     ed->bEndpointAddress & UE_IN ? "in" : "out",
2321.15Saugustss		     ed->bmAttributes & UE_XFERTYPE,
2331.15Saugustss		     UGETW(ed->wMaxPacketSize), ed->bInterval));
2341.1Saugustss
2351.1Saugustss	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
2361.1Saugustss	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
2371.1Saugustss		printf("%s: unexpected endpoint\n",
2381.16Saugustss		       USBDEVNAME(sc->sc_dev));
2391.16Saugustss		USB_ATTACH_ERROR_RETURN;
2401.1Saugustss	}
2411.1Saugustss
2421.1Saugustss	r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
2431.1Saugustss	if (r != USBD_NORMAL_COMPLETION)
2441.16Saugustss		USB_ATTACH_ERROR_RETURN;
2451.16Saugustss
2461.2Saugustss	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
2471.2Saugustss		       hid_input, &sc->sc_loc_x, &flags)) {
2481.16Saugustss		printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev));
2491.16Saugustss		USB_ATTACH_ERROR_RETURN;
2501.16Saugustss	}
2511.16Saugustss	if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
2521.16Saugustss		printf("%s: X report 0x%04x not supported\n",
2531.16Saugustss		       USBDEVNAME(sc->sc_dev), flags);
2541.16Saugustss		USB_ATTACH_ERROR_RETURN;
2551.1Saugustss	}
2561.10Saugustss
2571.2Saugustss	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
2581.2Saugustss		       hid_input, &sc->sc_loc_y, &flags)) {
2591.16Saugustss		printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev));
2601.16Saugustss		USB_ATTACH_ERROR_RETURN;
2611.16Saugustss	}
2621.16Saugustss	if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
2631.16Saugustss		printf("%s: Y report 0x%04x not supported\n",
2641.16Saugustss		       USBDEVNAME(sc->sc_dev), flags);
2651.16Saugustss		USB_ATTACH_ERROR_RETURN;
2661.1Saugustss	}
2671.10Saugustss
2681.16Saugustss	/* try to guess the Z activator: first check Z, then WHEEL */
2691.6Saugustss	if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
2701.6Saugustss		       hid_input, &sc->sc_loc_z, &flags) ||
2711.6Saugustss	    hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
2721.6Saugustss		       hid_input, &sc->sc_loc_z, &flags)) {
2731.16Saugustss		if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
2741.16Saugustss			sc->sc_loc_z.size = 0;	/* Bad Z coord, ignore it */
2751.16Saugustss		} else {
2761.16Saugustss			sc->flags |= UMS_Z;
2771.16Saugustss		}
2781.6Saugustss	}
2791.6Saugustss
2801.16Saugustss	/* figure out the number of buttons, 7 is an arbitrary limit */
2811.16Saugustss	for (i = 1; i <= 7; i++)
2821.16Saugustss		if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
2831.16Saugustss				hid_input, &loc_btn, 0))
2841.16Saugustss			break;
2851.16Saugustss	sc->nbuttons = i - 1;
2861.16Saugustss	sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons,
2871.16Saugustss				M_USBDEV, M_NOWAIT);
2881.16Saugustss	if (!sc->sc_loc_btn)
2891.16Saugustss		USB_ATTACH_ERROR_RETURN;
2901.16Saugustss
2911.16Saugustss	printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev),
2921.16Saugustss	       sc->nbuttons, (sc->flags & UMS_Z? " and Z dir." : ""));
2931.16Saugustss
2941.16Saugustss	for (i = 1; i <= sc->nbuttons; i++)
2951.16Saugustss		hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
2961.16Saugustss				hid_input, &sc->sc_loc_btn[i-1], 0);
2971.1Saugustss
2981.1Saugustss	sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
2991.1Saugustss	sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
3001.16Saugustss	if (!sc->sc_ibuf) {
3011.16Saugustss		free(sc->sc_loc_btn, M_USB);
3021.16Saugustss		USB_ATTACH_ERROR_RETURN;
3031.16Saugustss	}
3041.1Saugustss
3051.1Saugustss	sc->sc_ep_addr = ed->bEndpointAddress;
3061.1Saugustss	sc->sc_disconnected = 0;
3071.1Saugustss	free(desc, M_TEMP);
3081.3Saugustss
3091.16Saugustss#ifdef USB_DEBUG
3101.16Saugustss	DPRINTF(("ums_attach: sc=%p\n", sc));
3111.16Saugustss	DPRINTF(("ums_attach: X\t%d/%d\n",
3121.16Saugustss		 sc->sc_loc_x.pos, sc->sc_loc_x.size));
3131.16Saugustss	DPRINTF(("ums_attach: Y\t%d/%d\n",
3141.16Saugustss		 sc->sc_loc_x.pos, sc->sc_loc_x.size));
3151.16Saugustss	if (sc->flags & UMS_Z)
3161.16Saugustss		DPRINTF(("ums_attach: Z\t%d/%d\n",
3171.16Saugustss			 sc->sc_loc_z.pos, sc->sc_loc_z.size));
3181.16Saugustss	for (i = 1; i <= sc->nbuttons; i++) {
3191.16Saugustss		DPRINTF(("ums_attach: B%d\t%d/%d\n",
3201.16Saugustss			 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
3211.16Saugustss	}
3221.16Saugustss	DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
3231.16Saugustss#endif
3241.16Saugustss
3251.16Saugustss#if defined(__NetBSD__)
3261.3Saugustss	a.accessops = &ums_accessops;
3271.3Saugustss	a.accesscookie = sc;
3281.3Saugustss
3291.3Saugustss	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
3301.16Saugustss#elif defined(__FreeBSD__)
3311.16Saugustss	sc->hw.buttons = 2;		/* XXX hw&mode values are bogus */
3321.16Saugustss	sc->hw.iftype = MOUSE_IF_PS2;
3331.16Saugustss	sc->hw.type = MOUSE_MOUSE;
3341.16Saugustss	if (sc->flags & UMS_Z)
3351.16Saugustss		sc->hw.model = MOUSE_MODEL_INTELLI;
3361.16Saugustss	else
3371.16Saugustss		sc->hw.model = MOUSE_MODEL_GENERIC;
3381.16Saugustss	sc->hw.hwid = 0;
3391.16Saugustss	sc->mode.protocol = MOUSE_PROTO_PS2;
3401.16Saugustss	sc->mode.rate = -1;
3411.16Saugustss	sc->mode.resolution = MOUSE_RES_DEFAULT;
3421.16Saugustss	sc->mode.accelfactor = 1;
3431.16Saugustss	sc->mode.level = 0;
3441.16Saugustss	if (sc->flags & UMS_Z) {
3451.16Saugustss		sc->mode.packetsize = MOUSE_INTELLI_PACKETSIZE;
3461.16Saugustss		sc->mode.syncmask[0] = 0xc8;
3471.16Saugustss	} else {
3481.16Saugustss		sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
3491.16Saugustss		sc->mode.syncmask[0] = 0xc0;
3501.16Saugustss	}
3511.16Saugustss	sc->mode.syncmask[1] = 0;
3521.16Saugustss
3531.16Saugustss	sc->status.flags = 0;
3541.16Saugustss	sc->status.button = sc->status.obutton = 0;
3551.16Saugustss	sc->status.dx = sc->status.dy = sc->status.dz = 0;
3561.16Saugustss
3571.16Saugustss	sc->rsel.si_flags = 0;
3581.16Saugustss	sc->rsel.si_pid = 0;
3591.16Saugustss#endif
3601.16Saugustss
3611.16Saugustss	USB_ATTACH_SUCCESS_RETURN;
3621.16Saugustss}
3631.16Saugustss
3641.16Saugustss
3651.16Saugustss#if defined(__FreeBSD__)
3661.16Saugustssstatic int
3671.16Saugustssums_detach(device_t self)
3681.16Saugustss{
3691.16Saugustss	struct ums_softc *sc = device_get_softc(self);
3701.16Saugustss	char *devinfo = (char *) device_get_desc(self);
3711.16Saugustss
3721.16Saugustss	if (devinfo) {
3731.16Saugustss		device_set_desc(self, NULL);
3741.16Saugustss		free(devinfo, M_USB);
3751.16Saugustss	}
3761.16Saugustss	free(sc->sc_loc_btn, M_USB);
3771.16Saugustss	free(sc->sc_ibuf, M_USB);
3781.16Saugustss
3791.16Saugustss	return 0;
3801.1Saugustss}
3811.16Saugustss#endif
3821.1Saugustss
3831.1Saugustssvoid
3841.1Saugustssums_disco(p)
3851.1Saugustss	void *p;
3861.1Saugustss{
3871.1Saugustss	struct ums_softc *sc = p;
3881.8Saugustss
3891.8Saugustss	DPRINTF(("ums_disco: sc=%p\n", sc));
3901.8Saugustss	usbd_abort_pipe(sc->sc_intrpipe);
3911.1Saugustss	sc->sc_disconnected = 1;
3921.1Saugustss}
3931.1Saugustss
3941.1Saugustssvoid
3951.1Saugustssums_intr(reqh, addr, status)
3961.1Saugustss	usbd_request_handle reqh;
3971.1Saugustss	usbd_private_handle addr;
3981.1Saugustss	usbd_status status;
3991.1Saugustss{
4001.1Saugustss	struct ums_softc *sc = addr;
4011.1Saugustss	u_char *ibuf;
4021.6Saugustss	int dx, dy, dz;
4031.16Saugustss	u_char buttons = 0;
4041.16Saugustss	int i;
4051.16Saugustss#if defined(__NetBSD__)
4061.16Saugustss#define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
4071.16Saugustss#elif defined(__FreeBSD__)
4081.16Saugustss#define UMS_BUT(i) (i)
4091.16Saugustss#endif
4101.1Saugustss
4111.1Saugustss	DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
4121.1Saugustss	DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
4131.1Saugustss		     sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
4141.1Saugustss
4151.1Saugustss	if (status == USBD_CANCELLED)
4161.1Saugustss		return;
4171.1Saugustss
4181.1Saugustss	if (status != USBD_NORMAL_COMPLETION) {
4191.1Saugustss		DPRINTF(("ums_intr: status=%d\n", status));
4201.7Saugustss		usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
4211.1Saugustss		return;
4221.1Saugustss	}
4231.1Saugustss
4241.1Saugustss	ibuf = sc->sc_ibuf;
4251.1Saugustss	if (sc->sc_iid) {
4261.1Saugustss		if (*ibuf++ != sc->sc_iid)
4271.1Saugustss			return;
4281.1Saugustss	}
4291.1Saugustss	dx =  hid_get_data(ibuf, &sc->sc_loc_x);
4301.1Saugustss	dy = -hid_get_data(ibuf, &sc->sc_loc_y);
4311.6Saugustss	dz =  hid_get_data(ibuf, &sc->sc_loc_z);
4321.16Saugustss	/* NWH Why are you modifying the button assignments here?
4331.16Saugustss	 * That's the purpose of a high level mouse driver
4341.16Saugustss	 */
4351.16Saugustss	for (i = 0; i < sc->nbuttons; i++)
4361.16Saugustss		if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
4371.16Saugustss			buttons |= (1 << UMS_BUT(i));
4381.4Saugustss
4391.16Saugustss#if defined(__NetBSD__)
4401.4Saugustss	if (dx || dy || buttons != sc->sc_buttons) {
4411.16Saugustss		DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
4421.16Saugustss			dx, dy, dz, buttons));
4431.4Saugustss		sc->sc_buttons = buttons;
4441.4Saugustss		if (sc->sc_wsmousedev)
4451.6Saugustss			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz);
4461.16Saugustss#elif defined(__FreeBSD__)
4471.16Saugustss	if (dx || dy || buttons != sc->status.button) {
4481.16Saugustss		DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
4491.16Saugustss			dx, dy, dz, buttons));
4501.16Saugustss
4511.16Saugustss		sc->status.button = buttons;
4521.16Saugustss		sc->status.dx += dx;
4531.16Saugustss		sc->status.dy += dy;
4541.16Saugustss		sc->status.dz += dz;
4551.16Saugustss
4561.16Saugustss		/* Discard data in case of full buffer */
4571.16Saugustss		if (sc->qcount == sizeof(sc->qbuf)) {
4581.16Saugustss			DPRINTF(("Buffer full, discarded packet"));
4591.16Saugustss			return;
4601.16Saugustss		}
4611.16Saugustss
4621.16Saugustss		sc->qbuf[sc->qhead] = MOUSE_PS2_SYNC;
4631.16Saugustss		if (dx < 0)
4641.16Saugustss			sc->qbuf[sc->qhead] |= MOUSE_PS2_XNEG;
4651.16Saugustss		if (dx > 255 || dx < -255)
4661.16Saugustss			sc->qbuf[sc->qhead] |= MOUSE_PS2_XOVERFLOW;
4671.16Saugustss		if (dy < 0)
4681.16Saugustss			sc->qbuf[sc->qhead] |= MOUSE_PS2_YNEG;
4691.16Saugustss		if (dy > 255 || dy < -255)
4701.16Saugustss			sc->qbuf[sc->qhead] |= MOUSE_PS2_YOVERFLOW;
4711.16Saugustss		sc->qbuf[sc->qhead++] |= buttons;
4721.16Saugustss		sc->qbuf[sc->qhead++] = dx;
4731.16Saugustss		sc->qbuf[sc->qhead++] = dy;
4741.16Saugustss		sc->qcount += 3;
4751.16Saugustss		if (sc->flags & UMS_Z) {
4761.16Saugustss			sc->qbuf[sc->qhead++] = dz;
4771.16Saugustss			sc->qcount++;
4781.16Saugustss		}
4791.16Saugustss#ifdef USB_DEBUG
4801.16Saugustss		if (sc->qhead > sizeof(sc->qbuf))
4811.16Saugustss			DPRINTF(("Buffer overrun! %d %d\n",
4821.16Saugustss				 sc->qhead, sizeof(sc->qbuf)));
4831.16Saugustss#endif
4841.16Saugustss		/* wrap round at end of buffer */
4851.16Saugustss		if (sc->qhead >= sizeof(sc->qbuf))
4861.16Saugustss			sc->qhead = 0;
4871.16Saugustss
4881.16Saugustss		/* someone waiting for data */
4891.16Saugustss		if (sc->state & UMS_ASLEEP)
4901.16Saugustss			wakeup(sc);
4911.16Saugustss		/* wake up any pending selects */
4921.16Saugustss		selwakeup(&sc->rsel);
4931.16Saugustss		sc->state &= ~UMS_SELECT;
4941.16Saugustss#endif
4951.1Saugustss	}
4961.1Saugustss}
4971.3Saugustss
4981.16Saugustss
4991.16Saugustssstatic int
5001.3Saugustssums_enable(v)
5011.3Saugustss	void *v;
5021.3Saugustss{
5031.3Saugustss	struct ums_softc *sc = v;
5041.16Saugustss
5051.3Saugustss	usbd_status r;
5061.3Saugustss
5071.3Saugustss	if (sc->sc_enabled)
5081.3Saugustss		return EBUSY;
5091.3Saugustss
5101.3Saugustss	sc->sc_enabled = 1;
5111.16Saugustss#if defined(__NetBSD__)
5121.4Saugustss	sc->sc_buttons = 0;
5131.16Saugustss#elif defined(__FreeBSD__)
5141.16Saugustss	sc->qcount = 0;
5151.16Saugustss	sc->qhead = sc->qtail = 0;
5161.16Saugustss#ifdef USB_DEBUG
5171.16Saugustss	if (sizeof(sc->qbuf) % 4 || sizeof(sc->qbuf) % 3) {
5181.16Saugustss		DPRINTF(("Buffer size not divisible by 3 or 4\n"));
5191.16Saugustss		return ENXIO;
5201.16Saugustss	}
5211.16Saugustss#endif
5221.16Saugustss	sc->status.flags = 0;
5231.16Saugustss	sc->status.button = sc->status.obutton = 0;
5241.16Saugustss	sc->status.dx = sc->status.dy = sc->status.dz = 0;
5251.16Saugustss#endif
5261.3Saugustss
5271.3Saugustss	/* Set up interrupt pipe. */
5281.3Saugustss	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
5291.3Saugustss				USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
5301.3Saugustss				sc->sc_ibuf, sc->sc_isize, ums_intr);
5311.3Saugustss	if (r != USBD_NORMAL_COMPLETION) {
5321.3Saugustss		DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
5331.3Saugustss			 r));
5341.3Saugustss		sc->sc_enabled = 0;
5351.3Saugustss		return (EIO);
5361.3Saugustss	}
5371.3Saugustss	usbd_set_disco(sc->sc_intrpipe, ums_disco, sc);
5381.3Saugustss	return (0);
5391.3Saugustss}
5401.3Saugustss
5411.16Saugustssstatic void
5421.3Saugustssums_disable(v)
5431.3Saugustss	void *v;
5441.3Saugustss{
5451.3Saugustss	struct ums_softc *sc = v;
5461.3Saugustss
5471.3Saugustss	/* Disable interrupts. */
5481.3Saugustss	usbd_abort_pipe(sc->sc_intrpipe);
5491.3Saugustss	usbd_close_pipe(sc->sc_intrpipe);
5501.3Saugustss
5511.3Saugustss	sc->sc_enabled = 0;
5521.16Saugustss
5531.16Saugustss#if defined(USBVERBOSE) && defined(__FreeBSD__)
5541.16Saugustss	if (sc->qcount != 0)
5551.16Saugustss		DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
5561.16Saugustss#endif
5571.3Saugustss}
5581.3Saugustss
5591.16Saugustss#if defined(__NetBSD__)
5601.16Saugustssstatic int
5611.3Saugustssums_ioctl(v, cmd, data, flag, p)
5621.3Saugustss	void *v;
5631.3Saugustss	u_long cmd;
5641.3Saugustss	caddr_t data;
5651.3Saugustss	int flag;
5661.3Saugustss	struct proc *p;
5671.16Saugustss
5681.3Saugustss{
5691.3Saugustss	switch (cmd) {
5701.3Saugustss	case WSMOUSEIO_GTYPE:
5711.3Saugustss		*(u_int *)data = WSMOUSE_TYPE_PS2;
5721.3Saugustss		return (0);
5731.3Saugustss	}
5741.16Saugustss
5751.3Saugustss	return (-1);
5761.3Saugustss}
5771.16Saugustss
5781.16Saugustss#elif defined(__FreeBSD__)
5791.16Saugustssstatic int
5801.16Saugustssums_open(dev_t dev, int flag, int fmt, struct proc *p)
5811.16Saugustss{
5821.16Saugustss	struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
5831.16Saugustss
5841.16Saugustss	if (!sc) {
5851.16Saugustss		DPRINTF(("sc not found at open"));
5861.16Saugustss		return EINVAL;
5871.16Saugustss	}
5881.16Saugustss
5891.16Saugustss	return ums_enable(sc);
5901.16Saugustss}
5911.16Saugustss
5921.16Saugustssstatic int
5931.16Saugustssums_close(dev_t dev, int flag, int fmt, struct proc *p)
5941.16Saugustss{
5951.16Saugustss	struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
5961.16Saugustss
5971.16Saugustss	if (!sc) {
5981.16Saugustss		DPRINTF(("sc not found at close"));
5991.16Saugustss		return EINVAL;
6001.16Saugustss	}
6011.16Saugustss
6021.16Saugustss	if (sc->sc_enabled)
6031.16Saugustss		ums_disable(sc);
6041.16Saugustss	return 0;
6051.16Saugustss}
6061.16Saugustss
6071.16Saugustssstatic int
6081.16Saugustssums_read(dev_t dev, struct uio *uio, int flag)
6091.16Saugustss{
6101.16Saugustss	struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
6111.16Saugustss	int s;
6121.16Saugustss	char buf[sizeof(sc->qbuf)];
6131.16Saugustss	int l = 0;
6141.16Saugustss	int error;
6151.16Saugustss
6161.16Saugustss	if (!sc || !sc->sc_enabled) {
6171.16Saugustss		DPRINTF(("sc not found at read"));
6181.16Saugustss		return EINVAL;
6191.16Saugustss	}
6201.16Saugustss
6211.16Saugustss	s = splusb();
6221.16Saugustss	while (sc->qcount == 0 )  {
6231.16Saugustss		/* NWH XXX non blocking I/O ??
6241.16Saugustss		if (non blocking I/O ) {
6251.16Saugustss			splx(s);
6261.16Saugustss			return EWOULDBLOCK;
6271.16Saugustss		} else {
6281.16Saugustss		*/
6291.16Saugustss		sc->state |= UMS_ASLEEP;
6301.16Saugustss		error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
6311.16Saugustss		sc->state &= ~UMS_ASLEEP;
6321.16Saugustss		if (error) {
6331.16Saugustss			splx(s);
6341.16Saugustss			return error;
6351.16Saugustss		}
6361.16Saugustss	}
6371.16Saugustss
6381.16Saugustss	while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
6391.16Saugustss		l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
6401.16Saugustss		if (l > sizeof(buf))
6411.16Saugustss			l = sizeof(buf);
6421.16Saugustss		if (l > sizeof(sc->qbuf) - sc->qtail)		/* transfer till end of buf */
6431.16Saugustss			l = sizeof(sc->qbuf) - sc->qtail;
6441.16Saugustss
6451.16Saugustss		splx(s);
6461.16Saugustss		uiomove(&sc->qbuf[sc->qtail], l, uio);
6471.16Saugustss		s = splusb();
6481.16Saugustss
6491.16Saugustss		if ( sc->qcount - l < 0 ) {
6501.16Saugustss			DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
6511.16Saugustss			sc->qcount = l;
6521.16Saugustss		}
6531.16Saugustss		sc->qcount -= l;	/* remove the bytes from the buffer */
6541.16Saugustss		sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
6551.16Saugustss	}
6561.16Saugustss	splx(s);
6571.16Saugustss
6581.16Saugustss	return 0;
6591.16Saugustss}
6601.16Saugustss
6611.16Saugustssstatic int
6621.16Saugustssums_poll(dev_t dev, int events, struct proc *p)
6631.16Saugustss{
6641.16Saugustss	struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
6651.16Saugustss	int revents = 0;
6661.16Saugustss	int s;
6671.16Saugustss
6681.16Saugustss	if (!sc) {
6691.16Saugustss		DPRINTF(("sc not found at poll"));
6701.16Saugustss		return 0;	/* just to make sure */
6711.16Saugustss	}
6721.16Saugustss
6731.16Saugustss	s = splusb();
6741.16Saugustss	if (events & (POLLIN | POLLRDNORM))
6751.16Saugustss		if (sc->qcount) {
6761.16Saugustss			revents = events & (POLLIN | POLLRDNORM);
6771.16Saugustss		} else {
6781.16Saugustss			sc->state |= UMS_SELECT;
6791.16Saugustss			selrecord(p, &sc->rsel);
6801.16Saugustss		}
6811.16Saugustss	splx(s);
6821.16Saugustss
6831.16Saugustss	return revents;
6841.16Saugustss}
6851.16Saugustss
6861.16Saugustssint
6871.16Saugustssums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
6881.16Saugustss{
6891.16Saugustss	struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
6901.16Saugustss	int error = 0;
6911.16Saugustss	int s;
6921.16Saugustss
6931.16Saugustss	if (!sc) {
6941.16Saugustss		DPRINTF(("sc not found at ioctl"));
6951.16Saugustss		return ENOENT;
6961.16Saugustss	}
6971.16Saugustss
6981.16Saugustss	switch(cmd) {
6991.16Saugustss	case MOUSE_GETHWINFO:
7001.16Saugustss		*(mousehw_t *)addr = sc->hw;
7011.16Saugustss		break;
7021.16Saugustss	case MOUSE_GETMODE:
7031.16Saugustss		*(mousemode_t *)addr = sc->mode;
7041.16Saugustss		break;
7051.16Saugustss	case MOUSE_GETLEVEL:
7061.16Saugustss		*(int *)addr = sc->mode.level;
7071.16Saugustss		break;
7081.16Saugustss	case MOUSE_GETSTATUS: {
7091.16Saugustss		mousestatus_t *status = (mousestatus_t *) addr;
7101.16Saugustss
7111.16Saugustss		s = splusb();
7121.16Saugustss		*status = sc->status;
7131.16Saugustss		sc->status.obutton = sc->status.button;
7141.16Saugustss		sc->status.button = 0;
7151.16Saugustss		sc->status.dx = sc->status.dy = sc->status.dz = 0;
7161.16Saugustss		splx(s);
7171.16Saugustss
7181.16Saugustss		if (status->dx || status->dy || status->dz)
7191.16Saugustss			status->flags |= MOUSE_POSCHANGED;
7201.16Saugustss		if (status->button != status->obutton)
7211.16Saugustss			status->flags |= MOUSE_BUTTONSCHANGED;
7221.16Saugustss		break;
7231.16Saugustss		}
7241.16Saugustss	default:
7251.16Saugustss		error = ENOTTY;
7261.16Saugustss	}
7271.16Saugustss
7281.16Saugustss	return error;
7291.16Saugustss}
7301.16Saugustss#endif
7311.16Saugustss
7321.16Saugustss#if defined(__FreeBSD__)
7331.16SaugustssCDEV_DRIVER_MODULE(ums, usb, ums_driver, ums_devclass,
7341.16Saugustss			UMS_CDEV_MAJOR, ums_cdevsw, usb_driver_load, 0);
7351.16Saugustss#endif
7361.3Saugustss
737