Annotation of sys/dev/usb/ums.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ums.c,v 1.25 2007/06/14 10:11:16 mbalmer Exp $ */
2: /* $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $ */
3:
4: /*
5: * Copyright (c) 1998 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Lennart Augustsson (lennart@augustsson.net) at
10: * Carlstedt Research & Technology.
11: *
12: * Redistribution and use in source and binary forms, with or without
13: * modification, are permitted provided that the following conditions
14: * are met:
15: * 1. Redistributions of source code must retain the above copyright
16: * notice, this list of conditions and the following disclaimer.
17: * 2. Redistributions in binary form must reproduce the above copyright
18: * notice, this list of conditions and the following disclaimer in the
19: * documentation and/or other materials provided with the distribution.
20: * 3. All advertising materials mentioning features or use of this software
21: * must display the following acknowledgement:
22: * This product includes software developed by the NetBSD
23: * Foundation, Inc. and its contributors.
24: * 4. Neither the name of The NetBSD Foundation nor the names of its
25: * contributors may be used to endorse or promote products derived
26: * from this software without specific prior written permission.
27: *
28: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38: * POSSIBILITY OF SUCH DAMAGE.
39: */
40:
41: /*
42: * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
43: */
44:
45: #include <sys/param.h>
46: #include <sys/systm.h>
47: #include <sys/kernel.h>
48: #include <sys/malloc.h>
49: #include <sys/device.h>
50: #include <sys/ioctl.h>
51: #include <sys/tty.h>
52: #include <sys/file.h>
53: #include <sys/selinfo.h>
54: #include <sys/proc.h>
55: #include <sys/vnode.h>
56: #include <sys/poll.h>
57:
58: #include <dev/usb/usb.h>
59: #include <dev/usb/usbhid.h>
60:
61: #include <dev/usb/usbdi.h>
62: #include <dev/usb/usbdi_util.h>
63: #include <dev/usb/usbdevs.h>
64: #include <dev/usb/usb_quirks.h>
65: #include <dev/usb/uhidev.h>
66: #include <dev/usb/hid.h>
67:
68: #include <dev/wscons/wsconsio.h>
69: #include <dev/wscons/wsmousevar.h>
70:
71: #ifdef USB_DEBUG
72: #define DPRINTF(x) do { if (umsdebug) printf x; } while (0)
73: #define DPRINTFN(n,x) do { if (umsdebug>(n)) printf x; } while (0)
74: int umsdebug = 0;
75: #else
76: #define DPRINTF(x)
77: #define DPRINTFN(n,x)
78: #endif
79:
80: #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
81:
82: #define UMSUNIT(s) (minor(s))
83:
84: #define PS2LBUTMASK x01
85: #define PS2RBUTMASK x02
86: #define PS2MBUTMASK x04
87: #define PS2BUTMASK 0x0f
88:
89: #define MAX_BUTTONS 16 /* must not exceed size of sc_buttons */
90:
91: struct ums_softc {
92: struct uhidev sc_hdev;
93:
94: struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_w;
95: struct hid_location sc_loc_btn[MAX_BUTTONS];
96:
97: int sc_enabled;
98:
99: int flags; /* device configuration */
100: #define UMS_Z 0x01 /* z direction available */
101: #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
102: #define UMS_REVZ 0x04 /* Z-axis is reversed */
103:
104: int nbuttons;
105:
106: u_int32_t sc_buttons; /* mouse button status */
107: struct device *sc_wsmousedev;
108:
109: char sc_dying;
110: };
111:
112: #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
113: #define MOUSE_FLAGS (HIO_RELATIVE)
114:
115: void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
116:
117: int ums_enable(void *);
118: void ums_disable(void *);
119: int ums_ioctl(void *, u_long, caddr_t, int, struct proc *);
120:
121: const struct wsmouse_accessops ums_accessops = {
122: ums_enable,
123: ums_ioctl,
124: ums_disable,
125: };
126:
127: int ums_match(struct device *, void *, void *);
128: void ums_attach(struct device *, struct device *, void *);
129: int ums_detach(struct device *, int);
130: int ums_activate(struct device *, enum devact);
131:
132: struct cfdriver ums_cd = {
133: NULL, "ums", DV_DULL
134: };
135:
136: const struct cfattach ums_ca = {
137: sizeof(struct ums_softc),
138: ums_match,
139: ums_attach,
140: ums_detach,
141: ums_activate,
142: };
143:
144: int
145: ums_match(struct device *parent, void *match, void *aux)
146: {
147: struct usb_attach_arg *uaa = aux;
148: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
149: int size;
150: void *desc;
151:
152: uhidev_get_report_desc(uha->parent, &desc, &size);
153: if (!hid_is_collection(desc, size, uha->reportid,
154: HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
155: return (UMATCH_NONE);
156:
157: return (UMATCH_IFACECLASS);
158: }
159:
160: void
161: ums_attach(struct device *parent, struct device *self, void *aux)
162: {
163: struct ums_softc *sc = (struct ums_softc *)self;
164: struct usb_attach_arg *uaa = aux;
165: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
166: struct wsmousedev_attach_args a;
167: int size;
168: void *desc;
169: u_int32_t flags, quirks;
170: int i;
171: struct hid_location loc_btn;
172:
173: sc->sc_hdev.sc_intr = ums_intr;
174: sc->sc_hdev.sc_parent = uha->parent;
175: sc->sc_hdev.sc_report_id = uha->reportid;
176:
177: quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
178: if (quirks & UQ_MS_REVZ)
179: sc->flags |= UMS_REVZ;
180: if (quirks & UQ_SPUR_BUT_UP)
181: sc->flags |= UMS_SPUR_BUT_UP;
182:
183: uhidev_get_report_desc(uha->parent, &desc, &size);
184:
185: if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
186: uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
187: printf("\n%s: mouse has no X report\n",
188: sc->sc_hdev.sc_dev.dv_xname);
189: return;
190: }
191: if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
192: printf("\n%s: X report 0x%04x not supported\n",
193: sc->sc_hdev.sc_dev.dv_xname, flags);
194: return;
195: }
196:
197: if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
198: uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
199: printf("\n%s: mouse has no Y report\n",
200: sc->sc_hdev.sc_dev.dv_xname);
201: return;
202: }
203: if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
204: printf("\n%s: Y report 0x%04x not supported\n",
205: sc->sc_hdev.sc_dev.dv_xname, flags);
206: return;
207: }
208:
209: /* Try the wheel as Z activator first */
210: if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
211: uha->reportid, hid_input, &sc->sc_loc_z, &flags)) {
212: if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
213: DPRINTF(("\n%s: Wheel report 0x%04x not supported\n",
214: sc->sc_hdev.sc_dev.dv_xname, flags));
215: sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
216: } else {
217: sc->flags |= UMS_Z;
218: /* Wheels need the Z axis reversed. */
219: sc->flags ^= UMS_REVZ;
220: }
221: /*
222: * We might have both a wheel and Z direction; in this case,
223: * report the Z direction on the W axis.
224: */
225: if (hid_locate(desc, size,
226: HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
227: uha->reportid, hid_input, &sc->sc_loc_w, &flags)) {
228: if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
229: DPRINTF(("\n%s: Z report 0x%04x not supported\n",
230: sc->sc_hdev.sc_dev.dv_xname, flags));
231: /* Bad Z coord, ignore it */
232: sc->sc_loc_w.size = 0;
233: }
234: }
235: } else if (hid_locate(desc, size,
236: HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
237: uha->reportid, hid_input, &sc->sc_loc_z, &flags)) {
238: if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
239: DPRINTF(("\n%s: Z report 0x%04x not supported\n",
240: sc->sc_hdev.sc_dev.dv_xname, flags));
241: sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
242: } else {
243: sc->flags |= UMS_Z;
244: }
245: }
246:
247: /* figure out the number of buttons */
248: for (i = 1; i <= MAX_BUTTONS; i++)
249: if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
250: uha->reportid, hid_input, &loc_btn, 0))
251: break;
252: sc->nbuttons = i - 1;
253:
254: printf(": %d button%s%s\n",
255: sc->nbuttons, sc->nbuttons == 1 ? "" : "s",
256: sc->flags & UMS_Z ? " and Z dir." : "");
257:
258: for (i = 1; i <= sc->nbuttons; i++)
259: hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
260: uha->reportid, hid_input,
261: &sc->sc_loc_btn[i-1], 0);
262:
263: #ifdef USB_DEBUG
264: DPRINTF(("ums_attach: sc=%p\n", sc));
265: DPRINTF(("ums_attach: X\t%d/%d\n",
266: sc->sc_loc_x.pos, sc->sc_loc_x.size));
267: DPRINTF(("ums_attach: Y\t%d/%d\n",
268: sc->sc_loc_y.pos, sc->sc_loc_y.size));
269: if (sc->flags & UMS_Z)
270: DPRINTF(("ums_attach: Z\t%d/%d\n",
271: sc->sc_loc_z.pos, sc->sc_loc_z.size));
272: for (i = 1; i <= sc->nbuttons; i++) {
273: DPRINTF(("ums_attach: B%d\t%d/%d\n",
274: i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
275: }
276: #endif
277:
278: a.accessops = &ums_accessops;
279: a.accesscookie = sc;
280:
281: sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
282: }
283:
284: int
285: ums_activate(struct device *self, enum devact act)
286: {
287: struct ums_softc *sc = (struct ums_softc *)self;
288: int rv = 0;
289:
290: switch (act) {
291: case DVACT_ACTIVATE:
292: break;
293:
294: case DVACT_DEACTIVATE:
295: if (sc->sc_wsmousedev != NULL)
296: rv = config_deactivate(sc->sc_wsmousedev);
297: sc->sc_dying = 1;
298: break;
299: }
300: return (rv);
301: }
302:
303: int
304: ums_detach(struct device *self, int flags)
305: {
306: struct ums_softc *sc = (struct ums_softc *)self;
307: int rv = 0;
308:
309: DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags));
310:
311: /* No need to do reference counting of ums, wsmouse has all the goo. */
312: if (sc->sc_wsmousedev != NULL)
313: rv = config_detach(sc->sc_wsmousedev, flags);
314:
315: return (rv);
316: }
317:
318: void
319: ums_intr(struct uhidev *addr, void *ibuf, u_int len)
320: {
321: struct ums_softc *sc = (struct ums_softc *)addr;
322: int dx, dy, dz, dw;
323: u_int32_t buttons = 0;
324: int i;
325: int s;
326:
327: DPRINTFN(5,("ums_intr: len=%d\n", len));
328:
329: dx = hid_get_data(ibuf, &sc->sc_loc_x);
330: dy = -hid_get_data(ibuf, &sc->sc_loc_y);
331: dz = hid_get_data(ibuf, &sc->sc_loc_z);
332: dw = hid_get_data(ibuf, &sc->sc_loc_w);
333: if (sc->flags & UMS_REVZ)
334: dz = -dz;
335: for (i = 0; i < sc->nbuttons; i++)
336: if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
337: buttons |= (1 << UMS_BUT(i));
338:
339: if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
340: buttons != sc->sc_buttons) {
341: DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n",
342: dx, dy, dz, dw, buttons));
343: sc->sc_buttons = buttons;
344: if (sc->sc_wsmousedev != NULL) {
345: s = spltty();
346: wsmouse_input(sc->sc_wsmousedev, buttons,
347: dx, dy, dz, dw, WSMOUSE_INPUT_DELTA);
348: splx(s);
349: }
350: }
351: }
352:
353: int
354: ums_enable(void *v)
355: {
356: struct ums_softc *sc = v;
357:
358: DPRINTFN(1,("ums_enable: sc=%p\n", sc));
359:
360: if (sc->sc_dying)
361: return (EIO);
362:
363: if (sc->sc_enabled)
364: return (EBUSY);
365:
366: sc->sc_enabled = 1;
367: sc->sc_buttons = 0;
368:
369: return (uhidev_open(&sc->sc_hdev));
370: }
371:
372: void
373: ums_disable(void *v)
374: {
375: struct ums_softc *sc = v;
376:
377: DPRINTFN(1,("ums_disable: sc=%p\n", sc));
378: #ifdef DIAGNOSTIC
379: if (!sc->sc_enabled) {
380: printf("ums_disable: not enabled\n");
381: return;
382: }
383: #endif
384:
385: sc->sc_enabled = 0;
386: uhidev_close(&sc->sc_hdev);
387: }
388:
389: int
390: ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
391:
392: {
393: switch (cmd) {
394: case WSMOUSEIO_GTYPE:
395: *(u_int *)data = WSMOUSE_TYPE_USB;
396: return (0);
397: }
398:
399: return (-1);
400: }
CVSweb