ums 12.2 KB
Newer Older
1
/*	$NetBSD: ums.c,v 1.80 2010/01/14 09:30:39 matthias 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
 *    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.
 *
 * 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.
 */

/*
34
 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
35 36
 */

37
#include <sys/cdefs.h>
38
__KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.80 2010/01/14 09:30:39 matthias Exp $");
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

#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/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>
59
#include <dev/usb/uhidev.h>
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
#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

83
#define MAX_BUTTONS	31	/* must not exceed size of sc_buttons */
84

85
struct ums_softc {
86 87
	struct uhidev sc_hdev;

88
	struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_w;
89
	struct hid_location sc_loc_btn[MAX_BUTTONS];
90 91 92 93 94 95 96

	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 */
97 98
#define UMS_W		0x08	/* w direction/tilt available */
#define UMS_ABS		0x10	/* absolute position, touchpanel */
99 100 101 102

	int nbuttons;

	u_int32_t sc_buttons;	/* mouse button status */
103
	device_t sc_wsmousedev;
104 105 106 107 108 109

	char			sc_dying;
};

#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)

110
Static void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
111 112 113

Static int	ums_enable(void *);
Static void	ums_disable(void *);
114
Static int	ums_ioctl(void *, u_long, void *, int, struct lwp * );
115 116 117 118 119 120 121

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

122 123 124 125 126 127 128 129
int ums_match(device_t, cfdata_t, void *);
void ums_attach(device_t, device_t, void *);
void ums_childdet(device_t, device_t);
int ums_detach(device_t, int);
int ums_activate(device_t, enum devact);
extern struct cfdriver ums_cd;
CFATTACH_DECL2_NEW(ums, sizeof(struct ums_softc), ums_match, ums_attach,
    ums_detach, ums_activate, NULL, ums_childdet);
130

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

138 139 140 141 142 143 144 145
	/*
	 * Some (older) Griffin PowerMate knobs may masquerade as a
	 * mouse, avoid treating them as such, they have only one axis.
	 */
	if (uha->uaa->vendor == USB_VENDOR_GRIFFIN &&
	    uha->uaa->product == USB_PRODUCT_GRIFFIN_POWERMATE)
		return (UMATCH_NONE);

146 147
	uhidev_get_report_desc(uha->parent, &desc, &size);
	if (!hid_is_collection(desc, size, uha->reportid,
148 149 150
			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) &&
	    !hid_is_collection(desc, size, uha->reportid,
			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)))
151 152
		return (UMATCH_NONE);

153
	return (UMATCH_IFACECLASS);
154 155
}

156
void
157
ums_attach(device_t parent, device_t self, void *aux)
158
{
159
	struct ums_softc *sc = device_private(self);
160
	struct uhidev_attach_arg *uha = aux;
161 162 163 164
	struct wsmousedev_attach_args a;
	int size;
	void *desc;
	u_int32_t flags, quirks;
165 166
	int i, hl;
	struct hid_location *zloc;
167 168
	struct hid_location loc_btn;

169 170 171
	aprint_naive("\n");

	sc->sc_hdev.sc_dev = self;
172 173 174
	sc->sc_hdev.sc_intr = ums_intr;
	sc->sc_hdev.sc_parent = uha->parent;
	sc->sc_hdev.sc_report_id = uha->reportid;
175

176
	quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
177 178 179 180 181
	if (quirks & UQ_MS_REVZ)
		sc->flags |= UMS_REVZ;
	if (quirks & UQ_SPUR_BUT_UP)
		sc->flags |= UMS_SPUR_BUT_UP;

182
	uhidev_get_report_desc(uha->parent, &desc, &size);
183

184 185 186
	if (!pmf_device_register(self, NULL, NULL))
		aprint_error_dev(self, "couldn't establish power handler\n");

187
	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
188
	       uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
189
		aprint_error("\n%s: mouse has no X report\n",
190
		       USBDEVNAME(sc->sc_hdev.sc_dev));
191 192
		USB_ATTACH_ERROR_RETURN;
	}
193 194 195 196 197 198 199 200
	switch (flags & MOUSE_FLAGS_MASK) {
	case 0:
		sc->flags |= UMS_ABS;
		break;
	case HIO_RELATIVE:
		break;
	default:
		aprint_error("\n%s: X report 0x%04x not supported\n",
201
		       USBDEVNAME(sc->sc_hdev.sc_dev), flags);
202 203 204 205
		USB_ATTACH_ERROR_RETURN;
	}

	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
206
	       uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
207
		aprint_error("\n%s: mouse has no Y report\n",
208
		       USBDEVNAME(sc->sc_hdev.sc_dev));
209 210
		USB_ATTACH_ERROR_RETURN;
	}
211 212 213 214 215 216 217 218
	switch (flags & MOUSE_FLAGS_MASK) {
	case 0:
		sc->flags |= UMS_ABS;
		break;
	case HIO_RELATIVE:
		break;
	default:
		aprint_error("\n%s: Y report 0x%04x not supported\n",
219
		       USBDEVNAME(sc->sc_hdev.sc_dev), flags);
220 221 222
		USB_ATTACH_ERROR_RETURN;
	}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	/* Try the wheel first as the Z activator since it's tradition. */
	hl = hid_locate(desc, 
			size, 
			HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 
			uha->reportid, 
			hid_input, 
			&sc->sc_loc_z, 
			&flags);

	zloc = &sc->sc_loc_z;
	if (hl) {
		if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) {
			aprint_verbose("\n%s: Wheel report 0x%04x not "
			    "supported\n", USBDEVNAME(sc->sc_hdev.sc_dev),
			    flags);
238 239 240 241
			sc->sc_loc_z.size = 0;	/* Bad Z coord, ignore it */
		} else {
			sc->flags |= UMS_Z;
			/* Wheels need the Z axis reversed. */
242 243 244
			sc->flags ^= UMS_REVZ;
			/* Put Z on the W coordinate */
			zloc = &sc->sc_loc_w;
245 246 247
		}
	}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	hl = hid_locate(desc, 
			size, 
			HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
			uha->reportid,
			hid_input,
			zloc,
			&flags);

	/*
	 * The horizontal component of the scrollball can also be given by
	 * Application Control Pan in the Consumer page, so if we didnt see
	 * any Z then check that.
	 */
	if (!hl) {
		hl = hid_locate(desc, 
				size, 
				HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), 
				uha->reportid,
				hid_input,
				zloc,
				&flags);
	}

	if (hl) {
		if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) {
			aprint_verbose("\n%s: Z report 0x%04x not supported\n",
			       USBDEVNAME(sc->sc_hdev.sc_dev), flags);
			zloc->size = 0;	/* Bad Z coord, ignore it */
		} else {
			if (sc->flags & UMS_Z)
				sc->flags |= UMS_W;
			else
				sc->flags |= UMS_Z;
		}
	}

	/*
	 * The Microsoft Wireless Laser Mouse 6000 v2.0 reports a bad
	 * position for the wheel and wheel tilt controls -- should be
	 * in bytes 3 & 4 of the report.  Fix this if necessary.
	 */
	if (uha->uaa->vendor == USB_VENDOR_MICROSOFT &&
	    (uha->uaa->product == USB_PRODUCT_MICROSOFT_24GHZ_XCVR10 ||
	     uha->uaa->product == USB_PRODUCT_MICROSOFT_24GHZ_XCVR20)) {	
		if ((sc->flags & UMS_Z) && sc->sc_loc_z.pos == 0)
			sc->sc_loc_z.pos = 24;
		if ((sc->flags & UMS_W) && sc->sc_loc_w.pos == 0)
			sc->sc_loc_w.pos = sc->sc_loc_z.pos + 8;
	}

298 299 300
	/* figure out the number of buttons */
	for (i = 1; i <= MAX_BUTTONS; i++)
		if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
301
			uha->reportid, hid_input, &loc_btn, 0))
302 303 304
			break;
	sc->nbuttons = i - 1;

305
	aprint_normal(": %d button%s%s%s%s\n",
306
	    sc->nbuttons, sc->nbuttons == 1 ? "" : "s",
307 308 309
	    sc->flags & UMS_W ? ", W" : "",
	    sc->flags & UMS_Z ? " and Z dir" : "",
	    sc->flags & UMS_W ? "s" : "");
310 311 312

	for (i = 1; i <= sc->nbuttons; i++)
		hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
313 314
			   uha->reportid, hid_input,
			   &sc->sc_loc_btn[i-1], 0);
315 316 317 318 319 320

#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",
321
		 sc->sc_loc_y.pos, sc->sc_loc_y.size));
322 323 324
	if (sc->flags & UMS_Z)
		DPRINTF(("ums_attach: Z\t%d/%d\n",
			 sc->sc_loc_z.pos, sc->sc_loc_z.size));
325 326 327
	if (sc->flags & UMS_W)
		DPRINTF(("ums_attach: W\t%d/%d\n",
			 sc->sc_loc_w.pos, sc->sc_loc_w.size));
328 329 330 331 332 333 334 335 336
	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;

337
	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
338 339 340 341 342 343 344

	USB_ATTACH_SUCCESS_RETURN;
}

int
ums_activate(device_ptr_t self, enum devact act)
{
345
	struct ums_softc *sc = device_private(self);
346 347 348 349

	switch (act) {
	case DVACT_DEACTIVATE:
		sc->sc_dying = 1;
350 351 352
		return 0;
	default:
		return EOPNOTSUPP;
353
	}
354 355 356 357 358 359 360 361 362
}

void
ums_childdet(device_t self, device_t child)
{
	struct ums_softc *sc = device_private(self);

	KASSERT(sc->sc_wsmousedev == child);
	sc->sc_wsmousedev = NULL;
363 364
}

365
int
366
ums_detach(device_t self, int flags)
367
{
368
	struct ums_softc *sc = device_private(self);
369 370 371 372 373 374 375 376
	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);

377 378
	pmf_device_deregister(self);

379 380 381 382
	return (rv);
}

void
383
ums_intr(struct uhidev *addr, void *ibuf, u_int len)
384
{
385
	struct ums_softc *sc = (struct ums_softc *)addr;
386
	int dx, dy, dz, dw;
387
	u_int32_t buttons = 0;
388
	int i, flags, s;
389

390
	DPRINTFN(5,("ums_intr: len=%d\n", len));
391

392 393
	flags = WSMOUSE_INPUT_DELTA;	/* equals 0 */

394
	dx =  hid_get_data(ibuf, &sc->sc_loc_x);
395 396 397 398 399
	if (sc->flags & UMS_ABS) {
		flags |= (WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
		dy = hid_get_data(ibuf, &sc->sc_loc_y);
	} else
		dy = -hid_get_data(ibuf, &sc->sc_loc_y);
400
	dz =  hid_get_data(ibuf, &sc->sc_loc_z);
401 402
	dw =  hid_get_data(ibuf, &sc->sc_loc_w);

403 404 405 406
	if (sc->flags & UMS_REVZ)
		dz = -dz;
	for (i = 0; i < sc->nbuttons; i++)
		if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
407
			buttons |= (1 << UMS_BUT(i));
408

409 410 411 412
	if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
	    buttons != sc->sc_buttons) {
		DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n",
			dx, dy, dz, dw, buttons));
413 414 415 416
		sc->sc_buttons = buttons;
		if (sc->sc_wsmousedev != NULL) {
			s = spltty();
			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz,
417
			    dw, flags);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
			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;

439
	return (uhidev_open(&sc->sc_hdev));
440 441 442 443 444 445 446 447 448 449
}

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) {
450
		printf("ums_disable: not enabled\n");
451 452 453 454 455
		return;
	}
#endif

	sc->sc_enabled = 0;
456
	uhidev_close(&sc->sc_hdev);
457 458 459
}

Static int
460 461
ums_ioctl(void *v, u_long cmd, void *data, int flag,
    struct lwp * p)
462 463

{
464 465
	struct ums_softc *sc = v;

466 467
	switch (cmd) {
	case WSMOUSEIO_GTYPE:
468 469 470 471
		if (sc->flags & UMS_ABS)
			*(u_int *)data = WSMOUSE_TYPE_TPANEL;
		else
			*(u_int *)data = WSMOUSE_TYPE_USB;
472 473 474
		return (0);
	}

475
	return (EPASSTHROUGH);
476
}