wsbell.c revision 1.1 1 /* $NetBSD: wsbell.c,v 1.1 2017/06/11 03:55:56 nat 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.1 2017/06/11 03:55:56 nat Exp $");
111
112 #include "wsdisplay.h"
113 #include "wsmux.h"
114
115 #include <sys/param.h>
116 #include <sys/conf.h>
117 #include <sys/ioctl.h>
118 #include <sys/poll.h>
119 #include <sys/fcntl.h>
120 #include <sys/kernel.h>
121 #include <sys/condvar.h>
122 #include <sys/mutex.h>
123 #include <sys/kthread.h>
124 #include <sys/proc.h>
125 #include <sys/syslog.h>
126 #include <sys/systm.h>
127 #include <sys/tty.h>
128 #include <sys/signalvar.h>
129 #include <sys/device.h>
130 #include <sys/vnode.h>
131 #include <sys/callout.h>
132 #include <sys/malloc.h>
133
134 #include <dev/wscons/wsconsio.h>
135 #include <dev/wscons/wsbellvar.h>
136 #include <dev/wscons/wsbellmuxvar.h>
137 #include <dev/wscons/wsbelldata.h>
138
139 #include <dev/spkrio.h>
140
141 #if defined(WSMUX_DEBUG) && NWSMUX > 0
142 #define DPRINTF(x) if (wsmuxdebug) printf x
143 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
144 extern int wsmuxdebug;
145 #else
146 #define DPRINTF(x)
147 #define DPRINTFN(n,x)
148 #endif
149
150 static void bell_thread(void *);
151 static inline void spkr_audio_play(struct wsbell_softc *, u_int, u_int, u_int);
152
153 static int wsbell_match(device_t, cfdata_t, void *);
154 static void wsbell_attach(device_t, device_t, void *);
155 static int wsbell_detach(device_t, int);
156 static int wsbell_activate(device_t, enum devact);
157
158 #if NWSMUX > 0
159 static int wsbell_mux_open(struct wsevsrc *, struct wseventvar *);
160 static int wsbell_mux_close(struct wsevsrc *);
161
162 static int wsbelldoopen(struct wsbell_softc *, struct wseventvar *);
163 static int wsbelldoioctl(device_t, u_long, void *, int, struct lwp *);
164
165 static int wsbell_do_ioctl(struct wsbell_softc *, u_long, void *,
166 int, struct lwp *);
167
168 #endif
169
170 CFATTACH_DECL_NEW(wsbell, sizeof (struct wsbell_softc),
171 wsbell_match, wsbell_attach, wsbell_detach, wsbell_activate);
172
173 extern struct cfdriver wsbell_cd;
174
175 extern dev_type_open(spkropen);
176 extern dev_type_close(spkrclose);
177 extern dev_type_ioctl(spkrioctl);
178
179 const struct cdevsw wsbell_cdevsw = {
180 .d_open = noopen,
181 .d_close = noclose,
182 .d_read = noread,
183 .d_write = nowrite,
184 .d_ioctl = noioctl,
185 .d_stop = nostop,
186 .d_tty = notty,
187 .d_poll = nopoll,
188 .d_mmap = nommap,
189 .d_kqfilter = nokqfilter,
190 .d_discard = nodiscard,
191 .d_flag = D_OTHER
192 };
193
194 #if NWSMUX > 0
195 struct wssrcops wsbell_srcops = {
196 WSMUX_BELL,
197 wsbell_mux_open, wsbell_mux_close, wsbelldoioctl, wsbelldoioctl, NULL
198 };
199 #endif
200
201 int
202 wsbell_match(device_t parent, cfdata_t match, void *aux)
203 {
204 return (1);
205 }
206
207 void
208 wsbell_attach(device_t parent, device_t self, void *aux)
209 {
210 struct wsbell_softc *sc = device_private(self);
211 struct wsbelldev_attach_args *ap = aux;
212 #if NWSMUX > 0
213 int mux, error;
214 #endif
215
216 sc->sc_base.me_dv = self;
217 sc->sc_accesscookie = ap->accesscookie;
218
219 sc->sc_spkr = device_unit(parent);
220 sc->sc_bell_data = wskbd_default_bell_data;
221 #if NWSMUX > 0
222 sc->sc_base.me_ops = &wsbell_srcops;
223 mux = device_cfdata(self)->wsbelldevcf_mux;
224 if (mux >= 0) {
225 error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
226 if (error)
227 aprint_error(" attach error=%d", error);
228 else
229 aprint_normal(" mux %d", mux);
230 }
231 #else
232 if (device_cfdata(self)->wsbelldevcf_mux >= 0)
233 aprint_normal(" (mux ignored)");
234 #endif
235
236 aprint_naive("\n");
237 aprint_normal("\n");
238
239 if (!pmf_device_register(self, NULL, NULL))
240 aprint_error_dev(self, "couldn't establish power handler\n");
241
242 mutex_init(&sc->sc_bellock, MUTEX_DEFAULT, IPL_SCHED);
243 cv_init(&sc->sc_bellcv, "bellcv");
244
245 kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
246 bell_thread, sc, &sc->sc_bellthread, "%s", device_xname(self));
247 }
248
249 int
250 wsbell_activate(device_t self, enum devact act)
251 {
252 struct wsbell_softc *sc = device_private(self);
253
254 if (act == DVACT_DEACTIVATE)
255 sc->sc_dying = 1;
256 return (0);
257 }
258
259 int
260 wsbell_detach(device_t self, int flags)
261 {
262 struct wsbell_softc *sc = device_private(self);
263 struct wseventvar *evar;
264 int maj, mn;
265 int s;
266
267 #if NWSMUX > 0
268 /* Tell parent mux we're leaving. */
269 if (sc->sc_base.me_parent != NULL) {
270 DPRINTF(("wsbell_detach:\n"));
271 wsmux_detach_sc(&sc->sc_base);
272 }
273 #endif
274
275 /* If we're open ... */
276 evar = sc->sc_base.me_evp;
277 if (evar != NULL && evar->io != NULL) {
278 s = spltty();
279 if (--sc->sc_refcnt >= 0) {
280 struct wscons_event event;
281
282 /* Wake everyone by generating a dummy event. */
283 event.type = 0;
284 event.value = 0;
285 if (wsevent_inject(evar, &event, 1) != 0)
286 wsevent_wakeup(evar);
287
288 /* Wait for processes to go away. */
289 if (tsleep(sc, PZERO, "wsmdet", hz * 60))
290 printf("wsbell_detach: %s didn't detach\n",
291 device_xname(self));
292 }
293 splx(s);
294 }
295
296 /* locate the major number */
297 maj = cdevsw_lookup_major(&wsbell_cdevsw);
298
299 /* Nuke the vnodes for any open instances (calls close). */
300 mn = device_unit(self);
301 vdevgone(maj, mn, mn, VCHR);
302
303 mutex_enter(&sc->sc_bellock);
304 sc->sc_bell_args.dying = true;
305
306 cv_broadcast(&sc->sc_bellcv);
307 mutex_exit(&sc->sc_bellock);
308
309 kthread_join(sc->sc_bellthread);
310 cv_destroy(&sc->sc_bellcv);
311 mutex_destroy(&sc->sc_bellock);
312
313 return (0);
314 }
315
316 #if NWSMUX > 0
317 int
318 wsbelldoopen(struct wsbell_softc *sc, struct wseventvar *evp)
319 {
320 return (0);
321 }
322
323 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
324 int
325 wsbelldoioctl(device_t dv, u_long cmd, void *data, int flag,
326 struct lwp *l)
327 {
328 struct wsbell_softc *sc = device_private(dv);
329 int error;
330
331 sc->sc_refcnt++;
332 error = wsbell_do_ioctl(sc, cmd, data, flag, l);
333 if (--sc->sc_refcnt < 0)
334 wakeup(sc);
335 return (error);
336 }
337
338 int
339 wsbell_do_ioctl(struct wsbell_softc *sc, u_long cmd, void *data,
340 int flag, struct lwp *l)
341 {
342 struct wskbd_bell_data *ubdp, *kbdp;
343 if (sc->sc_dying)
344 return (EIO);
345
346 /*
347 * Try the wsbell specific ioctls.
348 */
349 switch (cmd) {
350 #define SETBELL(dstp, srcp, dfltp) \
351 do { \
352 (dstp)->pitch = ((srcp)->which & WSKBD_BELL_DOPITCH) ? \
353 (srcp)->pitch : (dfltp)->pitch; \
354 (dstp)->period = ((srcp)->which & WSKBD_BELL_DOPERIOD) ? \
355 (srcp)->period : (dfltp)->period; \
356 (dstp)->volume = ((srcp)->which & WSKBD_BELL_DOVOLUME) ? \
357 (srcp)->volume : (dfltp)->volume; \
358 (dstp)->which = WSKBD_BELL_DOALL; \
359 } while (0)
360
361 case WSKBDIO_SETBELL:
362 if ((flag & FWRITE) == 0)
363 return (EACCES);
364 kbdp = &sc->sc_bell_data;
365 ubdp = (struct wskbd_bell_data *)data;
366 SETBELL(kbdp, ubdp, kbdp);
367 return (0);
368
369 case WSKBDIO_GETBELL:
370 kbdp = &sc->sc_bell_data;
371 ubdp = (struct wskbd_bell_data *)data;
372 SETBELL(ubdp, kbdp, kbdp);
373 return (0);
374
375 case WSKBDIO_BELL:
376 if ((flag & FWRITE) == 0)
377 return (EACCES);
378 spkr_audio_play(sc, sc->sc_bell_data.pitch,
379 sc->sc_bell_data.period, sc->sc_bell_data.volume);
380
381 return 0;
382
383 case WSKBDIO_COMPLEXBELL:
384 if ((flag & FWRITE) == 0)
385 return (EACCES);
386 if (data == NULL)
387 return 0;
388 #define d ((struct wskbd_bell_data *)data)
389 spkr_audio_play(sc, d->pitch, d->period, d->volume);
390 #undef d
391 return 0;
392 }
393
394 return (EPASSTHROUGH);
395 }
396 #endif
397
398 static void
399 bell_thread(void *arg)
400 {
401 struct wsbell_softc *sc = arg;
402 struct vbell_args *vb = &sc->sc_bell_args;
403 tone_t tone;
404 u_int vol;
405
406 for (;;) {
407 mutex_enter(&sc->sc_bellock);
408 cv_wait_sig(&sc->sc_bellcv, &sc->sc_bellock);
409
410 if (vb->dying == true) {
411 mutex_exit(&sc->sc_bellock);
412 kthread_exit(0);
413 }
414
415 tone.frequency = vb->pitch;
416 tone.duration = vb->period;
417 vol = vb->volume;
418 mutex_exit(&sc->sc_bellock);
419
420 if (spkropen(sc->sc_spkr, FWRITE, 0, NULL) != 0)
421 continue;
422 spkrioctl(sc->sc_spkr, SPKRSETVOL, &vol, 0, curlwp);
423 spkrioctl(sc->sc_spkr, SPKRTONE, &tone, 0, curlwp);
424 spkrclose(sc->sc_spkr, FWRITE, 0, curlwp);
425 }
426 }
427
428 static inline void
429 spkr_audio_play(struct wsbell_softc *sc, u_int pitch, u_int period, u_int volume)
430 {
431
432 mutex_enter(&sc->sc_bellock);
433 sc->sc_bell_args.dying = false;
434 sc->sc_bell_args.pitch = pitch;
435 sc->sc_bell_args.period = period / 5;
436 sc->sc_bell_args.volume = volume;
437
438 cv_broadcast(&sc->sc_bellcv);
439 mutex_exit(&sc->sc_bellock);
440 }
441
442 #if NWSMUX > 0
443 int
444 wsbell_mux_open(struct wsevsrc *me, struct wseventvar *evp)
445 {
446 struct wsbell_softc *sc = (struct wsbell_softc *)me;
447
448 if (sc->sc_base.me_evp != NULL)
449 return (EBUSY);
450
451 return wsbelldoopen(sc, evp);
452 }
453
454 int
455 wsbell_mux_close(struct wsevsrc *me)
456 {
457 struct wsbell_softc *sc = (struct wsbell_softc *)me;
458
459 sc->sc_base.me_evp = NULL;
460
461 return (0);
462 }
463 #endif /* NWSMUX > 0 */
464