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