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