wsbell.c revision 1.11 1 /* $NetBSD: wsbell.c,v 1.11 2019/04/18 14:01:28 isaki Exp $ */
2
3 /*-
4 * Copyright (c) 2017 Nathanial Sloss <nathanialsloss (at) 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.11 2019/04/18 14:01:28 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 #define d ((struct wskbd_bell_data *)data)
400 spkr_audio_play(sc, d->pitch, d->period, d->volume);
401 #undef d
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