1 /* $NetBSD: wsevent.c,v 1.35 2012/05/24 18:16:31 abs 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.35 2012/05/24 18:16:31 abs Exp $"); 108 109 #include "opt_compat_netbsd.h" 110 #include "opt_modular.h" 111 112 #include <sys/param.h> 113 #include <sys/kernel.h> 114 #include <sys/fcntl.h> 115 #include <sys/kmem.h> 116 #include <sys/proc.h> 117 #include <sys/systm.h> 118 #include <sys/vnode.h> 119 #include <sys/select.h> 120 #include <sys/poll.h> 121 122 #include <dev/wscons/wsconsio.h> 123 #include <dev/wscons/wseventvar.h> 124 125 /* 126 * Size of a wsevent queue (measured in number of events). 127 * Should be a power of two so that `%' is fast. 128 * At the moment, the value below makes the queues use 2 Kbytes each; this 129 * value may need tuning. 130 */ 131 #define WSEVENT_QSIZE 256 132 133 #define EVSIZE(ver) ((ver) == WSEVENT_VERSION ? \ 134 sizeof(struct wscons_event) : \ 135 sizeof(struct owscons_event)) 136 #define EVARRAY(ev, idx) (&(ev)->q[(idx)]) 137 138 /* 139 * Priority of code managing wsevent queues. PWSEVENT is set just above 140 * PSOCK, which is just above TTIPRI, on the theory that mouse and keyboard 141 * `user' input should be quick. 142 */ 143 #define PWSEVENT 23 144 #define splwsevent() spltty() 145 146 static void wsevent_intr(void *); 147 148 /* 149 * Initialize a wscons_event queue. 150 */ 151 void 152 wsevent_init(struct wseventvar *ev, struct proc *p) 153 { 154 155 if (ev->q != NULL) { 156 #ifdef DIAGNOSTIC 157 printf("wsevent_init: already init\n"); 158 #endif 159 return; 160 } 161 /* For binary compat. New code must call WSxxxIO_SETVERSION */ 162 ev->version = 0; 163 ev->get = ev->put = 0; 164 ev->q = kmem_alloc(WSEVENT_QSIZE * sizeof(*ev->q), KM_SLEEP); 165 selinit(&ev->sel); 166 ev->io = p; 167 ev->sih = softint_establish(SOFTINT_MPSAFE | SOFTINT_CLOCK, 168 wsevent_intr, ev); 169 } 170 171 /* 172 * Tear down a wscons_event queue. 173 */ 174 void 175 wsevent_fini(struct wseventvar *ev) 176 { 177 if (ev->q == NULL) { 178 #ifdef DIAGNOSTIC 179 printf("wsevent_fini: already fini\n"); 180 #endif 181 return; 182 } 183 seldestroy(&ev->sel); 184 kmem_free(ev->q, WSEVENT_QSIZE * sizeof(*ev->q)); 185 ev->q = NULL; 186 softint_disestablish(ev->sih); 187 } 188 189 #if defined(COMPAT_50) || defined(MODULAR) 190 static int 191 wsevent_copyout_events50(const struct wscons_event *events, int cnt, 192 struct uio *uio) 193 { 194 int i; 195 196 for (i = 0; i < cnt; i++) { 197 const struct wscons_event *ev = &events[i]; 198 struct owscons_event ev50; 199 int error; 200 201 ev50.type = ev->type; 202 ev50.value = ev->value; 203 timespec_to_timespec50(&ev->time, &ev50.time); 204 205 error = uiomove(&ev50, sizeof(ev50), uio); 206 if (error) { 207 return error; 208 } 209 } 210 return 0; 211 } 212 #else /* defined(COMPAT_50) || defined(MODULAR) */ 213 static int 214 wsevent_copyout_events50(const struct wscons_event *events, int cnt, 215 struct uio *uio) 216 { 217 218 return EINVAL; 219 } 220 #endif /* defined(COMPAT_50) || defined(MODULAR) */ 221 222 static int 223 wsevent_copyout_events(const struct wscons_event *events, int cnt, 224 struct uio *uio, int ver) 225 { 226 227 switch (ver) { 228 case 0: 229 return wsevent_copyout_events50(events, cnt, uio); 230 case WSEVENT_VERSION: 231 return uiomove(__UNCONST(events), cnt * sizeof(*events), uio); 232 default: 233 panic("%s: unknown version %d", __func__, ver); 234 } 235 } 236 237 /* 238 * User-level interface: read, poll. 239 * (User cannot write an event queue.) 240 */ 241 int 242 wsevent_read(struct wseventvar *ev, struct uio *uio, int flags) 243 { 244 int s, n, cnt, error; 245 const int ver = ev->version; 246 const size_t evsize = EVSIZE(ver); 247 248 /* 249 * Make sure we can return at least 1. 250 */ 251 if (uio->uio_resid < evsize) 252 return (EMSGSIZE); /* ??? */ 253 s = splwsevent(); 254 while (ev->get == ev->put) { 255 if (flags & IO_NDELAY) { 256 splx(s); 257 return (EWOULDBLOCK); 258 } 259 ev->wanted = 1; 260 error = tsleep(ev, PWSEVENT | PCATCH, "wsevent_read", 0); 261 if (error) { 262 splx(s); 263 return (error); 264 } 265 } 266 /* 267 * Move wscons_event from tail end of queue (there is at least one 268 * there). 269 */ 270 if (ev->put < ev->get) 271 cnt = WSEVENT_QSIZE - ev->get; /* events in [get..QSIZE) */ 272 else 273 cnt = ev->put - ev->get; /* events in [get..put) */ 274 splx(s); 275 n = howmany(uio->uio_resid, evsize); 276 if (cnt > n) 277 cnt = n; 278 error = wsevent_copyout_events(EVARRAY(ev, ev->get), cnt, uio, ver); 279 n -= cnt; 280 /* 281 * If we do not wrap to 0, used up all our space, or had an error, 282 * stop. Otherwise move from front of queue to put index, if there 283 * is anything there to move. 284 */ 285 if ((ev->get = (ev->get + cnt) % WSEVENT_QSIZE) != 0 || 286 n == 0 || error || (cnt = ev->put) == 0) 287 return (error); 288 if (cnt > n) 289 cnt = n; 290 error = wsevent_copyout_events(EVARRAY(ev, 0), cnt, uio, ver); 291 ev->get = cnt; 292 return (error); 293 } 294 295 int 296 wsevent_poll(struct wseventvar *ev, int events, struct lwp *l) 297 { 298 int revents = 0; 299 int s = splwsevent(); 300 301 if (events & (POLLIN | POLLRDNORM)) { 302 if (ev->get != ev->put) 303 revents |= events & (POLLIN | POLLRDNORM); 304 else 305 selrecord(l, &ev->sel); 306 } 307 308 splx(s); 309 return (revents); 310 } 311 312 static void 313 filt_wseventrdetach(struct knote *kn) 314 { 315 struct wseventvar *ev = kn->kn_hook; 316 int s; 317 318 s = splwsevent(); 319 SLIST_REMOVE(&ev->sel.sel_klist, kn, knote, kn_selnext); 320 splx(s); 321 } 322 323 static int 324 filt_wseventread(struct knote *kn, long hint) 325 { 326 struct wseventvar *ev = kn->kn_hook; 327 328 if (ev->get == ev->put) 329 return (0); 330 331 if (ev->get < ev->put) 332 kn->kn_data = ev->put - ev->get; 333 else 334 kn->kn_data = (WSEVENT_QSIZE - ev->get) + ev->put; 335 336 kn->kn_data *= EVSIZE(ev->version); 337 338 return (1); 339 } 340 341 static const struct filterops wsevent_filtops = 342 { 1, NULL, filt_wseventrdetach, filt_wseventread }; 343 344 int 345 wsevent_kqfilter(struct wseventvar *ev, struct knote *kn) 346 { 347 struct klist *klist; 348 int s; 349 350 switch (kn->kn_filter) { 351 case EVFILT_READ: 352 klist = &ev->sel.sel_klist; 353 kn->kn_fop = &wsevent_filtops; 354 break; 355 356 default: 357 return (EINVAL); 358 } 359 360 kn->kn_hook = ev; 361 362 s = splwsevent(); 363 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 364 splx(s); 365 366 return (0); 367 } 368 369 /* 370 * Wakes up all listener of the 'ev' queue. 371 */ 372 void 373 wsevent_wakeup(struct wseventvar *ev) 374 { 375 376 selnotify(&ev->sel, 0, 0); 377 378 if (ev->wanted) { 379 ev->wanted = 0; 380 wakeup(ev); 381 } 382 383 if (ev->async) { 384 softint_schedule(ev->sih); 385 } 386 } 387 388 /* 389 * Soft interrupt handler: sends signal to async proc. 390 */ 391 static void 392 wsevent_intr(void *cookie) 393 { 394 struct wseventvar *ev; 395 396 ev = cookie; 397 398 if (ev->async) { 399 mutex_enter(proc_lock); 400 psignal(ev->io, SIGIO); 401 mutex_exit(proc_lock); 402 } 403 } 404 405 /* 406 * Injects the set of events given in 'events', whose size is 'nevents', 407 * into the 'ev' queue. If there is not enough free space to inject them 408 * all, returns ENOSPC and the queue is left intact; otherwise returns 0 409 * and wakes up all listeners. 410 */ 411 int 412 wsevent_inject(struct wseventvar *ev, struct wscons_event *events, 413 size_t nevents) 414 { 415 size_t avail, i; 416 struct timespec t; 417 418 /* Calculate number of free slots in the queue. */ 419 if (ev->put < ev->get) 420 avail = ev->get - ev->put; 421 else 422 avail = WSEVENT_QSIZE - (ev->put - ev->get); 423 KASSERT(avail <= WSEVENT_QSIZE); 424 425 /* Fail if there is all events will not fit in the queue. */ 426 if (avail < nevents) 427 return ENOSPC; 428 429 /* Use the current time for all events. */ 430 getnanotime(&t); 431 432 /* Inject the events. */ 433 for (i = 0; i < nevents; i++) { 434 struct wscons_event *we; 435 436 we = EVARRAY(ev, ev->put); 437 we->type = events[i].type; 438 we->value = events[i].value; 439 we->time = t; 440 441 ev->put = (ev->put + 1) % WSEVENT_QSIZE; 442 } 443 wsevent_wakeup(ev); 444 445 return 0; 446 } 447 448 int 449 wsevent_setversion(struct wseventvar *ev, int vers) 450 { 451 if (ev == NULL) 452 return EINVAL; 453 454 switch (vers) { 455 case 0: 456 case WSEVENT_VERSION: 457 break; 458 default: 459 return EINVAL; 460 } 461 462 if (vers == ev->version) 463 return 0; 464 465 ev->get = ev->put = 0; 466 ev->version = vers; 467 return 0; 468 } 469