xref: /netbsd/sys/dev/wscons/wsbell.c (revision 74849b7b)
1 /* $NetBSD: wsbell.c,v 1.14 2022/03/31 19:30:17 pgoyette 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.14 2022/03/31 19:30:17 pgoyette 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 dev_type_open(spkropen);
178 extern dev_type_close(spkrclose);
179 extern dev_type_ioctl(spkrioctl);
180 
181 const struct cdevsw wsbell_cdevsw = {
182 	.d_open = noopen,
183 	.d_close = noclose,
184 	.d_read = noread,
185 	.d_write = nowrite,
186 	.d_ioctl = noioctl,
187 	.d_stop = nostop,
188 	.d_tty = notty,
189 	.d_poll = nopoll,
190 	.d_mmap = nommap,
191 	.d_kqfilter = nokqfilter,
192 	.d_discard = nodiscard,
193 	.d_flag = D_OTHER
194 };
195 
196 #if NWSMUX > 0
197 struct wssrcops wsbell_srcops = {
198 	WSMUX_BELL,
199 	wsbell_mux_open, wsbell_mux_close, wsbelldoioctl, wsbelldoioctl, NULL
200 };
201 #endif
202 
203 int
wsbell_match(device_t parent,cfdata_t match,void * aux)204 wsbell_match(device_t parent, cfdata_t match, void *aux)
205 {
206 	return (1);
207 }
208 
209 void
wsbell_attach(device_t parent,device_t self,void * aux)210 wsbell_attach(device_t parent, device_t self, void *aux)
211 {
212 	struct wsbell_softc *sc = device_private(self);
213 	struct wsbelldev_attach_args *ap = aux;
214 #if NWSMUX > 0
215 	int mux, error;
216 #endif
217 
218 	sc->sc_base.me_dv = self;
219 	sc->sc_accesscookie = ap->accesscookie;
220 
221 	sc->sc_dying = false;
222 	sc->sc_spkr = device_unit(parent);
223 	sc->sc_bell_data = wskbd_default_bell_data;
224 #if NWSMUX > 0
225 	sc->sc_base.me_ops = &wsbell_srcops;
226 	mux = device_cfdata(self)->wsbelldevcf_mux;
227 	if (mux >= 0) {
228 		error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
229 		if (error)
230 			aprint_error(" attach error=%d", error);
231 		else
232 			aprint_normal(" mux %d", mux);
233 	}
234 #else
235 	if (device_cfdata(self)->wsbelldevcf_mux >= 0)
236 		aprint_normal(" (mux ignored)");
237 #endif
238 
239 	aprint_naive("\n");
240 	aprint_normal("\n");
241 
242 	if (!pmf_device_register(self, NULL, NULL))
243 		aprint_error_dev(self, "couldn't establish power handler\n");
244 
245 	mutex_init(&sc->sc_bellock, MUTEX_DEFAULT, IPL_SCHED);
246 	cv_init(&sc->sc_bellcv, "bellcv");
247 
248 	kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
249 	    bell_thread, sc, &sc->sc_bellthread, "%s", device_xname(self));
250 }
251 
252 int
wsbell_activate(device_t self,enum devact act)253 wsbell_activate(device_t self, enum devact act)
254 {
255 	struct wsbell_softc *sc = device_private(self);
256 
257 	if (act == DVACT_DEACTIVATE)
258 		sc->sc_dying = true;
259 	return (0);
260 }
261 
262 int
wsbell_detach(device_t self,int flags)263 wsbell_detach(device_t self, int flags)
264 {
265 	struct wsbell_softc *sc = device_private(self);
266 	struct wseventvar *evar;
267 	int maj, mn;
268 	int s;
269 
270 #if NWSMUX > 0
271 	/* Tell parent mux we're leaving. */
272 	if (sc->sc_base.me_parent != NULL) {
273 		DPRINTF(("wsbell_detach:\n"));
274 		wsmux_detach_sc(&sc->sc_base);
275 	}
276 #endif
277 
278 	/* If we're open ... */
279 	evar = sc->sc_base.me_evp;
280 	if (evar != NULL && evar->io != NULL) {
281 		s = spltty();
282 		if (--sc->sc_refcnt >= 0) {
283 			struct wscons_event event;
284 
285 			/* Wake everyone by generating a dummy event. */
286 			event.type = 0;
287 			event.value = 0;
288 			if (wsevent_inject(evar, &event, 1) != 0)
289 				wsevent_wakeup(evar);
290 
291 			/* Wait for processes to go away. */
292 			if (tsleep(sc, PZERO, "wsmdet", hz * 60))
293 				printf("wsbell_detach: %s didn't detach\n",
294 				       device_xname(self));
295 		}
296 		splx(s);
297 	}
298 
299 	/* locate the major number */
300 	maj = cdevsw_lookup_major(&wsbell_cdevsw);
301 
302 	/* Nuke the vnodes for any open instances (calls close). */
303 	mn = device_unit(self);
304 	vdevgone(maj, mn, mn, VCHR);
305 
306 	mutex_enter(&sc->sc_bellock);
307 	sc->sc_dying = true;
308 
309 	cv_broadcast(&sc->sc_bellcv);
310 	mutex_exit(&sc->sc_bellock);
311 
312 	kthread_join(sc->sc_bellthread);
313 	cv_destroy(&sc->sc_bellcv);
314 	mutex_destroy(&sc->sc_bellock);
315 
316 	return (0);
317 }
318 
319 #if NWSMUX > 0
320 int
wsbelldoopen(struct wsbell_softc * sc,struct wseventvar * evp)321 wsbelldoopen(struct wsbell_softc *sc, struct wseventvar *evp)
322 {
323 	return (0);
324 }
325 
326 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
327 int
wsbelldoioctl(device_t dv,u_long cmd,void * data,int flag,struct lwp * l)328 wsbelldoioctl(device_t dv, u_long cmd, void *data, int flag,
329 	       struct lwp *l)
330 {
331 	struct wsbell_softc *sc = device_private(dv);
332 	int error;
333 
334 	sc->sc_refcnt++;
335 	error = wsbell_do_ioctl(sc, cmd, data, flag, l);
336 	if (--sc->sc_refcnt < 0)
337 		wakeup(sc);
338 	return (error);
339 }
340 
341 int
wsbell_do_ioctl(struct wsbell_softc * sc,u_long cmd,void * data,int flag,struct lwp * l)342 wsbell_do_ioctl(struct wsbell_softc *sc, u_long cmd, void *data,
343 		 int flag, struct lwp *l)
344 {
345 	struct wskbd_bell_data *ubdp, *kbdp;
346 	int error;
347 
348 	if (sc->sc_dying == true)
349 		return (EIO);
350 
351 	/*
352 	 * Try the wsbell specific ioctls.
353 	 */
354 	switch (cmd) {
355 	case WSKBDIO_SETBELL:
356 		if ((flag & FWRITE) == 0)
357 			return (EACCES);
358 		kbdp = &sc->sc_bell_data;
359 setbell:
360 		ubdp = (struct wskbd_bell_data *)data;
361 		SETBELL(kbdp, ubdp, kbdp);
362 		return (0);
363 
364 	case WSKBDIO_GETBELL:
365 		kbdp = &sc->sc_bell_data;
366 getbell:
367 		ubdp = (struct wskbd_bell_data *)data;
368 		SETBELL(ubdp, kbdp, kbdp);
369 		return (0);
370 
371 	case WSKBDIO_SETDEFAULTBELL:
372 		if ((error = kauth_authorize_device(l->l_cred,
373 		    KAUTH_DEVICE_WSCONS_KEYBOARD_BELL, NULL, NULL,
374 		    NULL, NULL)) != 0)
375 			return (error);
376 		kbdp = &wskbd_default_bell_data;
377 		goto setbell;
378 
379 
380 	case WSKBDIO_GETDEFAULTBELL:
381 		kbdp = &wskbd_default_bell_data;
382 		goto getbell;
383 
384 	case WSKBDIO_BELL:
385 		if ((flag & FWRITE) == 0)
386 			return (EACCES);
387 		spkr_audio_play(sc, sc->sc_bell_data.pitch,
388 		    sc->sc_bell_data.period, sc->sc_bell_data.volume);
389 
390 		return 0;
391 
392 	case WSKBDIO_COMPLEXBELL:
393 		if ((flag & FWRITE) == 0)
394 			return (EACCES);
395 		if (data == NULL)
396 			return 0;
397 		ubdp = (struct wskbd_bell_data *)data;
398 		SETBELL(ubdp, ubdp, &sc->sc_bell_data);
399 		spkr_audio_play(sc, ubdp->pitch, ubdp->period, ubdp->volume);
400 		return 0;
401 	}
402 
403 	return (EPASSTHROUGH);
404 }
405 #endif
406 
407 static void
bell_thread(void * arg)408 bell_thread(void *arg)
409 {
410 	struct wsbell_softc *sc = arg;
411 	struct vbell_args *vb = &sc->sc_bell_args;
412 	tone_t tone;
413 	u_int vol;
414 
415 	for (;;) {
416 		mutex_enter(&sc->sc_bellock);
417 		cv_wait_sig(&sc->sc_bellcv, &sc->sc_bellock);
418 
419 		if (sc->sc_dying == true) {
420 			mutex_exit(&sc->sc_bellock);
421 			kthread_exit(0);
422 		}
423 
424 		tone.frequency = vb->pitch;
425 		/*
426 		 * period (derived from wskbd) is in msec.
427 		 * duration (derived from spkr) is in units of 10msec.
428 		 */
429 		tone.duration = vb->period / 10;
430 		vol = vb->volume;
431 		mutex_exit(&sc->sc_bellock);
432 
433 		if (spkropen(sc->sc_spkr, FWRITE, 0, NULL) != 0)
434 			continue;
435 		spkrioctl(sc->sc_spkr, SPKRSETVOL, &vol, 0, curlwp);
436 		spkrioctl(sc->sc_spkr, SPKRTONE, &tone, 0, curlwp);
437 		spkrclose(sc->sc_spkr, FWRITE, 0, curlwp);
438 	}
439 }
440 
441 static inline void
spkr_audio_play(struct wsbell_softc * sc,u_int pitch,u_int period,u_int volume)442 spkr_audio_play(struct wsbell_softc *sc, u_int pitch, u_int period, u_int volume)
443 {
444 
445 	mutex_enter(&sc->sc_bellock);
446 	sc->sc_bell_args.pitch = pitch;
447 	sc->sc_bell_args.period = period;
448 	sc->sc_bell_args.volume = volume;
449 
450 	cv_broadcast(&sc->sc_bellcv);
451 	mutex_exit(&sc->sc_bellock);
452 }
453 
454 #if NWSMUX > 0
455 int
wsbell_mux_open(struct wsevsrc * me,struct wseventvar * evp)456 wsbell_mux_open(struct wsevsrc *me, struct wseventvar *evp)
457 {
458 	struct wsbell_softc *sc = (struct wsbell_softc *)me;
459 
460 	if (sc->sc_base.me_evp != NULL)
461 		return (EBUSY);
462 
463 	return wsbelldoopen(sc, evp);
464 }
465 
466 int
wsbell_mux_close(struct wsevsrc * me)467 wsbell_mux_close(struct wsevsrc *me)
468 {
469 	struct wsbell_softc *sc = (struct wsbell_softc *)me;
470 
471 	sc->sc_base.me_evp = NULL;
472 
473 	return (0);
474 }
475 #endif /* NWSMUX > 0 */
476 
477 MODULE(MODULE_CLASS_DRIVER, wsbell, "spkr");
478 
479 #ifdef _MODULE
480 int wsbell_bmajor = -1, wsbell_cmajor = -1;
481 
482 #include "ioconf.c"
483 #endif
484 
485 static int
wsbell_modcmd(modcmd_t cmd,void * arg)486 wsbell_modcmd(modcmd_t cmd, void *arg)
487 {
488 	int error = 0;
489 
490 	switch (cmd) {
491 	case MODULE_CMD_INIT:
492 #ifdef _MODULE
493 		error = devsw_attach("wsbell", NULL, &wsbell_bmajor,
494 		    &wsbell_cdevsw, &wsbell_cmajor);
495 		if (error)
496 			break;
497 
498 		error = config_init_component(cfdriver_ioconf_wsbell,
499 		    cfattach_ioconf_wsbell, cfdata_ioconf_wsbell);
500 		if (error)
501 			devsw_detach(NULL, &wsbell_cdevsw);
502 #endif
503 		break;
504 
505 	case MODULE_CMD_FINI:
506 #ifdef _MODULE
507 		error = config_fini_component(cfdriver_ioconf_wsbell,
508 		    cfattach_ioconf_wsbell, cfdata_ioconf_wsbell);
509 		if (error == 0)
510 			devsw_detach(NULL, &wsbell_cdevsw);
511 #endif
512 		break;
513 
514 	default:
515 		error = ENOTTY;
516 		break;
517 	}
518 
519 	return error;
520 }
521