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