xref: /netbsd-src/sys/dev/wscons/wsevent.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /* $NetBSD: wsevent.c,v 1.24 2007/12/05 17:19:57 pooka Exp $ */
2 
3 /*-
4  * Copyright (c) 2006 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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  * 3. All advertising materials mentioning features or use of this software
51  *    must display the following acknowledgement:
52  *      This product includes software developed by Christopher G. Demetriou
53  *	for the NetBSD Project.
54  * 4. The name of the author may not be used to endorse or promote products
55  *    derived from this software without specific prior written permission
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
58  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
59  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
60  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
61  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
62  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
63  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
64  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
65  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
66  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67  */
68 
69 /*
70  * Copyright (c) 1992, 1993
71  *	The Regents of the University of California.  All rights reserved.
72  *
73  * This software was developed by the Computer Systems Engineering group
74  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
75  * contributed to Berkeley.
76  *
77  * All advertising materials mentioning features or use of this software
78  * must display the following acknowledgement:
79  *	This product includes software developed by the University of
80  *	California, Lawrence Berkeley Laboratory.
81  *
82  * Redistribution and use in source and binary forms, with or without
83  * modification, are permitted provided that the following conditions
84  * are met:
85  * 1. Redistributions of source code must retain the above copyright
86  *    notice, this list of conditions and the following disclaimer.
87  * 2. Redistributions in binary form must reproduce the above copyright
88  *    notice, this list of conditions and the following disclaimer in the
89  *    documentation and/or other materials provided with the distribution.
90  * 3. Neither the name of the University nor the names of its contributors
91  *    may be used to endorse or promote products derived from this software
92  *    without specific prior written permission.
93  *
94  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
95  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
96  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
97  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
98  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
99  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
100  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
101  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
102  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
103  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
104  * SUCH DAMAGE.
105  *
106  *	@(#)event.c	8.1 (Berkeley) 6/11/93
107  */
108 
109 /*
110  * Internal "wscons_event" queue interface for the keyboard and mouse drivers.
111  */
112 
113 #include <sys/cdefs.h>
114 __KERNEL_RCSID(0, "$NetBSD: wsevent.c,v 1.24 2007/12/05 17:19:57 pooka Exp $");
115 
116 #include <sys/param.h>
117 #include <sys/kernel.h>
118 #include <sys/fcntl.h>
119 #include <sys/malloc.h>
120 #include <sys/proc.h>
121 #include <sys/systm.h>
122 #include <sys/vnode.h>
123 #include <sys/select.h>
124 #include <sys/poll.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 /*
138  * Priority of code managing wsevent queues.  PWSEVENT is set just above
139  * PSOCK, which is just above TTIPRI, on the theory that mouse and keyboard
140  * `user' input should be quick.
141  */
142 #define	PWSEVENT	23
143 #define	splwsevent()	spltty()
144 
145 /*
146  * Initialize a wscons_event queue.
147  */
148 void
149 wsevent_init(struct wseventvar *ev, struct proc *p)
150 {
151 
152 	if (ev->q != NULL) {
153 #ifdef DIAGNOSTIC
154 		printf("wsevent_init: already init\n");
155 #endif
156 		return;
157 	}
158 	ev->get = ev->put = 0;
159 	ev->q = malloc((u_long)WSEVENT_QSIZE * sizeof(struct wscons_event),
160 		       M_DEVBUF, M_WAITOK|M_ZERO);
161 	ev->io = p;
162 }
163 
164 /*
165  * Tear down a wscons_event queue.
166  */
167 void
168 wsevent_fini(struct wseventvar *ev)
169 {
170 	if (ev->q == NULL) {
171 #ifdef DIAGNOSTIC
172 		printf("wsevent_fini: already fini\n");
173 #endif
174 		return;
175 	}
176 	free(ev->q, M_DEVBUF);
177 	ev->q = NULL;
178 }
179 
180 /*
181  * User-level interface: read, poll.
182  * (User cannot write an event queue.)
183  */
184 int
185 wsevent_read(struct wseventvar *ev, struct uio *uio, int flags)
186 {
187 	int s, n, cnt, error;
188 
189 	/*
190 	 * Make sure we can return at least 1.
191 	 */
192 	if (uio->uio_resid < sizeof(struct wscons_event))
193 		return (EMSGSIZE);	/* ??? */
194 	s = splwsevent();
195 	while (ev->get == ev->put) {
196 		if (flags & IO_NDELAY) {
197 			splx(s);
198 			return (EWOULDBLOCK);
199 		}
200 		ev->wanted = 1;
201 		error = tsleep(ev, PWSEVENT | PCATCH,
202 		    "wsevent_read", 0);
203 		if (error) {
204 			splx(s);
205 			return (error);
206 		}
207 	}
208 	/*
209 	 * Move wscons_event from tail end of queue (there is at least one
210 	 * there).
211 	 */
212 	if (ev->put < ev->get)
213 		cnt = WSEVENT_QSIZE - ev->get;	/* events in [get..QSIZE) */
214 	else
215 		cnt = ev->put - ev->get;	/* events in [get..put) */
216 	splx(s);
217 	n = howmany(uio->uio_resid, sizeof(struct wscons_event));
218 	if (cnt > n)
219 		cnt = n;
220 	error = uiomove(&ev->q[ev->get],
221 	    cnt * sizeof(struct wscons_event), uio);
222 	n -= cnt;
223 	/*
224 	 * If we do not wrap to 0, used up all our space, or had an error,
225 	 * stop.  Otherwise move from front of queue to put index, if there
226 	 * is anything there to move.
227 	 */
228 	if ((ev->get = (ev->get + cnt) % WSEVENT_QSIZE) != 0 ||
229 	    n == 0 || error || (cnt = ev->put) == 0)
230 		return (error);
231 	if (cnt > n)
232 		cnt = n;
233 	error = uiomove(&ev->q[0],
234 	    cnt * sizeof(struct wscons_event), uio);
235 	ev->get = cnt;
236 	return (error);
237 }
238 
239 int
240 wsevent_poll(struct wseventvar *ev, int events, struct lwp *l)
241 {
242 	int revents = 0;
243 	int s = splwsevent();
244 
245         if (events & (POLLIN | POLLRDNORM)) {
246 		if (ev->get != ev->put)
247 			revents |= events & (POLLIN | POLLRDNORM);
248 		else
249 			selrecord(l, &ev->sel);
250 	}
251 
252 	splx(s);
253 	return (revents);
254 }
255 
256 static void
257 filt_wseventrdetach(struct knote *kn)
258 {
259 	struct wseventvar *ev = kn->kn_hook;
260 	int s;
261 
262 	s = splwsevent();
263 	SLIST_REMOVE(&ev->sel.sel_klist, kn, knote, kn_selnext);
264 	splx(s);
265 }
266 
267 static int
268 filt_wseventread(struct knote *kn, long hint)
269 {
270 	struct wseventvar *ev = kn->kn_hook;
271 
272 	if (ev->get == ev->put)
273 		return (0);
274 
275 	if (ev->get < ev->put)
276 		kn->kn_data = ev->put - ev->get;
277 	else
278 		kn->kn_data = (WSEVENT_QSIZE - ev->get) +
279 		    ev->put;
280 
281 	kn->kn_data *= sizeof(struct wscons_event);
282 
283 	return (1);
284 }
285 
286 static const struct filterops wsevent_filtops =
287 	{ 1, NULL, filt_wseventrdetach, filt_wseventread };
288 
289 int
290 wsevent_kqfilter(struct wseventvar *ev, struct knote *kn)
291 {
292 	struct klist *klist;
293 	int s;
294 
295 	switch (kn->kn_filter) {
296 	case EVFILT_READ:
297 		klist = &ev->sel.sel_klist;
298 		kn->kn_fop = &wsevent_filtops;
299 		break;
300 
301 	default:
302 		return (EINVAL);
303 	}
304 
305 	kn->kn_hook = ev;
306 
307 	s = splwsevent();
308 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
309 	splx(s);
310 
311 	return (0);
312 }
313 
314 /*
315  * Wakes up all listener of the 'ev' queue.
316  */
317 void
318 wsevent_wakeup(struct wseventvar *ev)
319 {
320 
321 	selnotify(&ev->sel, 0);
322 
323 	if (ev->wanted) {
324 		ev->wanted = 0;
325 		wakeup(ev);
326 	}
327 
328 	if (ev->async) {
329 		mutex_enter(&proclist_mutex);
330 		psignal(ev->io, SIGIO);
331 		mutex_exit(&proclist_mutex);
332 	}
333 }
334 
335 /*
336  * Injects the set of events given in 'events', whose size is 'nevents',
337  * into the 'ev' queue.  If there is not enough free space to inject them
338  * all, returns ENOSPC and the queue is left intact; otherwise returns 0
339  * and wakes up all listeners.
340  */
341 int
342 wsevent_inject(struct wseventvar *ev, struct wscons_event *events,
343     size_t nevents)
344 {
345 	size_t avail, i;
346 	struct timespec t;
347 
348 	/* Calculate number of free slots in the queue. */
349 	if (ev->put < ev->get)
350 		avail = ev->get - ev->put;
351 	else
352 		avail = WSEVENT_QSIZE - (ev->put - ev->get);
353 	KASSERT(avail <= WSEVENT_QSIZE);
354 
355 	/* Fail if there is all events will not fit in the queue. */
356 	if (avail < nevents)
357 		return ENOSPC;
358 
359 	/* Use the current time for all events. */
360 	getnanotime(&t);
361 
362 	/* Inject the events. */
363 	for (i = 0; i < nevents; i++) {
364 		struct wscons_event *we;
365 
366 		we = &ev->q[ev->put];
367 		we->type = events[i].type;
368 		we->value = events[i].value;
369 		we->time = t;
370 
371 		ev->put = (ev->put + 1) % WSEVENT_QSIZE;
372 	}
373 
374 	wsevent_wakeup(ev);
375 
376 	return 0;
377 }
378