1 /* $NetBSD: wsbell.c,v 1.13 2020/12/27 16:09:33 tsutsui 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.13 2020/12/27 16:09:33 tsutsui 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 204 wsbell_match(device_t parent, cfdata_t match, void *aux) 205 { 206 return (1); 207 } 208 209 void 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 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 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 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 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 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 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 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 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 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 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 devsw_detach(NULL, &wsbell_cdevsw); 508 error = config_fini_component(cfdriver_ioconf_wsbell, 509 cfattach_ioconf_wsbell, cfdata_ioconf_wsbell); 510 if (error) 511 devsw_attach("wsbell", NULL, &wsbell_bmajor, 512 &wsbell_cdevsw, &wsbell_cmajor); 513 #endif 514 break; 515 516 default: 517 error = ENOTTY; 518 break; 519 } 520 521 return error; 522 } 523