Annotation of sys/dev/usb/uhid.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: uhid.c,v 1.41 2007/06/14 10:11:15 mbalmer Exp $ */
! 2: /* $NetBSD: uhid.c,v 1.57 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/signalvar.h>
! 50: #include <sys/device.h>
! 51: #include <sys/ioctl.h>
! 52: #include <sys/conf.h>
! 53: #include <sys/tty.h>
! 54: #include <sys/file.h>
! 55: #include <sys/selinfo.h>
! 56: #include <sys/proc.h>
! 57: #include <sys/vnode.h>
! 58: #include <sys/poll.h>
! 59:
! 60: #include <dev/usb/usb.h>
! 61: #include <dev/usb/usbhid.h>
! 62:
! 63: #include <dev/usb/usbdevs.h>
! 64: #include <dev/usb/usbdi.h>
! 65: #include <dev/usb/usbdi_util.h>
! 66: #include <dev/usb/hid.h>
! 67: #include <dev/usb/usb_quirks.h>
! 68:
! 69: #include <dev/usb/uhidev.h>
! 70:
! 71: #ifdef UHID_DEBUG
! 72: #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0)
! 73: #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0)
! 74: int uhiddebug = 0;
! 75: #else
! 76: #define DPRINTF(x)
! 77: #define DPRINTFN(n,x)
! 78: #endif
! 79:
! 80: struct uhid_softc {
! 81: struct uhidev sc_hdev;
! 82:
! 83: int sc_isize;
! 84: int sc_osize;
! 85: int sc_fsize;
! 86:
! 87: u_char *sc_obuf;
! 88:
! 89: struct clist sc_q;
! 90: struct selinfo sc_rsel;
! 91: struct proc *sc_async; /* process that wants SIGIO */
! 92: u_char sc_state; /* driver state */
! 93: #define UHID_ASLP 0x01 /* waiting for device data */
! 94: #define UHID_IMMED 0x02 /* return read data immediately */
! 95:
! 96: int sc_refcnt;
! 97: u_char sc_dying;
! 98: };
! 99:
! 100: #define UHIDUNIT(dev) (minor(dev))
! 101: #define UHID_CHUNK 128 /* chunk size for read */
! 102: #define UHID_BSIZE 1020 /* buffer size */
! 103:
! 104: void uhid_intr(struct uhidev *, void *, u_int len);
! 105:
! 106: int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
! 107: int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
! 108: int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int,
! 109: struct proc *);
! 110:
! 111: int uhid_match(struct device *, void *, void *);
! 112: void uhid_attach(struct device *, struct device *, void *);
! 113: int uhid_detach(struct device *, int);
! 114: int uhid_activate(struct device *, enum devact);
! 115:
! 116: struct cfdriver uhid_cd = {
! 117: NULL, "uhid", DV_DULL
! 118: };
! 119:
! 120: const struct cfattach uhid_ca = {
! 121: sizeof(struct uhid_softc),
! 122: uhid_match,
! 123: uhid_attach,
! 124: uhid_detach,
! 125: uhid_activate,
! 126: };
! 127:
! 128: int
! 129: uhid_match(struct device *parent, void *match, void *aux)
! 130: {
! 131: struct usb_attach_arg *uaa = aux;
! 132: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
! 133:
! 134: DPRINTF(("uhid_match: report=%d\n", uha->reportid));
! 135:
! 136: if (uha->matchlvl)
! 137: return (uha->matchlvl);
! 138: return (UMATCH_IFACECLASS_GENERIC);
! 139: }
! 140:
! 141: void
! 142: uhid_attach(struct device *parent, struct device *self, void *aux)
! 143: {
! 144: struct uhid_softc *sc = (struct uhid_softc *)self;
! 145: struct usb_attach_arg *uaa = aux;
! 146: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
! 147: int size, repid;
! 148: void *desc;
! 149:
! 150: sc->sc_hdev.sc_intr = uhid_intr;
! 151: sc->sc_hdev.sc_parent = uha->parent;
! 152: sc->sc_hdev.sc_report_id = uha->reportid;
! 153:
! 154: uhidev_get_report_desc(uha->parent, &desc, &size);
! 155: repid = uha->reportid;
! 156: sc->sc_isize = hid_report_size(desc, size, hid_input, repid);
! 157: sc->sc_osize = hid_report_size(desc, size, hid_output, repid);
! 158: sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid);
! 159:
! 160: printf(": input=%d, output=%d, feature=%d\n",
! 161: sc->sc_isize, sc->sc_osize, sc->sc_fsize);
! 162: }
! 163:
! 164: int
! 165: uhid_activate(struct device *self, enum devact act)
! 166: {
! 167: struct uhid_softc *sc = (struct uhid_softc *)self;
! 168:
! 169: switch (act) {
! 170: case DVACT_ACTIVATE:
! 171: break;
! 172:
! 173: case DVACT_DEACTIVATE:
! 174: sc->sc_dying = 1;
! 175: break;
! 176: }
! 177: return (0);
! 178: }
! 179:
! 180: int
! 181: uhid_detach(struct device *self, int flags)
! 182: {
! 183: struct uhid_softc *sc = (struct uhid_softc *)self;
! 184: int s;
! 185: int maj, mn;
! 186:
! 187: DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
! 188:
! 189: sc->sc_dying = 1;
! 190:
! 191: if (sc->sc_hdev.sc_state & UHIDEV_OPEN) {
! 192: s = splusb();
! 193: if (--sc->sc_refcnt >= 0) {
! 194: /* Wake everyone */
! 195: wakeup(&sc->sc_q);
! 196: /* Wait for processes to go away. */
! 197: usb_detach_wait(&sc->sc_hdev.sc_dev);
! 198: }
! 199: splx(s);
! 200: }
! 201:
! 202: /* locate the major number */
! 203: for (maj = 0; maj < nchrdev; maj++)
! 204: if (cdevsw[maj].d_open == uhidopen)
! 205: break;
! 206:
! 207: /* Nuke the vnodes for any open instances (calls close). */
! 208: mn = self->dv_unit;
! 209: vdevgone(maj, mn, mn, VCHR);
! 210:
! 211: #if 0
! 212: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH,
! 213: sc->sc_hdev.sc_parent->sc_udev,
! 214: &sc->sc_hdev.sc_dev);
! 215: #endif
! 216:
! 217: return (0);
! 218: }
! 219:
! 220: void
! 221: uhid_intr(struct uhidev *addr, void *data, u_int len)
! 222: {
! 223: struct uhid_softc *sc = (struct uhid_softc *)addr;
! 224:
! 225: #ifdef UHID_DEBUG
! 226: if (uhiddebug > 5) {
! 227: u_int32_t i;
! 228:
! 229: DPRINTF(("uhid_intr: data ="));
! 230: for (i = 0; i < len; i++)
! 231: DPRINTF((" %02x", ((u_char *)data)[i]));
! 232: DPRINTF(("\n"));
! 233: }
! 234: #endif
! 235:
! 236: (void)b_to_q(data, len, &sc->sc_q);
! 237:
! 238: if (sc->sc_state & UHID_ASLP) {
! 239: sc->sc_state &= ~UHID_ASLP;
! 240: DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
! 241: wakeup(&sc->sc_q);
! 242: }
! 243: selwakeup(&sc->sc_rsel);
! 244: if (sc->sc_async != NULL) {
! 245: DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async));
! 246: psignal(sc->sc_async, SIGIO);
! 247: }
! 248: }
! 249:
! 250: int
! 251: uhidopen(dev_t dev, int flag, int mode, struct proc *p)
! 252: {
! 253: struct uhid_softc *sc;
! 254: int error;
! 255:
! 256: if (UHIDUNIT(dev) >= uhid_cd.cd_ndevs)
! 257: return (ENXIO);
! 258: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 259: if (sc == NULL)
! 260: return (ENXIO);
! 261:
! 262: DPRINTF(("uhidopen: sc=%p\n", sc));
! 263:
! 264: if (sc->sc_dying)
! 265: return (ENXIO);
! 266:
! 267: error = uhidev_open(&sc->sc_hdev);
! 268: if (error)
! 269: return (error);
! 270:
! 271: if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
! 272: uhidev_close(&sc->sc_hdev);
! 273: return (ENOMEM);
! 274: }
! 275: sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK);
! 276: sc->sc_state &= ~UHID_IMMED;
! 277: sc->sc_async = NULL;
! 278:
! 279: return (0);
! 280: }
! 281:
! 282: int
! 283: uhidclose(dev_t dev, int flag, int mode, struct proc *p)
! 284: {
! 285: struct uhid_softc *sc;
! 286:
! 287: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 288:
! 289: DPRINTF(("uhidclose: sc=%p\n", sc));
! 290:
! 291: clfree(&sc->sc_q);
! 292: free(sc->sc_obuf, M_USBDEV);
! 293: sc->sc_async = NULL;
! 294: uhidev_close(&sc->sc_hdev);
! 295:
! 296: return (0);
! 297: }
! 298:
! 299: int
! 300: uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
! 301: {
! 302: int s;
! 303: int error = 0;
! 304: int extra;
! 305: size_t length;
! 306: u_char buffer[UHID_CHUNK];
! 307: usbd_status err;
! 308:
! 309: DPRINTFN(1, ("uhidread\n"));
! 310: if (sc->sc_state & UHID_IMMED) {
! 311: DPRINTFN(1, ("uhidread immed\n"));
! 312: extra = sc->sc_hdev.sc_report_id != 0;
! 313: err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT,
! 314: buffer, sc->sc_isize + extra);
! 315: if (err)
! 316: return (EIO);
! 317: return (uiomove(buffer+extra, sc->sc_isize, uio));
! 318: }
! 319:
! 320: s = splusb();
! 321: while (sc->sc_q.c_cc == 0) {
! 322: if (flag & IO_NDELAY) {
! 323: splx(s);
! 324: return (EWOULDBLOCK);
! 325: }
! 326: sc->sc_state |= UHID_ASLP;
! 327: DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
! 328: error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
! 329: DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
! 330: if (sc->sc_dying)
! 331: error = EIO;
! 332: if (error) {
! 333: sc->sc_state &= ~UHID_ASLP;
! 334: break;
! 335: }
! 336: }
! 337: splx(s);
! 338:
! 339: /* Transfer as many chunks as possible. */
! 340: while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
! 341: length = min(sc->sc_q.c_cc, uio->uio_resid);
! 342: if (length > sizeof(buffer))
! 343: length = sizeof(buffer);
! 344:
! 345: /* Remove a small chunk from the input queue. */
! 346: (void) q_to_b(&sc->sc_q, buffer, length);
! 347: DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length));
! 348:
! 349: /* Copy the data to the user process. */
! 350: if ((error = uiomove(buffer, length, uio)) != 0)
! 351: break;
! 352: }
! 353:
! 354: return (error);
! 355: }
! 356:
! 357: int
! 358: uhidread(dev_t dev, struct uio *uio, int flag)
! 359: {
! 360: struct uhid_softc *sc;
! 361: int error;
! 362:
! 363: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 364:
! 365: sc->sc_refcnt++;
! 366: error = uhid_do_read(sc, uio, flag);
! 367: if (--sc->sc_refcnt < 0)
! 368: usb_detach_wakeup(&sc->sc_hdev.sc_dev);
! 369: return (error);
! 370: }
! 371:
! 372: int
! 373: uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
! 374: {
! 375: int error;
! 376: int size;
! 377: usbd_status err;
! 378:
! 379: DPRINTFN(1, ("uhidwrite\n"));
! 380:
! 381: if (sc->sc_dying)
! 382: return (EIO);
! 383:
! 384: size = sc->sc_osize;
! 385: error = 0;
! 386: if (uio->uio_resid != size)
! 387: return (EINVAL);
! 388: error = uiomove(sc->sc_obuf, size, uio);
! 389: if (!error) {
! 390: err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT,
! 391: sc->sc_obuf, size);
! 392: if (err)
! 393: error = EIO;
! 394: }
! 395:
! 396: return (error);
! 397: }
! 398:
! 399: int
! 400: uhidwrite(dev_t dev, struct uio *uio, int flag)
! 401: {
! 402: struct uhid_softc *sc;
! 403: int error;
! 404:
! 405: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 406:
! 407: sc->sc_refcnt++;
! 408: error = uhid_do_write(sc, uio, flag);
! 409: if (--sc->sc_refcnt < 0)
! 410: usb_detach_wakeup(&sc->sc_hdev.sc_dev);
! 411: return (error);
! 412: }
! 413:
! 414: int
! 415: uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr,
! 416: int flag, struct proc *p)
! 417: {
! 418: struct usb_ctl_report_desc *rd;
! 419: struct usb_ctl_report *re;
! 420: u_char buffer[UHID_CHUNK];
! 421: int size, extra;
! 422: usbd_status err;
! 423: void *desc;
! 424:
! 425: DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
! 426:
! 427: if (sc->sc_dying)
! 428: return (EIO);
! 429:
! 430: switch (cmd) {
! 431: case FIONBIO:
! 432: /* All handled in the upper FS layer. */
! 433: break;
! 434:
! 435: case FIOASYNC:
! 436: if (*(int *)addr) {
! 437: if (sc->sc_async != NULL)
! 438: return (EBUSY);
! 439: sc->sc_async = p;
! 440: DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p));
! 441: } else
! 442: sc->sc_async = NULL;
! 443: break;
! 444:
! 445: /* XXX this is not the most general solution. */
! 446: case TIOCSPGRP:
! 447: if (sc->sc_async == NULL)
! 448: return (EINVAL);
! 449: if (*(int *)addr != sc->sc_async->p_pgid)
! 450: return (EPERM);
! 451: break;
! 452:
! 453: case USB_GET_REPORT_DESC:
! 454: uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size);
! 455: rd = (struct usb_ctl_report_desc *)addr;
! 456: size = min(size, sizeof rd->ucrd_data);
! 457: rd->ucrd_size = size;
! 458: memcpy(rd->ucrd_data, desc, size);
! 459: break;
! 460:
! 461: case USB_SET_IMMED:
! 462: if (*(int *)addr) {
! 463: extra = sc->sc_hdev.sc_report_id != 0;
! 464: err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT,
! 465: buffer, sc->sc_isize + extra);
! 466: if (err)
! 467: return (EOPNOTSUPP);
! 468:
! 469: sc->sc_state |= UHID_IMMED;
! 470: } else
! 471: sc->sc_state &= ~UHID_IMMED;
! 472: break;
! 473:
! 474: case USB_GET_REPORT:
! 475: re = (struct usb_ctl_report *)addr;
! 476: switch (re->ucr_report) {
! 477: case UHID_INPUT_REPORT:
! 478: size = sc->sc_isize;
! 479: break;
! 480: case UHID_OUTPUT_REPORT:
! 481: size = sc->sc_osize;
! 482: break;
! 483: case UHID_FEATURE_REPORT:
! 484: size = sc->sc_fsize;
! 485: break;
! 486: default:
! 487: return (EINVAL);
! 488: }
! 489: extra = sc->sc_hdev.sc_report_id != 0;
! 490: err = uhidev_get_report(&sc->sc_hdev, re->ucr_report,
! 491: re->ucr_data, size + extra);
! 492: if (extra)
! 493: memcpy(re->ucr_data, re->ucr_data+1, size);
! 494: if (err)
! 495: return (EIO);
! 496: break;
! 497:
! 498: case USB_SET_REPORT:
! 499: re = (struct usb_ctl_report *)addr;
! 500: switch (re->ucr_report) {
! 501: case UHID_INPUT_REPORT:
! 502: size = sc->sc_isize;
! 503: break;
! 504: case UHID_OUTPUT_REPORT:
! 505: size = sc->sc_osize;
! 506: break;
! 507: case UHID_FEATURE_REPORT:
! 508: size = sc->sc_fsize;
! 509: break;
! 510: default:
! 511: return (EINVAL);
! 512: }
! 513: err = uhidev_set_report(&sc->sc_hdev, re->ucr_report,
! 514: re->ucr_data, size);
! 515: if (err)
! 516: return (EIO);
! 517: break;
! 518:
! 519: case USB_GET_REPORT_ID:
! 520: *(int *)addr = sc->sc_hdev.sc_report_id;
! 521: break;
! 522:
! 523: default:
! 524: return (EINVAL);
! 525: }
! 526: return (0);
! 527: }
! 528:
! 529: int
! 530: uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
! 531: {
! 532: struct uhid_softc *sc;
! 533: int error;
! 534:
! 535: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 536:
! 537: sc->sc_refcnt++;
! 538: error = uhid_do_ioctl(sc, cmd, addr, flag, p);
! 539: if (--sc->sc_refcnt < 0)
! 540: usb_detach_wakeup(&sc->sc_hdev.sc_dev);
! 541: return (error);
! 542: }
! 543:
! 544: int
! 545: uhidpoll(dev_t dev, int events, struct proc *p)
! 546: {
! 547: struct uhid_softc *sc;
! 548: int revents = 0;
! 549: int s;
! 550:
! 551: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 552:
! 553: if (sc->sc_dying)
! 554: return (POLLERR);
! 555:
! 556: s = splusb();
! 557: if (events & (POLLOUT | POLLWRNORM))
! 558: revents |= events & (POLLOUT | POLLWRNORM);
! 559: if (events & (POLLIN | POLLRDNORM)) {
! 560: if (sc->sc_q.c_cc > 0)
! 561: revents |= events & (POLLIN | POLLRDNORM);
! 562: else
! 563: selrecord(p, &sc->sc_rsel);
! 564: }
! 565:
! 566: splx(s);
! 567: return (revents);
! 568: }
! 569:
! 570: void filt_uhidrdetach(struct knote *);
! 571: int filt_uhidread(struct knote *, long);
! 572: int uhidkqfilter(dev_t, struct knote *);
! 573:
! 574: void
! 575: filt_uhidrdetach(struct knote *kn)
! 576: {
! 577: struct uhid_softc *sc = (void *)kn->kn_hook;
! 578: int s;
! 579:
! 580: s = splusb();
! 581: SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext);
! 582: splx(s);
! 583: }
! 584:
! 585: int
! 586: filt_uhidread(struct knote *kn, long hint)
! 587: {
! 588: struct uhid_softc *sc = (void *)kn->kn_hook;
! 589:
! 590: kn->kn_data = sc->sc_q.c_cc;
! 591: return (kn->kn_data > 0);
! 592: }
! 593:
! 594: struct filterops uhidread_filtops =
! 595: { 1, NULL, filt_uhidrdetach, filt_uhidread };
! 596:
! 597: struct filterops uhid_seltrue_filtops =
! 598: { 1, NULL, filt_uhidrdetach, filt_seltrue };
! 599:
! 600: int
! 601: uhidkqfilter(dev_t dev, struct knote *kn)
! 602: {
! 603: struct uhid_softc *sc;
! 604: struct klist *klist;
! 605: int s;
! 606:
! 607: sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
! 608:
! 609: if (sc->sc_dying)
! 610: return (EIO);
! 611:
! 612: switch (kn->kn_filter) {
! 613: case EVFILT_READ:
! 614: klist = &sc->sc_rsel.si_note;
! 615: kn->kn_fop = &uhidread_filtops;
! 616: break;
! 617:
! 618: case EVFILT_WRITE:
! 619: klist = &sc->sc_rsel.si_note;
! 620: kn->kn_fop = &uhid_seltrue_filtops;
! 621: break;
! 622:
! 623: default:
! 624: return (1);
! 625: }
! 626:
! 627: kn->kn_hook = (void *)sc;
! 628:
! 629: s = splusb();
! 630: SLIST_INSERT_HEAD(klist, kn, kn_selnext);
! 631: splx(s);
! 632:
! 633: return (0);
! 634: }
CVSweb