xref: /netbsd-src/sys/dev/wscons/wsbell.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /* $NetBSD: wsbell.c,v 1.12 2019/06/22 08:03:01 isaki Exp $ */
2 
3 /*-
4  * Copyright (c) 2017 Nathanial Sloss <nathanialsloss@yahoo.com.au>
5  * All rights reserved.
6  *
7  * Copyright (c) 2006 The NetBSD Foundation, Inc.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by Julio M. Merino Vidal.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /*
36  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. All advertising materials mentioning features or use of this software
47  *    must display the following acknowledgement:
48  *      This product includes software developed by Christopher G. Demetriou
49  *	for the NetBSD Project.
50  * 4. The name of the author may not be used to endorse or promote products
51  *    derived from this software without specific prior written permission
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
54  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
55  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
56  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
57  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
58  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
59  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
60  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
62  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63  */
64 
65 /*
66  * Copyright (c) 1992, 1993
67  *	The Regents of the University of California.  All rights reserved.
68  *
69  * This software was developed by the Computer Systems Engineering group
70  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
71  * contributed to Berkeley.
72  *
73  * All advertising materials mentioning features or use of this software
74  * must display the following acknowledgement:
75  *	This product includes software developed by the University of
76  *	California, Lawrence Berkeley Laboratory.
77  *
78  * Redistribution and use in source and binary forms, with or without
79  * modification, are permitted provided that the following conditions
80  * are met:
81  * 1. Redistributions of source code must retain the above copyright
82  *    notice, this list of conditions and the following disclaimer.
83  * 2. Redistributions in binary form must reproduce the above copyright
84  *    notice, this list of conditions and the following disclaimer in the
85  *    documentation and/or other materials provided with the distribution.
86  * 3. Neither the name of the University nor the names of its contributors
87  *    may be used to endorse or promote products derived from this software
88  *    without specific prior written permission.
89  *
90  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
91  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
92  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
93  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
94  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
95  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
96  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
97  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
98  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
99  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
100  * SUCH DAMAGE.
101  *
102  *	@(#)ms.c	8.1 (Berkeley) 6/11/93
103  */
104 
105 /*
106  * Keyboard Bell driver.
107  */
108 
109 #include <sys/cdefs.h>
110 __KERNEL_RCSID(0, "$NetBSD: wsbell.c,v 1.12 2019/06/22 08:03:01 isaki Exp $");
111 
112 #if defined(_KERNEL_OPT)
113 #include "wsmux.h"
114 #endif
115 
116 #include <sys/param.h>
117 #include <sys/conf.h>
118 #include <sys/ioctl.h>
119 #include <sys/poll.h>
120 #include <sys/fcntl.h>
121 #include <sys/kernel.h>
122 #include <sys/condvar.h>
123 #include <sys/mutex.h>
124 #include <sys/kauth.h>
125 #include <sys/kthread.h>
126 #include <sys/proc.h>
127 #include <sys/syslog.h>
128 #include <sys/systm.h>
129 #include <sys/tty.h>
130 #include <sys/signalvar.h>
131 #include <sys/device.h>
132 #include <sys/vnode.h>
133 #include <sys/callout.h>
134 #include <sys/module.h>
135 
136 #include <dev/wscons/wsconsio.h>
137 #include <dev/wscons/wsbellvar.h>
138 #include <dev/wscons/wsbellmuxvar.h>
139 #include <dev/wscons/wsbelldata.h>
140 
141 #include <dev/spkrio.h>
142 
143 #include "ioconf.h"
144 
145 #if defined(WSMUX_DEBUG) && NWSMUX > 0
146 #define DPRINTF(x)	if (wsmuxdebug) printf x
147 #define DPRINTFN(n,x)	if (wsmuxdebug > (n)) printf x
148 extern int wsmuxdebug;
149 #else
150 #define DPRINTF(x)
151 #define DPRINTFN(n,x)
152 #endif
153 
154 static void bell_thread(void *);
155 static inline void spkr_audio_play(struct wsbell_softc *, u_int, u_int, u_int);
156 
157 static int  wsbell_match(device_t, cfdata_t, void *);
158 static void wsbell_attach(device_t, device_t, void *);
159 static int  wsbell_detach(device_t, int);
160 static int  wsbell_activate(device_t, enum devact);
161 
162 #if NWSMUX > 0
163 static int  wsbell_mux_open(struct wsevsrc *, struct wseventvar *);
164 static int  wsbell_mux_close(struct wsevsrc *);
165 
166 static int  wsbelldoopen(struct wsbell_softc *, struct wseventvar *);
167 static int  wsbelldoioctl(device_t, u_long, void *, int, struct lwp *);
168 
169 static int  wsbell_do_ioctl(struct wsbell_softc *, u_long, void *,
170 			     int, struct lwp *);
171 
172 #endif
173 
174 CFATTACH_DECL_NEW(wsbell, sizeof (struct wsbell_softc),
175     wsbell_match, wsbell_attach, wsbell_detach, wsbell_activate);
176 
177 extern struct cfdriver wsbell_cd;
178 
179 extern dev_type_open(spkropen);
180 extern dev_type_close(spkrclose);
181 extern dev_type_ioctl(spkrioctl);
182 
183 const struct cdevsw wsbell_cdevsw = {
184 	.d_open = noopen,
185 	.d_close = noclose,
186 	.d_read = noread,
187 	.d_write = nowrite,
188 	.d_ioctl = noioctl,
189 	.d_stop = nostop,
190 	.d_tty = notty,
191 	.d_poll = nopoll,
192 	.d_mmap = nommap,
193 	.d_kqfilter = nokqfilter,
194 	.d_discard = nodiscard,
195 	.d_flag = D_OTHER
196 };
197 
198 #if NWSMUX > 0
199 struct wssrcops wsbell_srcops = {
200 	WSMUX_BELL,
201 	wsbell_mux_open, wsbell_mux_close, wsbelldoioctl, wsbelldoioctl, NULL
202 };
203 #endif
204 
205 int
206 wsbell_match(device_t parent, cfdata_t match, void *aux)
207 {
208 	return (1);
209 }
210 
211 void
212 wsbell_attach(device_t parent, device_t self, void *aux)
213 {
214 	struct wsbell_softc *sc = device_private(self);
215 	struct wsbelldev_attach_args *ap = aux;
216 #if NWSMUX > 0
217 	int mux, error;
218 #endif
219 
220 	sc->sc_base.me_dv = self;
221 	sc->sc_accesscookie = ap->accesscookie;
222 
223 	sc->sc_dying = false;
224 	sc->sc_spkr = device_unit(parent);
225 	sc->sc_bell_data = wskbd_default_bell_data;
226 #if NWSMUX > 0
227 	sc->sc_base.me_ops = &wsbell_srcops;
228 	mux = device_cfdata(self)->wsbelldevcf_mux;
229 	if (mux >= 0) {
230 		error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
231 		if (error)
232 			aprint_error(" attach error=%d", error);
233 		else
234 			aprint_normal(" mux %d", mux);
235 	}
236 #else
237 	if (device_cfdata(self)->wsbelldevcf_mux >= 0)
238 		aprint_normal(" (mux ignored)");
239 #endif
240 
241 	aprint_naive("\n");
242 	aprint_normal("\n");
243 
244 	if (!pmf_device_register(self, NULL, NULL))
245 		aprint_error_dev(self, "couldn't establish power handler\n");
246 
247 	mutex_init(&sc->sc_bellock, MUTEX_DEFAULT, IPL_SCHED);
248 	cv_init(&sc->sc_bellcv, "bellcv");
249 
250 	kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
251 	    bell_thread, sc, &sc->sc_bellthread, "%s", device_xname(self));
252 }
253 
254 int
255 wsbell_activate(device_t self, enum devact act)
256 {
257 	struct wsbell_softc *sc = device_private(self);
258 
259 	if (act == DVACT_DEACTIVATE)
260 		sc->sc_dying = true;
261 	return (0);
262 }
263 
264 int
265 wsbell_detach(device_t self, int flags)
266 {
267 	struct wsbell_softc *sc = device_private(self);
268 	struct wseventvar *evar;
269 	int maj, mn;
270 	int s;
271 
272 #if NWSMUX > 0
273 	/* Tell parent mux we're leaving. */
274 	if (sc->sc_base.me_parent != NULL) {
275 		DPRINTF(("wsbell_detach:\n"));
276 		wsmux_detach_sc(&sc->sc_base);
277 	}
278 #endif
279 
280 	/* If we're open ... */
281 	evar = sc->sc_base.me_evp;
282 	if (evar != NULL && evar->io != NULL) {
283 		s = spltty();
284 		if (--sc->sc_refcnt >= 0) {
285 			struct wscons_event event;
286 
287 			/* Wake everyone by generating a dummy event. */
288 			event.type = 0;
289 			event.value = 0;
290 			if (wsevent_inject(evar, &event, 1) != 0)
291 				wsevent_wakeup(evar);
292 
293 			/* Wait for processes to go away. */
294 			if (tsleep(sc, PZERO, "wsmdet", hz * 60))
295 				printf("wsbell_detach: %s didn't detach\n",
296 				       device_xname(self));
297 		}
298 		splx(s);
299 	}
300 
301 	/* locate the major number */
302 	maj = cdevsw_lookup_major(&wsbell_cdevsw);
303 
304 	/* Nuke the vnodes for any open instances (calls close). */
305 	mn = device_unit(self);
306 	vdevgone(maj, mn, mn, VCHR);
307 
308 	mutex_enter(&sc->sc_bellock);
309 	sc->sc_dying = true;
310 
311 	cv_broadcast(&sc->sc_bellcv);
312 	mutex_exit(&sc->sc_bellock);
313 
314 	kthread_join(sc->sc_bellthread);
315 	cv_destroy(&sc->sc_bellcv);
316 	mutex_destroy(&sc->sc_bellock);
317 
318 	return (0);
319 }
320 
321 #if NWSMUX > 0
322 int
323 wsbelldoopen(struct wsbell_softc *sc, struct wseventvar *evp)
324 {
325 	return (0);
326 }
327 
328 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
329 int
330 wsbelldoioctl(device_t dv, u_long cmd, void *data, int flag,
331 	       struct lwp *l)
332 {
333 	struct wsbell_softc *sc = device_private(dv);
334 	int error;
335 
336 	sc->sc_refcnt++;
337 	error = wsbell_do_ioctl(sc, cmd, data, flag, l);
338 	if (--sc->sc_refcnt < 0)
339 		wakeup(sc);
340 	return (error);
341 }
342 
343 int
344 wsbell_do_ioctl(struct wsbell_softc *sc, u_long cmd, void *data,
345 		 int flag, struct lwp *l)
346 {
347 	struct wskbd_bell_data *ubdp, *kbdp;
348 	int error;
349 
350 	if (sc->sc_dying == true)
351 		return (EIO);
352 
353 	/*
354 	 * Try the wsbell specific ioctls.
355 	 */
356 	switch (cmd) {
357 	case WSKBDIO_SETBELL:
358 		if ((flag & FWRITE) == 0)
359 			return (EACCES);
360 		kbdp = &sc->sc_bell_data;
361 setbell:
362 		ubdp = (struct wskbd_bell_data *)data;
363 		SETBELL(kbdp, ubdp, kbdp);
364 		return (0);
365 
366 	case WSKBDIO_GETBELL:
367 		kbdp = &sc->sc_bell_data;
368 getbell:
369 		ubdp = (struct wskbd_bell_data *)data;
370 		SETBELL(ubdp, kbdp, kbdp);
371 		return (0);
372 
373 	case WSKBDIO_SETDEFAULTBELL:
374 		if ((error = kauth_authorize_device(l->l_cred,
375 		    KAUTH_DEVICE_WSCONS_KEYBOARD_BELL, NULL, NULL,
376 		    NULL, NULL)) != 0)
377 			return (error);
378 		kbdp = &wskbd_default_bell_data;
379 		goto setbell;
380 
381 
382 	case WSKBDIO_GETDEFAULTBELL:
383 		kbdp = &wskbd_default_bell_data;
384 		goto getbell;
385 
386 	case WSKBDIO_BELL:
387 		if ((flag & FWRITE) == 0)
388 			return (EACCES);
389 		spkr_audio_play(sc, sc->sc_bell_data.pitch,
390 		    sc->sc_bell_data.period, sc->sc_bell_data.volume);
391 
392 		return 0;
393 
394 	case WSKBDIO_COMPLEXBELL:
395 		if ((flag & FWRITE) == 0)
396 			return (EACCES);
397 		if (data == NULL)
398 			return 0;
399 		ubdp = (struct wskbd_bell_data *)data;
400 		SETBELL(ubdp, ubdp, &sc->sc_bell_data);
401 		spkr_audio_play(sc, ubdp->pitch, ubdp->period, ubdp->volume);
402 		return 0;
403 	}
404 
405 	return (EPASSTHROUGH);
406 }
407 #endif
408 
409 static void
410 bell_thread(void *arg)
411 {
412 	struct wsbell_softc *sc = arg;
413 	struct vbell_args *vb = &sc->sc_bell_args;
414 	tone_t tone;
415 	u_int vol;
416 
417 	for (;;) {
418 		mutex_enter(&sc->sc_bellock);
419 		cv_wait_sig(&sc->sc_bellcv, &sc->sc_bellock);
420 
421 		if (sc->sc_dying == true) {
422 			mutex_exit(&sc->sc_bellock);
423 			kthread_exit(0);
424 		}
425 
426 		tone.frequency = vb->pitch;
427 		/*
428 		 * period (derived from wskbd) is in msec.
429 		 * duration (derived from spkr) is in units of 10msec.
430 		 */
431 		tone.duration = vb->period / 10;
432 		vol = vb->volume;
433 		mutex_exit(&sc->sc_bellock);
434 
435 		if (spkropen(sc->sc_spkr, FWRITE, 0, NULL) != 0)
436 			continue;
437 		spkrioctl(sc->sc_spkr, SPKRSETVOL, &vol, 0, curlwp);
438 		spkrioctl(sc->sc_spkr, SPKRTONE, &tone, 0, curlwp);
439 		spkrclose(sc->sc_spkr, FWRITE, 0, curlwp);
440 	}
441 }
442 
443 static inline void
444 spkr_audio_play(struct wsbell_softc *sc, u_int pitch, u_int period, u_int volume)
445 {
446 
447 	mutex_enter(&sc->sc_bellock);
448 	sc->sc_bell_args.pitch = pitch;
449 	sc->sc_bell_args.period = period;
450 	sc->sc_bell_args.volume = volume;
451 
452 	cv_broadcast(&sc->sc_bellcv);
453 	mutex_exit(&sc->sc_bellock);
454 }
455 
456 #if NWSMUX > 0
457 int
458 wsbell_mux_open(struct wsevsrc *me, struct wseventvar *evp)
459 {
460 	struct wsbell_softc *sc = (struct wsbell_softc *)me;
461 
462 	if (sc->sc_base.me_evp != NULL)
463 		return (EBUSY);
464 
465 	return wsbelldoopen(sc, evp);
466 }
467 
468 int
469 wsbell_mux_close(struct wsevsrc *me)
470 {
471 	struct wsbell_softc *sc = (struct wsbell_softc *)me;
472 
473 	sc->sc_base.me_evp = NULL;
474 
475 	return (0);
476 }
477 #endif /* NWSMUX > 0 */
478 
479 MODULE(MODULE_CLASS_DRIVER, wsbell, "spkr");
480 
481 #ifdef _MODULE
482 int wsbell_bmajor = -1, wsbell_cmajor = -1;
483 
484 #include "ioconf.c"
485 #endif
486 
487 static int
488 wsbell_modcmd(modcmd_t cmd, void *arg)
489 {
490 	int error = 0;
491 
492 	switch (cmd) {
493 	case MODULE_CMD_INIT:
494 #ifdef _MODULE
495 		error = devsw_attach("wsbell", NULL, &wsbell_bmajor,
496 		    &wsbell_cdevsw, &wsbell_cmajor);
497 		if (error)
498 			break;
499 
500 		error = config_init_component(cfdriver_ioconf_wsbell,
501 		    cfattach_ioconf_wsbell, cfdata_ioconf_wsbell);
502 		if (error)
503 			devsw_detach(NULL, &wsbell_cdevsw);
504 #endif
505 		break;
506 
507 	case MODULE_CMD_FINI:
508 #ifdef _MODULE
509 		devsw_detach(NULL, &wsbell_cdevsw);
510 		error = config_fini_component(cfdriver_ioconf_wsbell,
511 		    cfattach_ioconf_wsbell, cfdata_ioconf_wsbell);
512 		if (error)
513 			devsw_attach("wsbell", NULL, &wsbell_bmajor,
514 			    &wsbell_cdevsw, &wsbell_cmajor);
515 #endif
516 		break;
517 
518 	default:
519 		error = ENOTTY;
520 		break;
521 	}
522 
523 	return error;
524 }
525