ums 9.67 KB
Newer Older
1
/*      $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $        */
2 3 4 5 6 7 8 9 10 11 12 13

/*
 * 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:
14
 * 1. Redistributions of source code must retain the above copyright
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 *    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.
 */

/*
41
 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
42 43
 */

44
#include <sys/cdefs.h>
45
__KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $");
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

#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>
67
#include <dev/usb/uhidev.h>
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
#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

91 92
#define MAX_BUTTONS	7	/* must not exceed size of sc_buttons */

93
struct ums_softc {
94 95
	struct uhidev sc_hdev;

96
	struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
97
	struct hid_location sc_loc_btn[MAX_BUTTONS];
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

	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;

	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)

117
Static void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
118 119 120

Static int	ums_enable(void *);
Static void	ums_disable(void *);
121
Static int	ums_ioctl(void *, u_long, caddr_t, int, usb_proc_ptr );
122 123 124 125 126 127 128 129 130

const struct wsmouse_accessops ums_accessops = {
	ums_enable,
	ums_ioctl,
	ums_disable,
};

USB_DECLARE_DRIVER(ums);

131 132
int
ums_match(struct device *parent, struct cfdata *match, void *aux)
133
{
134 135
	struct uhidev_attach_arg *uha = aux;
	int size;
136 137
	void *desc;

138 139 140
	uhidev_get_report_desc(uha->parent, &desc, &size);
	if (!hid_is_collection(desc, size, uha->reportid,
			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
141 142
		return (UMATCH_NONE);

143
	return (UMATCH_IFACECLASS);
144 145
}

146 147
void
ums_attach(struct device *parent, struct device *self, void *aux)
148
{
149 150
	struct ums_softc *sc = (struct ums_softc *)self;
	struct uhidev_attach_arg *uha = aux;
151 152 153 154 155 156 157
	struct wsmousedev_attach_args a;
	int size;
	void *desc;
	u_int32_t flags, quirks;
	int i, wheel;
	struct hid_location loc_btn;

158 159 160
	sc->sc_hdev.sc_intr = ums_intr;
	sc->sc_hdev.sc_parent = uha->parent;
	sc->sc_hdev.sc_report_id = uha->reportid;
161

162
	quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
163 164 165 166 167
	if (quirks & UQ_MS_REVZ)
		sc->flags |= UMS_REVZ;
	if (quirks & UQ_SPUR_BUT_UP)
		sc->flags |= UMS_SPUR_BUT_UP;

168
	uhidev_get_report_desc(uha->parent, &desc, &size);
169 170

	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
171 172 173
	       uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
		printf("\n%s: mouse has no X report\n",
		       USBDEVNAME(sc->sc_hdev.sc_dev));
174 175 176
		USB_ATTACH_ERROR_RETURN;
	}
	if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
177 178
		printf("\n%s: X report 0x%04x not supported\n",
		       USBDEVNAME(sc->sc_hdev.sc_dev), flags);
179 180 181 182
		USB_ATTACH_ERROR_RETURN;
	}

	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
183 184 185
	       uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
		printf("\n%s: mouse has no Y report\n",
		       USBDEVNAME(sc->sc_hdev.sc_dev));
186 187 188
		USB_ATTACH_ERROR_RETURN;
	}
	if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
189 190
		printf("\n%s: Y report 0x%04x not supported\n",
		       USBDEVNAME(sc->sc_hdev.sc_dev), flags);
191 192 193 194 195 196
		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),
197
	       uha->reportid, hid_input, &sc->sc_loc_z, &flags) ||
198 199
	    (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
						       HUG_WHEEL),
200
	       uha->reportid, hid_input, &sc->sc_loc_z, &flags))) {
201 202 203 204 205 206 207 208 209 210 211 212 213
		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),
214
			uha->reportid, hid_input, &loc_btn, 0))
215 216 217
			break;
	sc->nbuttons = i - 1;

218 219 220
	printf(": %d button%s%s\n",
	    sc->nbuttons, sc->nbuttons == 1 ? "" : "s",
	    sc->flags & UMS_Z ? " and Z dir." : "");
221 222 223

	for (i = 1; i <= sc->nbuttons; i++)
		hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
224 225
			   uha->reportid, hid_input,
			   &sc->sc_loc_btn[i-1], 0);
226 227 228 229 230 231

#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",
232
		 sc->sc_loc_y.pos, sc->sc_loc_y.size));
233 234 235 236 237 238 239 240 241 242 243 244
	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));
	}
#endif

	a.accessops = &ums_accessops;
	a.accesscookie = sc;

245
	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

	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);

	case DVACT_DEACTIVATE:
		if (sc->sc_wsmousedev != NULL)
			rv = config_deactivate(sc->sc_wsmousedev);
		sc->sc_dying = 1;
		break;
	}
	return (rv);
}

269 270
int
ums_detach(struct device *self, int flags)
271
{
272
	struct ums_softc *sc = (struct ums_softc *)self;
273 274 275 276 277 278 279 280 281 282 283 284
	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);

	return (rv);
}

void
285
ums_intr(struct uhidev *addr, void *ibuf, u_int len)
286
{
287
	struct ums_softc *sc = (struct ums_softc *)addr;
288 289 290 291 292
	int dx, dy, dz;
	u_int32_t buttons = 0;
	int i;
	int s;

293
	DPRINTFN(5,("ums_intr: len=%d\n", len));
294 295 296 297 298 299 300 301

	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]))
302
			buttons |= (1 << UMS_BUT(i));
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332

	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;

	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;

333
	return (uhidev_open(&sc->sc_hdev));
334 335 336 337 338 339 340 341 342 343
}

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) {
344
		printf("ums_disable: not enabled\n");
345 346 347 348 349
		return;
	}
#endif

	sc->sc_enabled = 0;
350
	uhidev_close(&sc->sc_hdev);
351 352 353
}

Static int
354
ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
355 356 357 358 359 360 361 362

{
	switch (cmd) {
	case WSMOUSEIO_GTYPE:
		*(u_int *)data = WSMOUSE_TYPE_USB;
		return (0);
	}

363
	return (EPASSTHROUGH);
364
}