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