1 /* $NetBSD: wsevent.c,v 1.46 2020/12/18 01:41:23 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed by Christopher G. Demetriou 46 * for the NetBSD Project. 47 * 4. The name of the author may not be used to endorse or promote products 48 * derived from this software without specific prior written permission 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 51 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 52 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 53 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 54 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 55 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 59 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 */ 61 62 /* 63 * Copyright (c) 1992, 1993 64 * The Regents of the University of California. All rights reserved. 65 * 66 * This software was developed by the Computer Systems Engineering group 67 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 68 * contributed to Berkeley. 69 * 70 * All advertising materials mentioning features or use of this software 71 * must display the following acknowledgement: 72 * This product includes software developed by the University of 73 * California, Lawrence Berkeley Laboratory. 74 * 75 * Redistribution and use in source and binary forms, with or without 76 * modification, are permitted provided that the following conditions 77 * are met: 78 * 1. Redistributions of source code must retain the above copyright 79 * notice, this list of conditions and the following disclaimer. 80 * 2. Redistributions in binary form must reproduce the above copyright 81 * notice, this list of conditions and the following disclaimer in the 82 * documentation and/or other materials provided with the distribution. 83 * 3. Neither the name of the University nor the names of its contributors 84 * may be used to endorse or promote products derived from this software 85 * without specific prior written permission. 86 * 87 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 88 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 89 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 90 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 91 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 92 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 93 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 94 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 95 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 96 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 97 * SUCH DAMAGE. 98 * 99 * @(#)event.c 8.1 (Berkeley) 6/11/93 100 */ 101 102 /* 103 * Internal "wscons_event" queue interface for the keyboard and mouse drivers. 104 */ 105 106 #include <sys/cdefs.h> 107 __KERNEL_RCSID(0, "$NetBSD: wsevent.c,v 1.46 2020/12/18 01:41:23 thorpej Exp $"); 108 109 #ifdef _KERNEL_OPT 110 #include "opt_compat_netbsd.h" 111 #include "opt_modular.h" 112 #endif 113 114 #include <sys/param.h> 115 #include <sys/kernel.h> 116 #include <sys/fcntl.h> 117 #include <sys/kmem.h> 118 #include <sys/proc.h> 119 #include <sys/systm.h> 120 #include <sys/vnode.h> 121 #include <sys/select.h> 122 #include <sys/poll.h> 123 #include <sys/compat_stub.h> 124 #include <sys/sysctl.h> 125 126 #include <dev/wscons/wsconsio.h> 127 #include <dev/wscons/wseventvar.h> 128 129 /* 130 * Size of a wsevent queue (measured in number of events). 131 * Should be a power of two so that `%' is fast. 132 * At the moment, the value below makes the queues use 2 Kbytes each; this 133 * value may need tuning. 134 */ 135 #define WSEVENT_QSIZE 256 136 137 #define EVSIZE(ver) ((ver) == WSEVENT_VERSION ? \ 138 sizeof(struct wscons_event) : \ 139 sizeof(struct owscons_event)) 140 #define EVARRAY(ev, idx) (&(ev)->q[(idx)]) 141 142 static int wsevent_default_version = WSEVENT_VERSION; 143 144 /* 145 * Priority of code managing wsevent queues. PWSEVENT is set just above 146 * PSOCK, which is just above TTIPRI, on the theory that mouse and keyboard 147 * `user' input should be quick. 148 */ 149 #define PWSEVENT 23 150 #define splwsevent() spltty() 151 152 static void wsevent_intr(void *); 153 154 /* 155 * Initialize a wscons_event queue. 156 */ 157 void 158 wsevent_init(struct wseventvar *ev, struct proc *p) 159 { 160 161 if (ev->q != NULL) { 162 #ifdef DIAGNOSTIC 163 printf("wsevent_init: already init\n"); 164 #endif 165 return; 166 } 167 /* 168 * For binary compat set default version and either build with 169 * COMPAT_50 or load COMPAT_50 module to include the compatibility 170 * code. 171 */ 172 if (wsevent_default_version >= 0 && 173 wsevent_default_version < WSEVENT_VERSION) 174 ev->version = wsevent_default_version; 175 else 176 ev->version = WSEVENT_VERSION; 177 178 ev->get = ev->put = 0; 179 ev->q = kmem_alloc(WSEVENT_QSIZE * sizeof(*ev->q), KM_SLEEP); 180 selinit(&ev->sel); 181 ev->io = p; 182 ev->sih = softint_establish(SOFTINT_MPSAFE | SOFTINT_CLOCK, 183 wsevent_intr, ev); 184 } 185 186 /* 187 * Tear down a wscons_event queue. 188 */ 189 void 190 wsevent_fini(struct wseventvar *ev) 191 { 192 if (ev->q == NULL) { 193 #ifdef DIAGNOSTIC 194 printf("wsevent_fini: already fini\n"); 195 #endif 196 return; 197 } 198 seldestroy(&ev->sel); 199 kmem_free(ev->q, WSEVENT_QSIZE * sizeof(*ev->q)); 200 ev->q = NULL; 201 softint_disestablish(ev->sih); 202 } 203 204 static int 205 wsevent_copyout_events(const struct wscons_event *events, int cnt, 206 struct uio *uio, int ver) 207 { 208 int error; 209 210 switch (ver) { 211 case 0: 212 MODULE_HOOK_CALL(wscons_copyout_events_50_hook, 213 (events, cnt, uio), enosys(), error); 214 if (error == ENOSYS) 215 error = EINVAL; 216 return error; 217 case WSEVENT_VERSION: 218 return uiomove(__UNCONST(events), cnt * sizeof(*events), uio); 219 default: 220 panic("%s: unknown version %d", __func__, ver); 221 } 222 } 223 224 /* 225 * User-level interface: read, poll. 226 * (User cannot write an event queue.) 227 */ 228 int 229 wsevent_read(struct wseventvar *ev, struct uio *uio, int flags) 230 { 231 int s, n, cnt, error; 232 const int ver = ev->version; 233 const size_t evsize = EVSIZE(ver); 234 235 /* 236 * Make sure we can return at least 1. 237 */ 238 if (uio->uio_resid < evsize) 239 return (EMSGSIZE); /* ??? */ 240 s = splwsevent(); 241 while (ev->get == ev->put) { 242 if (flags & IO_NDELAY) { 243 splx(s); 244 return (EWOULDBLOCK); 245 } 246 ev->wanted = 1; 247 error = tsleep(ev, PWSEVENT | PCATCH, "wsevent_read", 0); 248 if (error) { 249 splx(s); 250 return (error); 251 } 252 } 253 /* 254 * Move wscons_event from tail end of queue (there is at least one 255 * there). 256 */ 257 if (ev->put < ev->get) 258 cnt = WSEVENT_QSIZE - ev->get; /* events in [get..QSIZE) */ 259 else 260 cnt = ev->put - ev->get; /* events in [get..put) */ 261 splx(s); 262 n = howmany(uio->uio_resid, evsize); 263 if (cnt > n) 264 cnt = n; 265 error = wsevent_copyout_events(EVARRAY(ev, ev->get), cnt, uio, ver); 266 n -= cnt; 267 /* 268 * If we do not wrap to 0, used up all our space, or had an error, 269 * stop. Otherwise move from front of queue to put index, if there 270 * is anything there to move. 271 */ 272 if ((ev->get = (ev->get + cnt) % WSEVENT_QSIZE) != 0 || 273 n == 0 || error || (cnt = ev->put) == 0) 274 return (error); 275 if (cnt > n) 276 cnt = n; 277 error = wsevent_copyout_events(EVARRAY(ev, 0), cnt, uio, ver); 278 ev->get = cnt; 279 return (error); 280 } 281 282 int 283 wsevent_poll(struct wseventvar *ev, int events, struct lwp *l) 284 { 285 int revents = 0; 286 int s = splwsevent(); 287 288 if (events & (POLLIN | POLLRDNORM)) { 289 if (ev->get != ev->put) 290 revents |= events & (POLLIN | POLLRDNORM); 291 else 292 selrecord(l, &ev->sel); 293 } 294 295 splx(s); 296 return (revents); 297 } 298 299 static void 300 filt_wseventrdetach(struct knote *kn) 301 { 302 struct wseventvar *ev = kn->kn_hook; 303 int s; 304 305 s = splwsevent(); 306 selremove_knote(&ev->sel, kn); 307 splx(s); 308 } 309 310 static int 311 filt_wseventread(struct knote *kn, long hint) 312 { 313 struct wseventvar *ev = kn->kn_hook; 314 315 if (ev->get == ev->put) 316 return (0); 317 318 if (ev->get < ev->put) 319 kn->kn_data = ev->put - ev->get; 320 else 321 kn->kn_data = (WSEVENT_QSIZE - ev->get) + ev->put; 322 323 kn->kn_data *= EVSIZE(ev->version); 324 325 return (1); 326 } 327 328 static const struct filterops wsevent_filtops = { 329 .f_isfd = 1, 330 .f_attach = NULL, 331 .f_detach = filt_wseventrdetach, 332 .f_event = filt_wseventread, 333 }; 334 335 int 336 wsevent_kqfilter(struct wseventvar *ev, struct knote *kn) 337 { 338 int s; 339 340 switch (kn->kn_filter) { 341 case EVFILT_READ: 342 kn->kn_fop = &wsevent_filtops; 343 break; 344 345 default: 346 return (EINVAL); 347 } 348 349 kn->kn_hook = ev; 350 351 s = splwsevent(); 352 selrecord_knote(&ev->sel, kn); 353 splx(s); 354 355 return (0); 356 } 357 358 /* 359 * Wakes up all listener of the 'ev' queue. 360 */ 361 void 362 wsevent_wakeup(struct wseventvar *ev) 363 { 364 365 selnotify(&ev->sel, 0, 0); 366 367 if (ev->wanted) { 368 ev->wanted = 0; 369 wakeup(ev); 370 } 371 372 if (ev->async) { 373 softint_schedule(ev->sih); 374 } 375 } 376 377 /* 378 * Soft interrupt handler: sends signal to async proc. 379 */ 380 static void 381 wsevent_intr(void *cookie) 382 { 383 struct wseventvar *ev; 384 385 ev = cookie; 386 387 if (ev->async) { 388 mutex_enter(&proc_lock); 389 psignal(ev->io, SIGIO); 390 mutex_exit(&proc_lock); 391 } 392 } 393 394 /* 395 * Injects the set of events given in 'events', whose size is 'nevents', 396 * into the 'ev' queue. If there is not enough free space to inject them 397 * all, returns ENOSPC and the queue is left intact; otherwise returns 0 398 * and wakes up all listeners. 399 */ 400 int 401 wsevent_inject(struct wseventvar *ev, struct wscons_event *events, 402 size_t nevents) 403 { 404 size_t avail, i; 405 struct timespec t; 406 407 /* Calculate number of free slots in the queue. */ 408 if (ev->put < ev->get) 409 avail = ev->get - ev->put; 410 else 411 avail = WSEVENT_QSIZE - (ev->put - ev->get); 412 KASSERT(avail <= WSEVENT_QSIZE); 413 414 /* Fail if there is all events will not fit in the queue. */ 415 if (avail < nevents) 416 return ENOSPC; 417 418 /* Use the current time for all events. */ 419 getnanotime(&t); 420 421 /* Inject the events. */ 422 for (i = 0; i < nevents; i++) { 423 struct wscons_event *we; 424 425 we = EVARRAY(ev, ev->put); 426 we->type = events[i].type; 427 we->value = events[i].value; 428 we->time = t; 429 430 ev->put = (ev->put + 1) % WSEVENT_QSIZE; 431 } 432 wsevent_wakeup(ev); 433 434 return 0; 435 } 436 437 int 438 wsevent_setversion(struct wseventvar *ev, int vers) 439 { 440 if (ev == NULL) 441 return EINVAL; 442 443 switch (vers) { 444 case 0: 445 case WSEVENT_VERSION: 446 break; 447 default: 448 return EINVAL; 449 } 450 451 if (vers == ev->version) 452 return 0; 453 454 ev->get = ev->put = 0; 455 ev->version = vers; 456 return 0; 457 } 458 459 SYSCTL_SETUP(sysctl_wsevent_setup, "sysctl hw.wsevent subtree setup") 460 { 461 const struct sysctlnode *node = NULL; 462 463 if (sysctl_createv(clog, 0, NULL, &node, 464 CTLFLAG_PERMANENT, 465 CTLTYPE_NODE, "wsevent", NULL, 466 NULL, 0, NULL, 0, 467 CTL_HW, CTL_CREATE, CTL_EOL) != 0) 468 return; 469 470 sysctl_createv(clog, 0, &node, NULL, 471 CTLFLAG_READWRITE, 472 CTLTYPE_INT, "default_version", 473 SYSCTL_DESCR("Set default event version for compatibility"), 474 NULL, 0, &wsevent_default_version, 0, 475 CTL_CREATE, CTL_EOL); 476 } 477