Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / sys / dev / usbmisc / uhid / uhid.c
CommitLineData
1550dfd9
MD
1/*
2 * $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $
3 * $FreeBSD: src/sys/dev/usb/uhid.c,v 1.65 2003/11/09 09:17:22 tanimura Exp $
b13267a5 4 * $DragonFly: src/sys/dev/usbmisc/uhid/uhid.c,v 1.19 2006/09/10 01:26:37 dillon Exp $
1550dfd9
MD
5 */
6
7/* Also already merged from NetBSD:
8 * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $
9 */
984263bc
MD
10
11/*
12 * Copyright (c) 1998 The NetBSD Foundation, Inc.
13 * All rights reserved.
14 *
15 * This code is derived from software contributed to The NetBSD Foundation
16 * by Lennart Augustsson (lennart@augustsson.net) at
17 * Carlstedt Research & Technology.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions
21 * are met:
22 * 1. Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * 3. All advertising materials mentioning features or use of this software
28 * must display the following acknowledgement:
29 * This product includes software developed by the NetBSD
30 * Foundation, Inc. and its contributors.
31 * 4. Neither the name of The NetBSD Foundation nor the names of its
32 * contributors may be used to endorse or promote products derived
33 * from this software without specific prior written permission.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
36 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
37 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
39 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45 * POSSIBILITY OF SUCH DAMAGE.
46 */
47
48/*
49 * HID spec: http://www.usb.org/developers/data/usbhid10.pdf
50 */
51
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/kernel.h>
1550dfd9 55#include <sys/lock.h>
984263bc 56#include <sys/malloc.h>
d47ac1a2 57#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
1550dfd9
MD
58#include <sys/mutex.h>
59#endif
984263bc
MD
60#include <sys/signalvar.h>
61#if defined(__NetBSD__) || defined(__OpenBSD__)
62#include <sys/device.h>
63#include <sys/ioctl.h>
1550dfd9 64#include <sys/file.h>
d47ac1a2 65#elif defined(__FreeBSD__) || defined(__DragonFly__)
984263bc
MD
66#include <sys/ioccom.h>
67#include <sys/filio.h>
68#include <sys/module.h>
69#include <sys/bus.h>
70#include <sys/ioccom.h>
71#endif
72#include <sys/conf.h>
73#include <sys/tty.h>
d47ac1a2 74#if defined(__FreeBSD__) && __FreeBSD_version >= 500014
1550dfd9
MD
75#include <sys/selinfo.h>
76#else
984263bc 77#include <sys/select.h>
1550dfd9 78#endif
984263bc
MD
79#include <sys/proc.h>
80#include <sys/vnode.h>
81#include <sys/poll.h>
82#include <sys/sysctl.h>
4e01b467 83#include <sys/thread2.h>
984263bc 84
1f2de5d4
MD
85#include <bus/usb/usb.h>
86#include <bus/usb/usbhid.h>
984263bc 87
1550dfd9 88#include <bus/usb/usbdevs.h>
1f2de5d4
MD
89#include <bus/usb/usbdi.h>
90#include <bus/usb/usbdi_util.h>
91#include <bus/usb/hid.h>
984263bc 92
1550dfd9
MD
93/* Report descriptor for broken Wacom Graphire */
94#include <bus/usb/ugraphire_rdesc.h>
984263bc
MD
95
96#ifdef USB_DEBUG
97#define DPRINTF(x) if (uhiddebug) logprintf x
98#define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x
99int uhiddebug = 0;
100SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
101SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW,
102 &uhiddebug, 0, "uhid debug level");
103#else
104#define DPRINTF(x)
105#define DPRINTFN(n,x)
106#endif
107
108struct uhid_softc {
109 USBBASEDEVICE sc_dev; /* base device */
110 usbd_device_handle sc_udev;
111 usbd_interface_handle sc_iface; /* interface */
112 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
113 int sc_ep_addr;
114
115 int sc_isize;
116 int sc_osize;
117 int sc_fsize;
118 u_int8_t sc_iid;
119 u_int8_t sc_oid;
120 u_int8_t sc_fid;
121
122 u_char *sc_ibuf;
123 u_char *sc_obuf;
124
125 void *sc_repdesc;
126 int sc_repdesc_size;
127
128 struct clist sc_q;
129 struct selinfo sc_rsel;
130 struct proc *sc_async; /* process that wants SIGIO */
131 u_char sc_state; /* driver state */
132#define UHID_OPEN 0x01 /* device is open */
133#define UHID_ASLP 0x02 /* waiting for device data */
134#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
135#define UHID_IMMED 0x08 /* return read data immediately */
136
137 int sc_refcnt;
138 u_char sc_dying;
984263bc
MD
139};
140
141#define UHIDUNIT(dev) (minor(dev))
142#define UHID_CHUNK 128 /* chunk size for read */
143#define UHID_BSIZE 1020 /* buffer size */
144
145#if defined(__NetBSD__) || defined(__OpenBSD__)
146cdev_decl(uhid);
d47ac1a2 147#elif defined(__FreeBSD__) || defined(__DragonFly__)
984263bc
MD
148d_open_t uhidopen;
149d_close_t uhidclose;
150d_read_t uhidread;
151d_write_t uhidwrite;
152d_ioctl_t uhidioctl;
153d_poll_t uhidpoll;
154
155#define UHID_CDEV_MAJOR 122
156
fef8985e
MD
157Static struct dev_ops uhid_ops = {
158 { "uhid", UHID_CDEV_MAJOR, 0 },
159 .d_open = uhidopen,
160 .d_close = uhidclose,
161 .d_read = uhidread,
162 .d_write = uhidwrite,
163 .d_ioctl = uhidioctl,
164 .d_poll = uhidpoll,
984263bc
MD
165};
166#endif
167
168Static void uhid_intr(usbd_xfer_handle, usbd_private_handle,
169 usbd_status);
170
171Static int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
172Static int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
fef8985e 173Static int uhid_do_ioctl(struct uhid_softc *, u_long, caddr_t, int);
984263bc
MD
174
175USB_DECLARE_DRIVER(uhid);
176
177USB_MATCH(uhid)
178{
179 USB_MATCH_START(uhid, uaa);
180 usb_interface_descriptor_t *id;
1550dfd9 181
984263bc
MD
182 if (uaa->iface == NULL)
183 return (UMATCH_NONE);
184 id = usbd_get_interface_descriptor(uaa->iface);
185 if (id == NULL || id->bInterfaceClass != UICLASS_HID)
186 return (UMATCH_NONE);
1550dfd9
MD
187 if (uaa->matchlvl)
188 return (uaa->matchlvl);
984263bc
MD
189 return (UMATCH_IFACECLASS_GENERIC);
190}
191
192USB_ATTACH(uhid)
193{
194 USB_ATTACH_START(uhid, sc, uaa);
195 usbd_interface_handle iface = uaa->iface;
196 usb_interface_descriptor_t *id;
197 usb_endpoint_descriptor_t *ed;
198 int size;
199 void *desc;
200 usbd_status err;
201 char devinfo[1024];
1550dfd9 202
984263bc
MD
203 sc->sc_udev = uaa->device;
204 sc->sc_iface = iface;
205 id = usbd_get_interface_descriptor(iface);
206 usbd_devinfo(uaa->device, 0, devinfo);
207 USB_ATTACH_SETUP;
208 printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
209 devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
210
211 ed = usbd_interface2endpoint_descriptor(iface, 0);
212 if (ed == NULL) {
213 printf("%s: could not read endpoint descriptor\n",
214 USBDEVNAME(sc->sc_dev));
215 sc->sc_dying = 1;
216 USB_ATTACH_ERROR_RETURN;
217 }
218
219 DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d "
220 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
221 " bInterval=%d\n",
1550dfd9 222 ed->bLength, ed->bDescriptorType,
984263bc
MD
223 ed->bEndpointAddress & UE_ADDR,
224 UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
225 ed->bmAttributes & UE_XFERTYPE,
226 UGETW(ed->wMaxPacketSize), ed->bInterval));
227
228 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
229 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
230 printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev));
231 sc->sc_dying = 1;
232 USB_ATTACH_ERROR_RETURN;
233 }
234
235 sc->sc_ep_addr = ed->bEndpointAddress;
236
1550dfd9
MD
237 if (uaa->vendor == USB_VENDOR_WACOM &&
238 uaa->product == USB_PRODUCT_WACOM_GRAPHIRE /* &&
239 uaa->revision == 0x???? */) { /* XXX should use revision */
240 /* The report descriptor for the Wacom Graphire is broken. */
241 size = sizeof uhid_graphire_report_descr;
efda3bd0 242 desc = kmalloc(size, M_USBDEV, M_INTWAIT);
3aed1355
MD
243 err = USBD_NORMAL_COMPLETION;
244 memcpy(desc, uhid_graphire_report_descr, size);
1550dfd9
MD
245 } else {
246 desc = NULL;
247 err = usbd_read_report_desc(uaa->iface, &desc, &size,M_USBDEV);
248 }
249
984263bc
MD
250 if (err) {
251 printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev));
252 sc->sc_dying = 1;
253 USB_ATTACH_ERROR_RETURN;
254 }
1550dfd9 255
984263bc
MD
256 (void)usbd_set_idle(iface, 0, 0);
257
258 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
259 sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
260 sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
261
262 sc->sc_repdesc = desc;
263 sc->sc_repdesc_size = size;
264
d47ac1a2 265#if defined(__FreeBSD__) || defined(__DragonFly__)
fef8985e
MD
266 dev_ops_add(&uhid_ops, -1, device_get_unit(self));
267 make_dev(&uhid_ops, device_get_unit(self),
e4c9c0c8
MD
268 UID_ROOT, GID_OPERATOR,
269 0644, "uhid%d", device_get_unit(self));
984263bc
MD
270#endif
271
272 USB_ATTACH_SUCCESS_RETURN;
273}
274
275#if defined(__NetBSD__) || defined(__OpenBSD__)
276int
277uhid_activate(device_ptr_t self, enum devact act)
278{
279 struct uhid_softc *sc = (struct uhid_softc *)self;
280
281 switch (act) {
282 case DVACT_ACTIVATE:
283 return (EOPNOTSUPP);
984263bc
MD
284
285 case DVACT_DEACTIVATE:
286 sc->sc_dying = 1;
287 break;
288 }
289 return (0);
290}
291#endif
292
293USB_DETACH(uhid)
294{
295 USB_DETACH_START(uhid, sc);
984263bc
MD
296#if defined(__NetBSD__) || defined(__OpenBSD__)
297 int maj, mn;
984263bc
MD
298#endif
299
300#if defined(__NetBSD__) || defined(__OpenBSD__)
301 DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
302#else
303 DPRINTF(("uhid_detach: sc=%p\n", sc));
304#endif
305
306 sc->sc_dying = 1;
307 if (sc->sc_intrpipe != NULL)
308 usbd_abort_pipe(sc->sc_intrpipe);
309
310 if (sc->sc_state & UHID_OPEN) {
4e01b467 311 crit_enter();
984263bc
MD
312 if (--sc->sc_refcnt >= 0) {
313 /* Wake everyone */
314 wakeup(&sc->sc_q);
315 /* Wait for processes to go away. */
316 usb_detach_wait(USBDEV(sc->sc_dev));
317 }
4e01b467 318 crit_exit();
984263bc
MD
319 }
320
321#if defined(__NetBSD__) || defined(__OpenBSD__)
322 /* locate the major number */
323 for (maj = 0; maj < nchrdev; maj++)
324 if (cdevsw[maj].d_open == uhidopen)
325 break;
326
327 /* Nuke the vnodes for any open instances (calls close). */
328 mn = self->dv_unit;
329 vdevgone(maj, mn, mn, VCHR);
d47ac1a2 330#elif defined(__FreeBSD__) || defined(__DragonFly__)
fef8985e 331 dev_ops_remove(&uhid_ops, -1, device_get_unit(self));
984263bc
MD
332#endif
333
1550dfd9 334 if (sc->sc_repdesc)
efda3bd0 335 kfree(sc->sc_repdesc, M_USBDEV);
984263bc
MD
336
337 return (0);
338}
339
340void
341uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
342{
343 struct uhid_softc *sc = addr;
344
345#ifdef USB_DEBUG
346 if (uhiddebug > 5) {
347 u_int32_t cc, i;
1550dfd9 348
984263bc
MD
349 usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
350 DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc));
351 DPRINTF(("uhid_intr: data ="));
352 for (i = 0; i < cc; i++)
353 DPRINTF((" %02x", sc->sc_ibuf[i]));
354 DPRINTF(("\n"));
355 }
356#endif
357
358 if (status == USBD_CANCELLED)
359 return;
360
361 if (status != USBD_NORMAL_COMPLETION) {
362 DPRINTF(("uhid_intr: status=%d\n", status));
1550dfd9
MD
363 if (status == USBD_STALLED)
364 sc->sc_state |= UHID_NEEDCLEAR;
984263bc
MD
365 return;
366 }
367
368 (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
1550dfd9 369
984263bc
MD
370 if (sc->sc_state & UHID_ASLP) {
371 sc->sc_state &= ~UHID_ASLP;
1550dfd9 372 DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
984263bc
MD
373 wakeup(&sc->sc_q);
374 }
518fcd30 375 selwakeuppri(&sc->sc_rsel, 0);
984263bc
MD
376 if (sc->sc_async != NULL) {
377 DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async));
1550dfd9 378 PROC_LOCK(sc->sc_async);
84204577 379 ksignal(sc->sc_async, SIGIO);
1550dfd9 380 PROC_UNLOCK(sc->sc_async);
984263bc
MD
381 }
382}
383
384int
fef8985e 385uhidopen(struct dev_open_args *ap)
984263bc 386{
b13267a5 387 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
388 struct uhid_softc *sc;
389 usbd_status err;
984263bc
MD
390
391 USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc);
392
393 DPRINTF(("uhidopen: sc=%p\n", sc));
394
395 if (sc->sc_dying)
396 return (ENXIO);
397
398 if (sc->sc_state & UHID_OPEN)
399 return (EBUSY);
400 sc->sc_state |= UHID_OPEN;
401
402 if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
403 sc->sc_state &= ~UHID_OPEN;
404 return (ENOMEM);
405 }
406
efda3bd0
MD
407 sc->sc_ibuf = kmalloc(sc->sc_isize, M_USBDEV, M_WAITOK);
408 sc->sc_obuf = kmalloc(sc->sc_osize, M_USBDEV, M_WAITOK);
984263bc
MD
409
410 /* Set up interrupt pipe. */
1550dfd9
MD
411 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
412 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf,
984263bc
MD
413 sc->sc_isize, uhid_intr, USBD_DEFAULT_INTERVAL);
414 if (err) {
415 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
416 "error=%d\n",err));
efda3bd0
MD
417 kfree(sc->sc_ibuf, M_USBDEV);
418 kfree(sc->sc_obuf, M_USBDEV);
1550dfd9
MD
419 sc->sc_ibuf = sc->sc_obuf = NULL;
420
984263bc
MD
421 sc->sc_state &= ~UHID_OPEN;
422 return (EIO);
423 }
424
425 sc->sc_state &= ~UHID_IMMED;
426
427 sc->sc_async = 0;
428
429 return (0);
430}
431
432int
fef8985e 433uhidclose(struct dev_close_args *ap)
984263bc 434{
b13267a5 435 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
436 struct uhid_softc *sc;
437
438 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
439
440 DPRINTF(("uhidclose: sc=%p\n", sc));
441
442 /* Disable interrupts. */
443 usbd_abort_pipe(sc->sc_intrpipe);
444 usbd_close_pipe(sc->sc_intrpipe);
445 sc->sc_intrpipe = 0;
446
447 ndflush(&sc->sc_q, sc->sc_q.c_cc);
448 clfree(&sc->sc_q);
449
efda3bd0
MD
450 kfree(sc->sc_ibuf, M_USBDEV);
451 kfree(sc->sc_obuf, M_USBDEV);
1550dfd9 452 sc->sc_ibuf = sc->sc_obuf = NULL;
984263bc
MD
453
454 sc->sc_state &= ~UHID_OPEN;
455
456 sc->sc_async = 0;
457
458 return (0);
459}
460
461int
462uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
463{
984263bc
MD
464 int error = 0;
465 size_t length;
466 u_char buffer[UHID_CHUNK];
467 usbd_status err;
468
469 DPRINTFN(1, ("uhidread\n"));
470 if (sc->sc_state & UHID_IMMED) {
471 DPRINTFN(1, ("uhidread immed\n"));
1550dfd9 472
984263bc
MD
473 err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
474 sc->sc_iid, buffer, sc->sc_isize);
475 if (err)
476 return (EIO);
477 return (uiomove(buffer, sc->sc_isize, uio));
478 }
479
4e01b467 480 crit_enter();
984263bc
MD
481 while (sc->sc_q.c_cc == 0) {
482 if (flag & IO_NDELAY) {
4e01b467 483 crit_exit();
984263bc
MD
484 return (EWOULDBLOCK);
485 }
486 sc->sc_state |= UHID_ASLP;
1550dfd9 487 DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
377d4740 488 error = tsleep(&sc->sc_q, PCATCH, "uhidrea", 0);
984263bc
MD
489 DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
490 if (sc->sc_dying)
491 error = EIO;
492 if (error) {
493 sc->sc_state &= ~UHID_ASLP;
494 break;
495 }
496 if (sc->sc_state & UHID_NEEDCLEAR) {
497 DPRINTFN(-1,("uhidread: clearing stall\n"));
498 sc->sc_state &= ~UHID_NEEDCLEAR;
499 usbd_clear_endpoint_stall(sc->sc_intrpipe);
500 }
501 }
4e01b467 502 crit_exit();
984263bc
MD
503
504 /* Transfer as many chunks as possible. */
505 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
506 length = min(sc->sc_q.c_cc, uio->uio_resid);
507 if (length > sizeof(buffer))
508 length = sizeof(buffer);
509
510 /* Remove a small chunk from the input queue. */
511 (void) q_to_b(&sc->sc_q, buffer, length);
512 DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length));
513
514 /* Copy the data to the user process. */
515 if ((error = uiomove(buffer, length, uio)) != 0)
516 break;
517 }
518
519 return (error);
520}
521
522int
fef8985e 523uhidread(struct dev_read_args *ap)
984263bc 524{
b13267a5 525 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
526 struct uhid_softc *sc;
527 int error;
528
529 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
530
531 sc->sc_refcnt++;
fef8985e 532 error = uhid_do_read(sc, ap->a_uio, ap->a_ioflag);
984263bc
MD
533 if (--sc->sc_refcnt < 0)
534 usb_detach_wakeup(USBDEV(sc->sc_dev));
535 return (error);
536}
537
538int
539uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
540{
541 int error;
542 int size;
543 usbd_status err;
544
545 DPRINTFN(1, ("uhidwrite\n"));
1550dfd9 546
984263bc
MD
547 if (sc->sc_dying)
548 return (EIO);
549
550 size = sc->sc_osize;
551 error = 0;
552 if (uio->uio_resid != size)
553 return (EINVAL);
554 error = uiomove(sc->sc_obuf, size, uio);
555 if (!error) {
556 if (sc->sc_oid)
557 err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
558 sc->sc_obuf[0], sc->sc_obuf+1, size-1);
559 else
560 err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
561 0, sc->sc_obuf, size);
562 if (err)
563 error = EIO;
564 }
565
566 return (error);
567}
568
569int
fef8985e 570uhidwrite(struct dev_write_args *ap)
984263bc 571{
b13267a5 572 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
573 struct uhid_softc *sc;
574 int error;
575
576 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
577
578 sc->sc_refcnt++;
fef8985e 579 error = uhid_do_write(sc, ap->a_uio, ap->a_ioflag);
984263bc
MD
580 if (--sc->sc_refcnt < 0)
581 usb_detach_wakeup(USBDEV(sc->sc_dev));
582 return (error);
583}
584
585int
fef8985e 586uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag)
984263bc
MD
587{
588 struct usb_ctl_report_desc *rd;
589 struct usb_ctl_report *re;
590 int size, id;
591 usbd_status err;
592
593 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
594
595 if (sc->sc_dying)
596 return (EIO);
597
598 switch (cmd) {
984263bc
MD
599 case FIOASYNC:
600 if (*(int *)addr) {
601 if (sc->sc_async != NULL)
602 return (EBUSY);
1550dfd9 603#if defined(__DragonFly__)
fef8985e 604 sc->sc_async = curproc;
1550dfd9 605#elif __FreeBSD_version >= 500000
41c20dac 606 sc->sc_async = p->td_proc;
1550dfd9
MD
607#else
608 sc->sc_async = p;
609#endif
610 DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", sc->sc_async));
984263bc
MD
611 } else
612 sc->sc_async = NULL;
613 break;
614
615 /* XXX this is not the most general solution. */
616 case TIOCSPGRP:
617 if (sc->sc_async == NULL)
618 return (EINVAL);
619 if (*(int *)addr != sc->sc_async->p_pgid)
620 return (EPERM);
621 break;
622
623 case USB_GET_REPORT_DESC:
624 rd = (struct usb_ctl_report_desc *)addr;
625 size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data);
626 rd->ucrd_size = size;
627 memcpy(rd->ucrd_data, sc->sc_repdesc, size);
628 break;
629
630 case USB_SET_IMMED:
631 if (*(int *)addr) {
632 /* XXX should read into ibuf, but does it matter? */
633 err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
634 sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
635 if (err)
636 return (EOPNOTSUPP);
637
638 sc->sc_state |= UHID_IMMED;
639 } else
640 sc->sc_state &= ~UHID_IMMED;
641 break;
642
643 case USB_GET_REPORT:
644 re = (struct usb_ctl_report *)addr;
645 switch (re->ucr_report) {
646 case UHID_INPUT_REPORT:
647 size = sc->sc_isize;
648 id = sc->sc_iid;
649 break;
650 case UHID_OUTPUT_REPORT:
651 size = sc->sc_osize;
652 id = sc->sc_oid;
653 break;
654 case UHID_FEATURE_REPORT:
655 size = sc->sc_fsize;
656 id = sc->sc_fid;
657 break;
658 default:
659 return (EINVAL);
660 }
661 err = usbd_get_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
662 size);
663 if (err)
664 return (EIO);
665 break;
666
667 case USB_SET_REPORT:
668 re = (struct usb_ctl_report *)addr;
669 switch (re->ucr_report) {
670 case UHID_INPUT_REPORT:
671 size = sc->sc_isize;
672 id = sc->sc_iid;
673 break;
674 case UHID_OUTPUT_REPORT:
675 size = sc->sc_osize;
676 id = sc->sc_oid;
677 break;
678 case UHID_FEATURE_REPORT:
679 size = sc->sc_fsize;
680 id = sc->sc_fid;
681 break;
682 default:
683 return (EINVAL);
684 }
685 err = usbd_set_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
686 size);
687 if (err)
688 return (EIO);
689 break;
690
1550dfd9
MD
691 case USB_GET_REPORT_ID:
692 *(int *)addr = 0; /* XXX: we only support reportid 0? */
693 break;
694
984263bc
MD
695 default:
696 return (EINVAL);
697 }
698 return (0);
699}
700
701int
fef8985e 702uhidioctl(struct dev_ioctl_args *ap)
984263bc 703{
b13267a5 704 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
705 struct uhid_softc *sc;
706 int error;
707
708 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
709
710 sc->sc_refcnt++;
fef8985e 711 error = uhid_do_ioctl(sc, ap->a_cmd, ap->a_data, ap->a_fflag);
984263bc
MD
712 if (--sc->sc_refcnt < 0)
713 usb_detach_wakeup(USBDEV(sc->sc_dev));
714 return (error);
715}
716
717int
fef8985e 718uhidpoll(struct dev_poll_args *ap)
984263bc 719{
b13267a5 720 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
721 struct uhid_softc *sc;
722 int revents = 0;
984263bc
MD
723
724 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
725
726 if (sc->sc_dying)
727 return (EIO);
728
4e01b467 729 crit_enter();
fef8985e
MD
730 if (ap->a_events & (POLLOUT | POLLWRNORM))
731 revents |= ap->a_events & (POLLOUT | POLLWRNORM);
732 if (ap->a_events & (POLLIN | POLLRDNORM)) {
984263bc 733 if (sc->sc_q.c_cc > 0)
fef8985e 734 revents |= ap->a_events & (POLLIN | POLLRDNORM);
984263bc 735 else
fef8985e 736 selrecord(curthread, &sc->sc_rsel);
984263bc 737 }
4e01b467 738 crit_exit();
fef8985e
MD
739 ap->a_events = revents;
740 return (0);
984263bc
MD
741}
742
d47ac1a2 743#if defined(__FreeBSD__) || defined(__DragonFly__)
984263bc
MD
744DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0);
745#endif