dev - Properly propogate wakeup events for ums
[dragonfly.git] / sys / dev / usbmisc / ums / ums.c
CommitLineData
1550dfd9
MD
1/*
2 * $FreeBSD: src/sys/dev/usb/ums.c,v 1.64 2003/11/09 09:17:22 tanimura Exp $
74781d8f 3 * $DragonFly: src/sys/dev/usbmisc/ums/ums.c,v 1.31 2008/08/14 20:55:54 hasso Exp $
1550dfd9 4 */
984263bc
MD
5
6/*
7 * Copyright (c) 1998 The NetBSD Foundation, Inc.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Lennart Augustsson (lennart@augustsson.net) at
12 * Carlstedt Research & Technology.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43/*
1550dfd9 44 * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf
984263bc
MD
45 */
46
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/module.h>
52#include <sys/bus.h>
984263bc
MD
53#include <sys/conf.h>
54#include <sys/tty.h>
55#include <sys/file.h>
56#include <sys/select.h>
984263bc
MD
57#include <sys/vnode.h>
58#include <sys/poll.h>
96bcde04 59#include <sys/event.h>
984263bc 60#include <sys/sysctl.h>
4e01b467 61#include <sys/thread2.h>
984263bc 62
1f2de5d4
MD
63#include <bus/usb/usb.h>
64#include <bus/usb/usbhid.h>
984263bc 65
1f2de5d4
MD
66#include <bus/usb/usbdi.h>
67#include <bus/usb/usbdi_util.h>
1f2de5d4
MD
68#include <bus/usb/usb_quirks.h>
69#include <bus/usb/hid.h>
984263bc
MD
70
71#include <machine/mouse.h>
72
73#ifdef USB_DEBUG
fc1ef497
HT
74#define DPRINTF(x) if (umsdebug) kprintf x
75#define DPRINTFN(n,x) if (umsdebug>(n)) kprintf x
984263bc
MD
76int umsdebug = 0;
77SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
78SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
79 &umsdebug, 0, "ums debug level");
80#else
81#define DPRINTF(x)
82#define DPRINTFN(n,x)
83#endif
84
85#define UMSUNIT(s) (minor(s)&0x1f)
86
1550dfd9 87#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
984263bc
MD
88
89#define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */
90
91struct ums_softc {
92 device_t sc_dev; /* base device */
93 usbd_interface_handle sc_iface; /* interface */
94 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
95 int sc_ep_addr;
96
97 u_char *sc_ibuf;
98 u_int8_t sc_iid;
99 int sc_isize;
100 struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
101 struct hid_location *sc_loc_btn;
102
8c64e381 103 struct callout sc_timeout; /* for spurious button ups */
984263bc
MD
104
105 int sc_enabled;
106 int sc_disconnected; /* device is gone */
107
108 int flags; /* device configuration */
109#define UMS_Z 0x01 /* z direction available */
110#define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
111 int nbuttons;
bc5a1c0b 112#define MAX_BUTTONS 31 /* must not exceed size of sc_buttons */
984263bc
MD
113
114 u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */
115 u_char dummy[100]; /* XXX just for safety and for now */
116 int qcount, qhead, qtail;
117 mousehw_t hw;
118 mousemode_t mode;
119 mousestatus_t status;
120
121 int state;
122# define UMS_ASLEEP 0x01 /* readFromDevice is waiting */
984263bc 123 struct selinfo rsel; /* process waiting in select */
984263bc
MD
124};
125
126#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
127#define MOUSE_FLAGS (HIO_RELATIVE)
128
6ed427ca 129static void ums_intr(usbd_xfer_handle xfer,
984263bc
MD
130 usbd_private_handle priv, usbd_status status);
131
6ed427ca 132static void ums_add_to_queue(struct ums_softc *sc,
984263bc 133 int dx, int dy, int dz, int buttons);
6ed427ca 134static void ums_add_to_queue_timeout(void *priv);
984263bc 135
6ed427ca
HT
136static int ums_enable(void *);
137static void ums_disable(void *);
984263bc 138
6ed427ca
HT
139static d_open_t ums_open;
140static d_close_t ums_close;
141static d_read_t ums_read;
142static d_ioctl_t ums_ioctl;
143static d_poll_t ums_poll;
96bcde04
SG
144static d_kqfilter_t ums_kqfilter;
145
146static void ums_filt_detach(struct knote *);
147static int ums_filt(struct knote *, long);
984263bc
MD
148
149#define UMS_CDEV_MAJOR 111
150
6ed427ca 151static struct dev_ops ums_ops = {
96bcde04 152 { "ums", UMS_CDEV_MAJOR, D_KQFILTER },
fef8985e
MD
153 .d_open = ums_open,
154 .d_close = ums_close,
155 .d_read = ums_read,
156 .d_ioctl = ums_ioctl,
157 .d_poll = ums_poll,
96bcde04 158 .d_kqfilter = ums_kqfilter
984263bc
MD
159};
160
61b69189
HT
161static device_probe_t ums_match;
162static device_attach_t ums_attach;
163static device_detach_t ums_detach;
164
165static devclass_t ums_devclass;
166
167static kobj_method_t ums_methods[] = {
168 DEVMETHOD(device_probe, ums_match),
169 DEVMETHOD(device_attach, ums_attach),
170 DEVMETHOD(device_detach, ums_detach),
171 {0,0}
172};
173
174static driver_t ums_driver = {
175 "ums",
176 ums_methods,
177 sizeof(struct ums_softc)
178};
179
180MODULE_DEPEND(ums, usb, 1, 1, 1);
984263bc 181
e785a5d9
HT
182static int
183ums_match(device_t self)
984263bc 184{
e785a5d9 185 struct usb_attach_arg *uaa = device_get_ivars(self);
984263bc
MD
186 usb_interface_descriptor_t *id;
187 int size, ret;
188 void *desc;
189 usbd_status err;
1550dfd9 190
984263bc
MD
191 if (!uaa->iface)
192 return (UMATCH_NONE);
193 id = usbd_get_interface_descriptor(uaa->iface);
194 if (!id || id->bInterfaceClass != UICLASS_HID)
195 return (UMATCH_NONE);
196
1550dfd9 197 err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
984263bc
MD
198 if (err)
199 return (UMATCH_NONE);
200
1550dfd9 201 if (hid_is_collection(desc, size,
984263bc
MD
202 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
203 ret = UMATCH_IFACECLASS;
204 else
205 ret = UMATCH_NONE;
206
efda3bd0 207 kfree(desc, M_TEMP);
984263bc
MD
208 return (ret);
209}
210
e785a5d9
HT
211static int
212ums_attach(device_t self)
984263bc 213{
e785a5d9
HT
214 struct ums_softc *sc = device_get_softc(self);
215 struct usb_attach_arg *uaa = device_get_ivars(self);
984263bc 216 usbd_interface_handle iface = uaa->iface;
984263bc
MD
217 usb_endpoint_descriptor_t *ed;
218 int size;
219 void *desc;
220 usbd_status err;
984263bc
MD
221 u_int32_t flags;
222 int i;
223 struct hid_location loc_btn;
1550dfd9 224
984263bc
MD
225 sc->sc_disconnected = 1;
226 sc->sc_iface = iface;
e785a5d9 227 sc->sc_dev = self;
984263bc
MD
228 ed = usbd_interface2endpoint_descriptor(iface, 0);
229 if (!ed) {
e3869ec7 230 kprintf("%s: could not read endpoint descriptor\n",
6ed427ca 231 device_get_nameunit(sc->sc_dev));
e785a5d9 232 return ENXIO;
984263bc
MD
233 }
234
235 DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
236 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
237 " bInterval=%d\n",
1550dfd9 238 ed->bLength, ed->bDescriptorType,
984263bc
MD
239 UE_GET_ADDR(ed->bEndpointAddress),
240 UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
241 UE_GET_XFERTYPE(ed->bmAttributes),
242 UGETW(ed->wMaxPacketSize), ed->bInterval));
243
244 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
245 UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
e3869ec7 246 kprintf("%s: unexpected endpoint\n",
6ed427ca 247 device_get_nameunit(sc->sc_dev));
e785a5d9 248 return ENXIO;
984263bc
MD
249 }
250
1550dfd9 251 err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
984263bc 252 if (err)
e785a5d9 253 return ENXIO;
984263bc
MD
254
255 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
256 hid_input, &sc->sc_loc_x, &flags)) {
6ed427ca 257 kprintf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev));
e785a5d9 258 return ENXIO;
984263bc
MD
259 }
260 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
e3869ec7 261 kprintf("%s: X report 0x%04x not supported\n",
6ed427ca 262 device_get_nameunit(sc->sc_dev), flags);
e785a5d9 263 return ENXIO;
984263bc
MD
264 }
265
266 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
267 hid_input, &sc->sc_loc_y, &flags)) {
6ed427ca 268 kprintf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev));
e785a5d9 269 return ENXIO;
984263bc
MD
270 }
271 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
e3869ec7 272 kprintf("%s: Y report 0x%04x not supported\n",
6ed427ca 273 device_get_nameunit(sc->sc_dev), flags);
e785a5d9 274 return ENXIO;
984263bc
MD
275 }
276
277 /* try to guess the Z activator: first check Z, then WHEEL */
278 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
279 hid_input, &sc->sc_loc_z, &flags) ||
280 hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
281 hid_input, &sc->sc_loc_z, &flags)) {
282 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
283 sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
284 } else {
285 sc->flags |= UMS_Z;
286 }
287 }
288
289 /* figure out the number of buttons */
290 for (i = 1; i <= MAX_BUTTONS; i++)
291 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
292 hid_input, &loc_btn, 0))
293 break;
294 sc->nbuttons = i - 1;
77652cad 295 sc->sc_loc_btn = kmalloc(sizeof(struct hid_location)*sc->nbuttons,
ad86f6b6 296 M_USBDEV, M_INTWAIT);
984263bc 297
6ed427ca 298 kprintf("%s: %d buttons%s\n", device_get_nameunit(sc->sc_dev),
984263bc
MD
299 sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : "");
300
301 for (i = 1; i <= sc->nbuttons; i++)
302 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
303 hid_input, &sc->sc_loc_btn[i-1], 0);
304
305 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
efda3bd0 306 sc->sc_ibuf = kmalloc(sc->sc_isize, M_USB, M_INTWAIT);
984263bc
MD
307 sc->sc_ep_addr = ed->bEndpointAddress;
308 sc->sc_disconnected = 0;
efda3bd0 309 kfree(desc, M_TEMP);
984263bc
MD
310
311#ifdef USB_DEBUG
312 DPRINTF(("ums_attach: sc=%p\n", sc));
1550dfd9 313 DPRINTF(("ums_attach: X\t%d/%d\n",
984263bc 314 sc->sc_loc_x.pos, sc->sc_loc_x.size));
1550dfd9 315 DPRINTF(("ums_attach: Y\t%d/%d\n",
984263bc
MD
316 sc->sc_loc_y.pos, sc->sc_loc_y.size));
317 if (sc->flags & UMS_Z)
1550dfd9 318 DPRINTF(("ums_attach: Z\t%d/%d\n",
984263bc
MD
319 sc->sc_loc_z.pos, sc->sc_loc_z.size));
320 for (i = 1; i <= sc->nbuttons; i++) {
321 DPRINTF(("ums_attach: B%d\t%d/%d\n",
322 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
323 }
324 DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
325#endif
326
327 if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
328 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
329 else
330 sc->hw.buttons = sc->nbuttons;
331 sc->hw.iftype = MOUSE_IF_USB;
332 sc->hw.type = MOUSE_MOUSE;
333 sc->hw.model = MOUSE_MODEL_GENERIC;
334 sc->hw.hwid = 0;
335 sc->mode.protocol = MOUSE_PROTO_MSC;
336 sc->mode.rate = -1;
337 sc->mode.resolution = MOUSE_RES_UNKNOWN;
338 sc->mode.accelfactor = 0;
339 sc->mode.level = 0;
340 sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
341 sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
342 sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
343
344 sc->status.flags = 0;
345 sc->status.button = sc->status.obutton = 0;
346 sc->status.dx = sc->status.dy = sc->status.dz = 0;
347
fef8985e 348 make_dev(&ums_ops, device_get_unit(self),
3e82b46c
MD
349 UID_ROOT, GID_OPERATOR,
350 0644, "ums%d", device_get_unit(self));
984263bc
MD
351
352 if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
353 DPRINTF(("%s: Spurious button up events\n",
6ed427ca 354 device_get_nameunit(sc->sc_dev)));
984263bc
MD
355 sc->flags |= UMS_SPUR_BUT_UP;
356 }
357
e785a5d9 358 return 0;
984263bc
MD
359}
360
361
6ed427ca 362static int
984263bc
MD
363ums_detach(device_t self)
364{
365 struct ums_softc *sc = device_get_softc(self);
984263bc
MD
366
367 if (sc->sc_enabled)
368 ums_disable(sc);
369
6ed427ca 370 DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
984263bc 371
efda3bd0
MD
372 kfree(sc->sc_loc_btn, M_USB);
373 kfree(sc->sc_ibuf, M_USB);
984263bc 374
984263bc
MD
375 /* someone waiting for data */
376 /*
377 * XXX If we wakeup the process here, the device will be gone by
378 * the time the process gets a chance to notice. *_close and friends
379 * should be fixed to handle this case.
380 * Or we should do a delayed detach for this.
381 * Does this delay now force tsleep to exit with an error?
382 */
383 if (sc->state & UMS_ASLEEP) {
384 sc->state &= ~UMS_ASLEEP;
385 wakeup(sc);
386 }
0f538c08 387 selwakeup(&sc->rsel);
17336059 388 KNOTE(&sc->rsel.si_note, 0);
0f538c08 389
cd29885a 390 dev_ops_remove_minor(&ums_ops, /*-1, */device_get_unit(self));
984263bc
MD
391
392 return 0;
393}
394
395void
c436375a
SW
396ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr,
397 usbd_status status)
984263bc
MD
398{
399 struct ums_softc *sc = addr;
400 u_char *ibuf;
401 int dx, dy, dz;
402 u_char buttons = 0;
403 int i;
404
405#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
406
407 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
408 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
409 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
410
411 if (status == USBD_CANCELLED)
412 return;
413
414 if (status != USBD_NORMAL_COMPLETION) {
415 DPRINTF(("ums_intr: status=%d\n", status));
1550dfd9
MD
416 if (status == USBD_STALLED)
417 usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
984263bc
MD
418 return;
419 }
420
421 ibuf = sc->sc_ibuf;
422 if (sc->sc_iid) {
423 if (*ibuf++ != sc->sc_iid)
424 return;
425 }
426
427 dx = hid_get_data(ibuf, &sc->sc_loc_x);
428 dy = -hid_get_data(ibuf, &sc->sc_loc_y);
429 dz = -hid_get_data(ibuf, &sc->sc_loc_z);
430 for (i = 0; i < sc->nbuttons; i++)
431 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
432 buttons |= (1 << UMS_BUT(i));
433
434 if (dx || dy || dz || (sc->flags & UMS_Z)
435 || buttons != sc->status.button) {
436 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
437 dx, dy, dz, buttons));
438
439 sc->status.button = buttons;
440 sc->status.dx += dx;
441 sc->status.dy += dy;
442 sc->status.dz += dz;
443
444 /* Discard data in case of full buffer */
445 if (sc->qcount == sizeof(sc->qbuf)) {
446 DPRINTF(("Buffer full, discarded packet"));
447 return;
448 }
449
450 /*
451 * The Qtronix keyboard has a built in PS/2 port for a mouse.
452 * The firmware once in a while posts a spurious button up
453 * event. This event we ignore by doing a timeout for 50 msecs.
454 * If we receive dx=dy=dz=buttons=0 before we add the event to
455 * the queue.
456 * In any other case we delete the timeout event.
457 */
458 if (sc->flags & UMS_SPUR_BUT_UP &&
459 dx == 0 && dy == 0 && dz == 0 && buttons == 0) {
8c64e381 460 callout_reset(&sc->sc_timeout, MS_TO_TICKS(50),
1550dfd9 461 ums_add_to_queue_timeout, (void *) sc);
984263bc 462 } else {
8c64e381 463 callout_stop(&sc->sc_timeout);
984263bc
MD
464 ums_add_to_queue(sc, dx, dy, dz, buttons);
465 }
466 }
467}
468
6ed427ca 469static void
984263bc
MD
470ums_add_to_queue_timeout(void *priv)
471{
472 struct ums_softc *sc = priv;
984263bc 473
4e01b467 474 crit_enter();
984263bc 475 ums_add_to_queue(sc, 0, 0, 0, 0);
4e01b467 476 crit_exit();
984263bc
MD
477}
478
6ed427ca 479static void
984263bc
MD
480ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
481{
482 /* Discard data in case of full buffer */
483 if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
484 DPRINTF(("Buffer full, discarded packet"));
485 return;
486 }
487
488 if (dx > 254) dx = 254;
489 if (dx < -256) dx = -256;
490 if (dy > 254) dy = 254;
491 if (dy < -256) dy = -256;
492 if (dz > 126) dz = 126;
493 if (dz < -128) dz = -128;
494
495 sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
496 sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
497 sc->qbuf[sc->qhead+1] = dx >> 1;
498 sc->qbuf[sc->qhead+2] = dy >> 1;
499 sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
500 sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
501
502 if (sc->mode.level == 1) {
503 sc->qbuf[sc->qhead+5] = dz >> 1;
504 sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
505 sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
506 & MOUSE_SYS_EXTBUTTONS);
507 }
508
509 sc->qhead += sc->mode.packetsize;
510 sc->qcount += sc->mode.packetsize;
511 /* wrap round at end of buffer */
512 if (sc->qhead >= sizeof(sc->qbuf))
513 sc->qhead = 0;
514
515 /* someone waiting for data */
516 if (sc->state & UMS_ASLEEP) {
517 sc->state &= ~UMS_ASLEEP;
518 wakeup(sc);
519 }
0f538c08 520 selwakeup(&sc->rsel);
17336059 521 KNOTE(&sc->rsel.si_note, 0);
984263bc 522}
c436375a 523
6ed427ca 524static int
c436375a 525ums_enable(void *v)
984263bc
MD
526{
527 struct ums_softc *sc = v;
528
529 usbd_status err;
530
531 if (sc->sc_enabled)
532 return EBUSY;
533
534 sc->sc_enabled = 1;
535 sc->qcount = 0;
536 sc->qhead = sc->qtail = 0;
537 sc->status.flags = 0;
538 sc->status.button = sc->status.obutton = 0;
539 sc->status.dx = sc->status.dy = sc->status.dz = 0;
540
8c64e381 541 callout_init(&sc->sc_timeout);
984263bc
MD
542
543 /* Set up interrupt pipe. */
1550dfd9
MD
544 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
545 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
984263bc
MD
546 sc->sc_ibuf, sc->sc_isize, ums_intr,
547 USBD_DEFAULT_INTERVAL);
548 if (err) {
549 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
550 err));
551 sc->sc_enabled = 0;
552 return (EIO);
553 }
554 return (0);
555}
556
6ed427ca 557static void
c436375a 558ums_disable(void *priv)
984263bc
MD
559{
560 struct ums_softc *sc = priv;
561
8c64e381 562 callout_stop(&sc->sc_timeout);
984263bc
MD
563
564 /* Disable interrupts. */
565 usbd_abort_pipe(sc->sc_intrpipe);
566 usbd_close_pipe(sc->sc_intrpipe);
567
568 sc->sc_enabled = 0;
569
570 if (sc->qcount != 0)
571 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
572}
573
6ed427ca 574static int
fef8985e 575ums_open(struct dev_open_args *ap)
984263bc 576{
b13267a5 577 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
578 struct ums_softc *sc;
579
1e089e7e
HT
580 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
581 if (sc == NULL)
582 return (ENXIO);
984263bc
MD
583
584 return ums_enable(sc);
585}
586
6ed427ca 587static int
fef8985e 588ums_close(struct dev_close_args *ap)
984263bc 589{
b13267a5 590 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
591 struct ums_softc *sc;
592
1e089e7e 593 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
984263bc
MD
594
595 if (!sc)
596 return 0;
597
598 if (sc->sc_enabled)
599 ums_disable(sc);
600
601 return 0;
602}
603
6ed427ca 604static int
fef8985e 605ums_read(struct dev_read_args *ap)
984263bc 606{
b13267a5 607 cdev_t dev = ap->a_head.a_dev;
fef8985e 608 struct uio *uio = ap->a_uio;
984263bc 609 struct ums_softc *sc;
984263bc
MD
610 char buf[sizeof(sc->qbuf)];
611 int l = 0;
612 int error;
613
1e089e7e 614 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
984263bc 615
4e01b467 616 crit_enter();
984263bc 617 if (!sc) {
4e01b467 618 crit_exit();
984263bc
MD
619 return EIO;
620 }
621
622 while (sc->qcount == 0 ) {
fef8985e 623 if (ap->a_ioflag & IO_NDELAY) { /* non-blocking I/O */
4e01b467 624 crit_exit();
984263bc
MD
625 return EWOULDBLOCK;
626 }
1550dfd9 627
984263bc 628 sc->state |= UMS_ASLEEP; /* blocking I/O */
377d4740 629 error = tsleep(sc, PCATCH, "umsrea", 0);
984263bc 630 if (error) {
4e01b467 631 crit_exit();
984263bc
MD
632 return error;
633 } else if (!sc->sc_enabled) {
4e01b467 634 crit_exit();
984263bc
MD
635 return EINTR;
636 }
637 /* check whether the device is still there */
638
639 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
640 if (!sc) {
4e01b467 641 crit_exit();
984263bc
MD
642 return EIO;
643 }
644 }
645
646 /*
4e01b467
MD
647 * The writer process only extends qcount and qtail. We could copy
648 * them and use the copies to do the copying out of the queue.
984263bc
MD
649 */
650
651 while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
652 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
653 if (l > sizeof(buf))
654 l = sizeof(buf);
655 if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */
656 l = sizeof(sc->qbuf) - sc->qtail;
657
4e01b467 658 crit_exit();
984263bc 659 uiomove(&sc->qbuf[sc->qtail], l, uio);
4e01b467 660 crit_enter();
984263bc
MD
661
662 if ( sc->qcount - l < 0 ) {
663 DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
664 sc->qcount = l;
665 }
666 sc->qcount -= l; /* remove the bytes from the buffer */
667 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
668 }
4e01b467 669 crit_exit();
984263bc
MD
670
671 return 0;
672}
673
6ed427ca 674static int
fef8985e 675ums_poll(struct dev_poll_args *ap)
984263bc 676{
b13267a5 677 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
678 struct ums_softc *sc;
679 int revents = 0;
984263bc 680
1e089e7e 681 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
984263bc 682
fef8985e
MD
683 if (!sc) {
684 ap->a_events = 0;
984263bc 685 return 0;
fef8985e 686 }
984263bc 687
4e01b467 688 crit_enter();
fef8985e 689 if (ap->a_events & (POLLIN | POLLRDNORM)) {
984263bc 690 if (sc->qcount) {
fef8985e 691 revents = ap->a_events & (POLLIN | POLLRDNORM);
984263bc 692 } else {
0f538c08 693 /* sc->state |= UMS_SELECT; */
fef8985e 694 selrecord(curthread, &sc->rsel);
984263bc
MD
695 }
696 }
4e01b467 697 crit_exit();
fef8985e
MD
698 ap->a_events = revents;
699 return (0);
984263bc 700}
1550dfd9 701
96bcde04
SG
702static struct filterops ums_filtops =
703 { 1, NULL, ums_filt_detach, ums_filt };
704
705static int
706ums_kqfilter(struct dev_kqfilter_args *ap)
707{
708 cdev_t dev = ap->a_head.a_dev;
709 struct knote *kn = ap->a_kn;
710 struct ums_softc *sc;
711 struct klist *klist;
712
713 ap->a_result = 0;
714
715 switch (kn->kn_filter) {
716 case EVFILT_READ:
717 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
718 kn->kn_fop = &ums_filtops;
719 kn->kn_hook = (caddr_t)sc;
720 break;
721 default:
722 ap->a_result = 1;
723 return (0);
724 }
725
726 crit_enter();
727 klist = &sc->rsel.si_note;
728 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
729 crit_exit();
730
731 return (0);
732}
733
734static void
735ums_filt_detach(struct knote *kn)
736{
737 struct ums_softc *sc = (struct ums_softc *)kn->kn_hook;
738 struct klist *klist;
739
740 crit_enter();
741 klist = &sc->rsel.si_note;
742 SLIST_REMOVE(klist, kn, knote, kn_selnext);
743 crit_exit();
744}
745
746static int
747ums_filt(struct knote *kn, long hint)
748{
749 struct ums_softc *sc = (struct ums_softc *)kn->kn_hook;
750 int ready = 0;
751
752 crit_enter();
753 if (sc->qcount)
754 ready = 1;
755 crit_exit();
756
757 return (ready);
758}
759
984263bc 760int
fef8985e 761ums_ioctl(struct dev_ioctl_args *ap)
984263bc 762{
b13267a5 763 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
764 struct ums_softc *sc;
765 int error = 0;
984263bc
MD
766 mousemode_t mode;
767
1e089e7e 768 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
984263bc
MD
769
770 if (!sc)
771 return EIO;
772
fef8985e 773 switch(ap->a_cmd) {
984263bc 774 case MOUSE_GETHWINFO:
fef8985e 775 *(mousehw_t *)ap->a_data = sc->hw;
984263bc
MD
776 break;
777 case MOUSE_GETMODE:
fef8985e 778 *(mousemode_t *)ap->a_data = sc->mode;
984263bc
MD
779 break;
780 case MOUSE_SETMODE:
fef8985e 781 mode = *(mousemode_t *)ap->a_data;
984263bc
MD
782
783 if (mode.level == -1)
784 /* don't change the current setting */
785 ;
786 else if ((mode.level < 0) || (mode.level > 1))
787 return (EINVAL);
788
4e01b467 789 crit_enter();
984263bc
MD
790 sc->mode.level = mode.level;
791
792 if (sc->mode.level == 0) {
793 if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
794 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
795 else
796 sc->hw.buttons = sc->nbuttons;
797 sc->mode.protocol = MOUSE_PROTO_MSC;
798 sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
799 sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
800 sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
801 } else if (sc->mode.level == 1) {
802 if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
803 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
804 else
805 sc->hw.buttons = sc->nbuttons;
806 sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
807 sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
808 sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
809 sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
810 }
811
812 bzero(sc->qbuf, sizeof(sc->qbuf));
813 sc->qhead = sc->qtail = sc->qcount = 0;
4e01b467 814 crit_exit();
984263bc
MD
815
816 break;
817 case MOUSE_GETLEVEL:
fef8985e 818 *(int *)ap->a_data = sc->mode.level;
984263bc
MD
819 break;
820 case MOUSE_SETLEVEL:
fef8985e 821 if (*(int *)ap->a_data < 0 || *(int *)ap->a_data > 1)
984263bc
MD
822 return (EINVAL);
823
4e01b467 824 crit_enter();
fef8985e 825 sc->mode.level = *(int *)ap->a_data;
984263bc
MD
826
827 if (sc->mode.level == 0) {
828 if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
829 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
830 else
831 sc->hw.buttons = sc->nbuttons;
832 sc->mode.protocol = MOUSE_PROTO_MSC;
833 sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
834 sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
835 sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
836 } else if (sc->mode.level == 1) {
837 if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
838 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
839 else
840 sc->hw.buttons = sc->nbuttons;
841 sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
842 sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
843 sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
844 sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
845 }
846
847 bzero(sc->qbuf, sizeof(sc->qbuf));
848 sc->qhead = sc->qtail = sc->qcount = 0;
4e01b467 849 crit_exit();
984263bc
MD
850
851 break;
852 case MOUSE_GETSTATUS: {
fef8985e 853 mousestatus_t *status = (mousestatus_t *) ap->a_data;
984263bc 854
4e01b467 855 crit_enter();
984263bc
MD
856 *status = sc->status;
857 sc->status.obutton = sc->status.button;
858 sc->status.button = 0;
859 sc->status.dx = sc->status.dy = sc->status.dz = 0;
4e01b467 860 crit_exit();
984263bc
MD
861
862 if (status->dx || status->dy || status->dz)
863 status->flags |= MOUSE_POSCHANGED;
864 if (status->button != status->obutton)
865 status->flags |= MOUSE_BUTTONSCHANGED;
866 break;
867 }
868 default:
869 error = ENOTTY;
870 }
871
872 return error;
873}
874
875DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);