1 /* $OpenBSD: ums.c,v 1.15 2004/12/06 11:08:23 dlg 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/select.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) logprintf x; } while (0)
73 #define DPRINTFN(n,x) do { if (umsdebug>(n)) logprintf 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;
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 Static void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
116
117 Static int ums_enable(void *);
118 Static void ums_disable(void *);
119 Static int ums_ioctl(void *, u_long, caddr_t, int, usb_proc_ptr);
120
121 const struct wsmouse_accessops ums_accessops = {
122 ums_enable,
123 ums_ioctl,
124 ums_disable,
125 };
126
127 USB_DECLARE_DRIVER(ums);
128
USB_MATCH(ums)129 USB_MATCH(ums)
130 {
131 USB_MATCH_START(ums, uaa);
132 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
133 int size;
134 void *desc;
135
136 uhidev_get_report_desc(uha->parent, &desc, &size);
137 if (!hid_is_collection(desc, size, uha->reportid,
138 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
139 return (UMATCH_NONE);
140
141 return (UMATCH_IFACECLASS);
142 }
143
USB_ATTACH(ums)144 USB_ATTACH(ums)
145 {
146 USB_ATTACH_START(ums, sc, uaa);
147 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
148 struct wsmousedev_attach_args a;
149 int size;
150 void *desc;
151 u_int32_t flags, quirks;
152 int i, wheel;
153 struct hid_location loc_btn;
154
155 sc->sc_hdev.sc_intr = ums_intr;
156 sc->sc_hdev.sc_parent = uha->parent;
157 sc->sc_hdev.sc_report_id = uha->reportid;
158
159 quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
160 if (quirks & UQ_MS_REVZ)
161 sc->flags |= UMS_REVZ;
162 if (quirks & UQ_SPUR_BUT_UP)
163 sc->flags |= UMS_SPUR_BUT_UP;
164
165 uhidev_get_report_desc(uha->parent, &desc, &size);
166
167 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
168 uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
169 printf("\n%s: mouse has no X report\n",
170 USBDEVNAME(sc->sc_hdev.sc_dev));
171 USB_ATTACH_ERROR_RETURN;
172 }
173 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
174 printf("\n%s: X report 0x%04x not supported\n",
175 USBDEVNAME(sc->sc_hdev.sc_dev), flags);
176 USB_ATTACH_ERROR_RETURN;
177 }
178
179 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
180 uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
181 printf("\n%s: mouse has no Y report\n",
182 USBDEVNAME(sc->sc_hdev.sc_dev));
183 USB_ATTACH_ERROR_RETURN;
184 }
185 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
186 printf("\n%s: Y report 0x%04x not supported\n",
187 USBDEVNAME(sc->sc_hdev.sc_dev), flags);
188 USB_ATTACH_ERROR_RETURN;
189 }
190
191 /* Try to guess the Z activator: first check Z, then WHEEL. */
192 wheel = 0;
193 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
194 uha->reportid, hid_input, &sc->sc_loc_z, &flags) ||
195 (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
196 HUG_WHEEL),
197 uha->reportid, hid_input, &sc->sc_loc_z, &flags))) {
198 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
199 sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
200 } else {
201 sc->flags |= UMS_Z;
202 /* Wheels need the Z axis reversed. */
203 if (wheel)
204 sc->flags ^= UMS_REVZ;
205 }
206 }
207
208 /* figure out the number of buttons */
209 for (i = 1; i <= MAX_BUTTONS; i++)
210 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
211 uha->reportid, hid_input, &loc_btn, 0))
212 break;
213 sc->nbuttons = i - 1;
214
215 printf(": %d button%s%s\n",
216 sc->nbuttons, sc->nbuttons == 1 ? "" : "s",
217 sc->flags & UMS_Z ? " and Z dir." : "");
218
219 for (i = 1; i <= sc->nbuttons; i++)
220 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
221 uha->reportid, hid_input,
222 &sc->sc_loc_btn[i-1], 0);
223
224 #ifdef USB_DEBUG
225 DPRINTF(("ums_attach: sc=%p\n", sc));
226 DPRINTF(("ums_attach: X\t%d/%d\n",
227 sc->sc_loc_x.pos, sc->sc_loc_x.size));
228 DPRINTF(("ums_attach: Y\t%d/%d\n",
229 sc->sc_loc_y.pos, sc->sc_loc_y.size));
230 if (sc->flags & UMS_Z)
231 DPRINTF(("ums_attach: Z\t%d/%d\n",
232 sc->sc_loc_z.pos, sc->sc_loc_z.size));
233 for (i = 1; i <= sc->nbuttons; i++) {
234 DPRINTF(("ums_attach: B%d\t%d/%d\n",
235 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
236 }
237 #endif
238
239 a.accessops = &ums_accessops;
240 a.accesscookie = sc;
241
242 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
243
244 USB_ATTACH_SUCCESS_RETURN;
245 }
246
247 int
ums_activate(device_ptr_t self,enum devact act)248 ums_activate(device_ptr_t self, enum devact act)
249 {
250 struct ums_softc *sc = (struct ums_softc *)self;
251 int rv = 0;
252
253 switch (act) {
254 case DVACT_ACTIVATE:
255 return (EOPNOTSUPP);
256
257 case DVACT_DEACTIVATE:
258 if (sc->sc_wsmousedev != NULL)
259 rv = config_deactivate(sc->sc_wsmousedev);
260 sc->sc_dying = 1;
261 break;
262 }
263 return (rv);
264 }
265
USB_DETACH(ums)266 USB_DETACH(ums)
267 {
268 USB_DETACH_START(ums, sc);
269 int rv = 0;
270
271 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags));
272
273 /* No need to do reference counting of ums, wsmouse has all the goo. */
274 if (sc->sc_wsmousedev != NULL)
275 rv = config_detach(sc->sc_wsmousedev, flags);
276
277 return (rv);
278 }
279
280 void
ums_intr(struct uhidev * addr,void * ibuf,u_int len)281 ums_intr(struct uhidev *addr, void *ibuf, u_int len)
282 {
283 struct ums_softc *sc = (struct ums_softc *)addr;
284 int dx, dy, dz;
285 u_int32_t buttons = 0;
286 int i;
287 int s;
288
289 DPRINTFN(5,("ums_intr: len=%d\n", len));
290
291 dx = hid_get_data(ibuf, &sc->sc_loc_x);
292 dy = -hid_get_data(ibuf, &sc->sc_loc_y);
293 dz = hid_get_data(ibuf, &sc->sc_loc_z);
294 if (sc->flags & UMS_REVZ)
295 dz = -dz;
296 for (i = 0; i < sc->nbuttons; i++)
297 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
298 buttons |= (1 << UMS_BUT(i));
299
300 if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) {
301 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
302 dx, dy, dz, buttons));
303 sc->sc_buttons = buttons;
304 if (sc->sc_wsmousedev != NULL) {
305 s = spltty();
306 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz,
307 WSMOUSE_INPUT_DELTA);
308 splx(s);
309 }
310 }
311 }
312
313 Static int
ums_enable(void * v)314 ums_enable(void *v)
315 {
316 struct ums_softc *sc = v;
317
318 DPRINTFN(1,("ums_enable: sc=%p\n", sc));
319
320 if (sc->sc_dying)
321 return (EIO);
322
323 if (sc->sc_enabled)
324 return (EBUSY);
325
326 sc->sc_enabled = 1;
327 sc->sc_buttons = 0;
328
329 return (uhidev_open(&sc->sc_hdev));
330 }
331
332 Static void
ums_disable(void * v)333 ums_disable(void *v)
334 {
335 struct ums_softc *sc = v;
336
337 DPRINTFN(1,("ums_disable: sc=%p\n", sc));
338 #ifdef DIAGNOSTIC
339 if (!sc->sc_enabled) {
340 printf("ums_disable: not enabled\n");
341 return;
342 }
343 #endif
344
345 sc->sc_enabled = 0;
346 uhidev_close(&sc->sc_hdev);
347 }
348
349 Static int
ums_ioctl(void * v,u_long cmd,caddr_t data,int flag,usb_proc_ptr p)350 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
351
352 {
353 switch (cmd) {
354 case WSMOUSEIO_GTYPE:
355 *(u_int *)data = WSMOUSE_TYPE_USB;
356 return (0);
357 }
358
359 return (-1);
360 }
361