pad.c revision 1.70 1 /* $NetBSD: pad.c,v 1.70 2021/06/14 10:14:46 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pad.c,v 1.70 2021/06/14 10:14:46 riastradh Exp $");
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34
35 #include <sys/audioio.h>
36 #include <sys/buf.h>
37 #include <sys/condvar.h>
38 #include <sys/conf.h>
39 #include <sys/device.h>
40 #include <sys/file.h>
41 #include <sys/filedesc.h>
42 #include <sys/kauth.h>
43 #include <sys/kernel.h>
44 #include <sys/kmem.h>
45 #include <sys/module.h>
46 #include <sys/poll.h>
47 #include <sys/proc.h>
48 #include <sys/select.h>
49 #include <sys/stat.h>
50 #include <sys/vnode.h>
51
52 #include <dev/audio/audio_if.h>
53 #include <dev/audio/audiovar.h>
54
55 #include <dev/pad/padvar.h>
56
57 /* #define PAD_DEBUG */
58 #ifdef PAD_DEBUG
59 #define DPRINTF(fmt...) printf(fmt)
60 #else
61 #define DPRINTF(fmt...) /**/
62 #endif
63
64 #define MAXDEVS 128
65 #define PADCLONER 254
66 #define PADUNIT(x) minor(x)
67
68 #define PADFREQ 44100
69 #define PADCHAN 2
70 #define PADPREC 16
71
72 extern struct cfdriver pad_cd;
73 kmutex_t padconfig;
74
75 typedef struct pad_block {
76 uint8_t *pb_ptr;
77 int pb_len;
78 } pad_block_t;
79
80 enum {
81 PAD_OUTPUT_CLASS,
82 PAD_INPUT_CLASS,
83 PAD_OUTPUT_MASTER_VOLUME,
84 PAD_INPUT_DAC_VOLUME,
85 PAD_ENUM_LAST,
86 };
87
88 static int pad_match(device_t, cfdata_t, void *);
89 static void pad_attach(device_t, device_t, void *);
90 static int pad_detach(device_t, int);
91 static void pad_childdet(device_t, device_t);
92
93 static int pad_query_format(void *, audio_format_query_t *);
94 static int pad_set_format(void *, int,
95 const audio_params_t *, const audio_params_t *,
96 audio_filter_reg_t *, audio_filter_reg_t *);
97 static int pad_start_output(void *, void *, int,
98 void (*)(void *), void *);
99 static int pad_halt_output(void *);
100 static int pad_getdev(void *, struct audio_device *);
101 static int pad_set_port(void *, mixer_ctrl_t *);
102 static int pad_get_port(void *, mixer_ctrl_t *);
103 static int pad_query_devinfo(void *, mixer_devinfo_t *);
104 static int pad_get_props(void *);
105 static void pad_get_locks(void *, kmutex_t **, kmutex_t **);
106
107 static void pad_done_output(void *);
108 static void pad_swvol_codec(audio_filter_arg_t *);
109
110 static int pad_close(struct pad_softc *);
111 static int pad_read(struct pad_softc *, off_t *, struct uio *,
112 kauth_cred_t, int);
113
114 static int fops_pad_close(struct file *);
115 static int fops_pad_read(struct file *, off_t *, struct uio *,
116 kauth_cred_t, int);
117 static int fops_pad_write(struct file *, off_t *, struct uio *,
118 kauth_cred_t, int);
119 static int fops_pad_ioctl(struct file *, u_long, void *);
120 static int fops_pad_kqfilter(struct file *, struct knote *);
121 static int fops_pad_poll(struct file *, int);
122 static int fops_pad_stat(struct file *, struct stat *);
123 static int fops_pad_mmap(struct file *, off_t *, size_t, int, int *, int *,
124 struct uvm_object **, int *);
125
126 static const struct audio_hw_if pad_hw_if = {
127 .query_format = pad_query_format,
128 .set_format = pad_set_format,
129 .start_output = pad_start_output,
130 .halt_output = pad_halt_output,
131 .getdev = pad_getdev,
132 .set_port = pad_set_port,
133 .get_port = pad_get_port,
134 .query_devinfo = pad_query_devinfo,
135 .get_props = pad_get_props,
136 .get_locks = pad_get_locks,
137 };
138
139 #define PAD_NFORMATS 1
140 static const struct audio_format pad_formats[PAD_NFORMATS] = {
141 {
142 .mode = AUMODE_PLAY,
143 .encoding = AUDIO_ENCODING_SLINEAR_LE,
144 .validbits = PADPREC,
145 .precision = PADPREC,
146 .channels = PADCHAN,
147 .channel_mask = AUFMT_STEREO,
148 .frequency_type = 1,
149 .frequency = { PADFREQ },
150 },
151 };
152
153 extern void padattach(int);
154
155 static int pad_add_block(struct pad_softc *, uint8_t *, int);
156 static int pad_get_block(struct pad_softc *, pad_block_t *, int);
157
158 dev_type_open(cdev_pad_open);
159 dev_type_close(cdev_pad_close);
160 dev_type_read(cdev_pad_read);
161
162 const struct cdevsw pad_cdevsw = {
163 .d_open = cdev_pad_open,
164 .d_close = cdev_pad_close,
165 .d_read = cdev_pad_read,
166 .d_write = nowrite,
167 .d_ioctl = noioctl,
168 .d_stop = nostop,
169 .d_tty = notty,
170 .d_poll = nopoll,
171 .d_mmap = nommap,
172 .d_kqfilter = nokqfilter,
173 .d_discard = nodiscard,
174 .d_flag = D_OTHER | D_MPSAFE,
175 };
176
177 const struct fileops pad_fileops = {
178 .fo_name = "pad",
179 .fo_read = fops_pad_read,
180 .fo_write = fops_pad_write,
181 .fo_ioctl = fops_pad_ioctl,
182 .fo_fcntl = fnullop_fcntl,
183 .fo_stat = fops_pad_stat,
184 .fo_poll = fops_pad_poll,
185 .fo_close = fops_pad_close,
186 .fo_mmap = fops_pad_mmap,
187 .fo_kqfilter = fops_pad_kqfilter,
188 .fo_restart = fnullop_restart
189 };
190
191 CFATTACH_DECL2_NEW(pad, sizeof(struct pad_softc),
192 pad_match, pad_attach, pad_detach,
193 NULL, NULL, pad_childdet);
194
195 void
196 padattach(int n)
197 {
198 int error;
199
200 error = config_cfattach_attach(pad_cd.cd_name, &pad_ca);
201 if (error) {
202 aprint_error("%s: couldn't register cfattach: %d\n",
203 pad_cd.cd_name, error);
204 config_cfdriver_detach(&pad_cd);
205 return;
206 }
207 mutex_init(&padconfig, MUTEX_DEFAULT, IPL_NONE);
208
209 return;
210 }
211
212 static int
213 pad_match(device_t parent, cfdata_t data, void *opaque)
214 {
215
216 return 1;
217 }
218
219 static void
220 pad_attach(device_t parent, device_t self, void *opaque)
221 {
222 struct pad_softc *sc = device_private(self);
223
224 aprint_normal_dev(self, "outputs: 44100Hz, 16-bit, stereo\n");
225
226 sc->sc_dev = self;
227
228 cv_init(&sc->sc_condvar, device_xname(sc->sc_dev));
229 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
230 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
231 callout_init(&sc->sc_pcallout, CALLOUT_MPSAFE);
232 callout_setfunc(&sc->sc_pcallout, pad_done_output, sc);
233
234 sc->sc_swvol = 255;
235 sc->sc_buflen = 0;
236 sc->sc_rpos = sc->sc_wpos = 0;
237 sc->sc_audiodev = audio_attach_mi(&pad_hw_if, sc, sc->sc_dev);
238
239 if (!pmf_device_register(sc->sc_dev, NULL, NULL))
240 aprint_error_dev(sc->sc_dev,
241 "couldn't establish power handler\n");
242 }
243
244 static int
245 pad_detach(device_t self, int flags)
246 {
247 struct pad_softc *sc = device_private(self);
248 int cmaj, mn;
249 int error;
250
251 error = config_detach_children(self, flags);
252 if (error)
253 return error;
254
255 cmaj = cdevsw_lookup_major(&pad_cdevsw);
256 mn = device_unit(sc->sc_dev);
257 vdevgone(cmaj, mn, mn, VCHR);
258
259 pmf_device_deregister(sc->sc_dev);
260
261 mutex_destroy(&sc->sc_lock);
262 mutex_destroy(&sc->sc_intr_lock);
263 cv_destroy(&sc->sc_condvar);
264
265 return 0;
266 }
267
268 static void
269 pad_childdet(device_t self, device_t child)
270 {
271 struct pad_softc *sc = device_private(self);
272
273 if (child == sc->sc_audiodev)
274 sc->sc_audiodev = NULL;
275 }
276
277 static int
278 pad_add_block(struct pad_softc *sc, uint8_t *blk, int blksize)
279 {
280 int l;
281
282 if (sc->sc_buflen + blksize > PAD_BUFSIZE)
283 return ENOBUFS;
284
285 if (sc->sc_wpos + blksize <= PAD_BUFSIZE)
286 memcpy(sc->sc_audiobuf + sc->sc_wpos, blk, blksize);
287 else {
288 l = PAD_BUFSIZE - sc->sc_wpos;
289 memcpy(sc->sc_audiobuf + sc->sc_wpos, blk, l);
290 memcpy(sc->sc_audiobuf, blk + l, blksize - l);
291 }
292
293 sc->sc_wpos += blksize;
294 if (sc->sc_wpos >= PAD_BUFSIZE)
295 sc->sc_wpos -= PAD_BUFSIZE;
296
297 sc->sc_buflen += blksize;
298
299 return 0;
300 }
301
302 static int
303 pad_get_block(struct pad_softc *sc, pad_block_t *pb, int blksize)
304 {
305 int l;
306
307 KASSERT(pb != NULL);
308
309 pb->pb_ptr = (sc->sc_audiobuf + sc->sc_rpos);
310 if (sc->sc_rpos + blksize < PAD_BUFSIZE) {
311 pb->pb_len = blksize;
312 sc->sc_rpos += blksize;
313 } else {
314 l = PAD_BUFSIZE - sc->sc_rpos;
315 pb->pb_len = l;
316 sc->sc_rpos = 0;
317 }
318 sc->sc_buflen -= pb->pb_len;
319
320 return 0;
321 }
322
323 int
324 cdev_pad_open(dev_t dev, int flags, int fmt, struct lwp *l)
325 {
326 struct pad_softc *sc;
327 struct file *fp;
328 device_t paddev;
329 cfdata_t cf;
330 int error, fd, i;
331
332 error = 0;
333
334 mutex_enter(&padconfig);
335 if (PADUNIT(dev) == PADCLONER) {
336 for (i = 0; i < MAXDEVS; i++) {
337 if (device_lookup(&pad_cd, i) == NULL)
338 break;
339 }
340 if (i == MAXDEVS)
341 goto bad;
342 } else {
343 if (PADUNIT(dev) >= MAXDEVS)
344 goto bad;
345 i = PADUNIT(dev);
346 }
347
348 cf = kmem_alloc(sizeof(struct cfdata), KM_SLEEP);
349 cf->cf_name = pad_cd.cd_name;
350 cf->cf_atname = pad_cd.cd_name;
351 cf->cf_unit = i;
352 cf->cf_fstate = FSTATE_STAR;
353
354 bool existing = false;
355 paddev = device_lookup(&pad_cd, minor(dev));
356 if (paddev == NULL)
357 paddev = config_attach_pseudo(cf);
358 else
359 existing = true;
360 if (paddev == NULL)
361 goto bad;
362
363 sc = device_private(paddev);
364 if (sc == NULL)
365 goto bad;
366
367 if (sc->sc_open == 1) {
368 mutex_exit(&padconfig);
369 return EBUSY;
370 }
371
372 if (PADUNIT(dev) == PADCLONER) {
373 error = fd_allocfile(&fp, &fd);
374 if (error) {
375 if (existing == false)
376 config_detach(paddev, 0);
377 mutex_exit(&padconfig);
378 return error;
379 }
380 error = fd_clone(fp, fd, flags, &pad_fileops, sc);
381 KASSERT(error == EMOVEFD);
382 }
383 sc->sc_open = 1;
384 mutex_exit(&padconfig);
385
386 return error;
387 bad:
388 mutex_exit(&padconfig);
389 return ENXIO;
390 }
391
392 static int
393 pad_close(struct pad_softc *sc)
394 {
395 int rc __diagused;
396
397 /* XXX Defend against concurrent drvctl detach. */
398 rc = config_detach(sc->sc_dev, DETACH_FORCE);
399 KASSERT(rc == 0);
400
401 return 0;
402 }
403
404 static int
405 fops_pad_close(struct file *fp)
406 {
407 struct pad_softc *sc = fp->f_pad;
408
409 pad_close(sc);
410 fp->f_pad = NULL;
411
412 return 0;
413 }
414
415 int
416 cdev_pad_close(dev_t dev, int flags, int ifmt, struct lwp *l)
417 {
418 struct pad_softc *sc;
419
420 /*
421 * XXX Defend against concurrent drvctl detach. Simply testing
422 * sc == NULL here is not enough -- it could be detached after
423 * we look it up but before we've issued our own config_detach.
424 */
425 sc = device_lookup_private(&pad_cd, PADUNIT(dev));
426 if (sc == NULL)
427 return 0;
428 pad_close(sc);
429
430 return 0;
431 }
432
433 static int
434 fops_pad_poll(struct file *fp, int events)
435 {
436
437 return POLLERR;
438 }
439
440 static int
441 fops_pad_kqfilter(struct file *fp, struct knote *kn)
442 {
443 struct pad_softc *sc = fp->f_pad;
444 dev_t dev;
445
446 dev = makedev(cdevsw_lookup_major(&pad_cdevsw),
447 device_unit(sc->sc_dev));
448
449 return seltrue_kqfilter(dev, kn);
450 }
451
452 static int
453 fops_pad_ioctl(struct file *fp, u_long cmd, void *data)
454 {
455
456 return ENODEV;
457 }
458
459 static int
460 fops_pad_stat(struct file *fp, struct stat *st)
461 {
462 struct pad_softc *sc = fp->f_pad;
463
464 memset(st, 0, sizeof(*st));
465
466 st->st_dev = makedev(cdevsw_lookup_major(&pad_cdevsw),
467 device_unit(sc->sc_dev));
468
469 st->st_uid = kauth_cred_geteuid(fp->f_cred);
470 st->st_gid = kauth_cred_getegid(fp->f_cred);
471 st->st_mode = S_IFCHR;
472
473 return 0;
474 }
475
476 static int
477 fops_pad_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
478 int *advicep, struct uvm_object **uobjp, int *maxprotp)
479 {
480
481 return 1;
482 }
483
484 int
485 cdev_pad_read(dev_t dev, struct uio *uio, int ioflag)
486 {
487 struct pad_softc *sc;
488
489 sc = device_private(device_lookup(&pad_cd, PADUNIT(dev)));
490 if (sc == NULL)
491 return ENXIO;
492
493 return pad_read(sc, NULL, uio, NULL, ioflag);
494 }
495
496 static int
497 fops_pad_read(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
498 int ioflag)
499 {
500 struct pad_softc *sc = fp->f_pad;
501
502 return pad_read(sc, offp, uio, cred, ioflag);
503 }
504
505 static int
506 pad_read(struct pad_softc *sc, off_t *offp, struct uio *uio, kauth_cred_t cred,
507 int ioflag)
508 {
509 pad_block_t pb;
510 int len;
511 int err;
512
513 err = 0;
514 DPRINTF("%s: resid=%zu\n", __func__, uio->uio_resid);
515 while (uio->uio_resid > 0 && !err) {
516 mutex_enter(&sc->sc_intr_lock);
517 if (sc->sc_buflen == 0) {
518 DPRINTF("%s: wait\n", __func__);
519 err = cv_wait_sig(&sc->sc_condvar, &sc->sc_intr_lock);
520 DPRINTF("%s: wake up %d\n", __func__, err);
521 mutex_exit(&sc->sc_intr_lock);
522 if (err) {
523 if (err == ERESTART)
524 err = EINTR;
525 break;
526 }
527 if (sc->sc_buflen == 0)
528 break;
529 continue;
530 }
531
532 len = uimin(uio->uio_resid, sc->sc_buflen);
533 err = pad_get_block(sc, &pb, len);
534 mutex_exit(&sc->sc_intr_lock);
535 if (err)
536 break;
537 DPRINTF("%s: move %d\n", __func__, pb.pb_len);
538 uiomove(pb.pb_ptr, pb.pb_len, uio);
539 }
540
541 return err;
542 }
543
544 static int
545 fops_pad_write(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
546 int ioflag)
547 {
548
549 return EOPNOTSUPP;
550 }
551
552 static int
553 pad_query_format(void *opaque, audio_format_query_t *afp)
554 {
555
556 return audio_query_format(pad_formats, PAD_NFORMATS, afp);
557 }
558
559 static int
560 pad_set_format(void *opaque, int setmode,
561 const audio_params_t *play, const audio_params_t *rec,
562 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
563 {
564 struct pad_softc *sc = opaque;
565
566 KASSERT(mutex_owned(&sc->sc_lock));
567
568 /* XXX playback only */
569 pfil->codec = pad_swvol_codec;
570 pfil->context = sc;
571
572 return 0;
573 }
574
575 static int
576 pad_start_output(void *opaque, void *block, int blksize,
577 void (*intr)(void *), void *intrarg)
578 {
579 struct pad_softc *sc = opaque;
580 int err;
581 int ms;
582
583 KASSERT(mutex_owned(&sc->sc_intr_lock));
584
585 sc->sc_intr = intr;
586 sc->sc_intrarg = intrarg;
587 sc->sc_blksize = blksize;
588
589 DPRINTF("%s: blksize=%d\n", __func__, blksize);
590 err = pad_add_block(sc, block, blksize);
591 cv_broadcast(&sc->sc_condvar);
592
593 ms = blksize * 1000 / PADCHAN / (PADPREC / NBBY) / PADFREQ;
594 DPRINTF("%s: callout ms=%d\n", __func__, ms);
595 callout_schedule(&sc->sc_pcallout, mstohz(ms));
596
597 return err;
598 }
599
600 static int
601 pad_halt_output(void *opaque)
602 {
603 struct pad_softc *sc = opaque;
604
605 DPRINTF("%s\n", __func__);
606 KASSERT(mutex_owned(&sc->sc_intr_lock));
607
608 callout_halt(&sc->sc_pcallout, &sc->sc_intr_lock);
609
610 sc->sc_intr = NULL;
611 sc->sc_intrarg = NULL;
612 sc->sc_buflen = 0;
613 sc->sc_rpos = sc->sc_wpos = 0;
614 cv_broadcast(&sc->sc_condvar);
615
616 return 0;
617 }
618
619 static void
620 pad_done_output(void *arg)
621 {
622 struct pad_softc *sc = arg;
623
624 DPRINTF("%s\n", __func__);
625
626 mutex_enter(&sc->sc_intr_lock);
627 (*sc->sc_intr)(sc->sc_intrarg);
628 mutex_exit(&sc->sc_intr_lock);
629 }
630
631 static int
632 pad_getdev(void *opaque, struct audio_device *ret)
633 {
634
635 strlcpy(ret->name, "Virtual Audio", sizeof(ret->name));
636 strlcpy(ret->version, osrelease, sizeof(ret->version));
637 strlcpy(ret->config, "pad", sizeof(ret->config));
638
639 return 0;
640 }
641
642 static int
643 pad_set_port(void *opaque, mixer_ctrl_t *mc)
644 {
645 struct pad_softc *sc = opaque;
646
647 KASSERT(mutex_owned(&sc->sc_lock));
648
649 switch (mc->dev) {
650 case PAD_OUTPUT_MASTER_VOLUME:
651 case PAD_INPUT_DAC_VOLUME:
652 if (mc->un.value.num_channels != 1)
653 return EINVAL;
654 sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
655 return 0;
656 }
657
658 return ENXIO;
659 }
660
661 static int
662 pad_get_port(void *opaque, mixer_ctrl_t *mc)
663 {
664 struct pad_softc *sc = opaque;
665
666 KASSERT(mutex_owned(&sc->sc_lock));
667
668 switch (mc->dev) {
669 case PAD_OUTPUT_MASTER_VOLUME:
670 case PAD_INPUT_DAC_VOLUME:
671 if (mc->un.value.num_channels != 1)
672 return EINVAL;
673 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_swvol;
674 return 0;
675 }
676
677 return ENXIO;
678 }
679
680 static int
681 pad_query_devinfo(void *opaque, mixer_devinfo_t *di)
682 {
683 struct pad_softc *sc __diagused = opaque;
684
685 KASSERT(mutex_owned(&sc->sc_lock));
686
687 switch (di->index) {
688 case PAD_OUTPUT_CLASS:
689 di->mixer_class = PAD_OUTPUT_CLASS;
690 strcpy(di->label.name, AudioCoutputs);
691 di->type = AUDIO_MIXER_CLASS;
692 di->next = di->prev = AUDIO_MIXER_LAST;
693 return 0;
694 case PAD_INPUT_CLASS:
695 di->mixer_class = PAD_INPUT_CLASS;
696 strcpy(di->label.name, AudioCinputs);
697 di->type = AUDIO_MIXER_CLASS;
698 di->next = di->prev = AUDIO_MIXER_LAST;
699 return 0;
700 case PAD_OUTPUT_MASTER_VOLUME:
701 di->mixer_class = PAD_OUTPUT_CLASS;
702 strcpy(di->label.name, AudioNmaster);
703 di->type = AUDIO_MIXER_VALUE;
704 di->next = di->prev = AUDIO_MIXER_LAST;
705 di->un.v.num_channels = 1;
706 strcpy(di->un.v.units.name, AudioNvolume);
707 return 0;
708 case PAD_INPUT_DAC_VOLUME:
709 di->mixer_class = PAD_INPUT_CLASS;
710 strcpy(di->label.name, AudioNdac);
711 di->type = AUDIO_MIXER_VALUE;
712 di->next = di->prev = AUDIO_MIXER_LAST;
713 di->un.v.num_channels = 1;
714 strcpy(di->un.v.units.name, AudioNvolume);
715 return 0;
716 }
717
718 return ENXIO;
719 }
720
721 static int
722 pad_get_props(void *opaque)
723 {
724
725 return AUDIO_PROP_PLAYBACK;
726 }
727
728 static void
729 pad_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
730 {
731 struct pad_softc *sc = opaque;
732
733 *intr = &sc->sc_intr_lock;
734 *thread = &sc->sc_lock;
735 }
736
737 static void
738 pad_swvol_codec(audio_filter_arg_t *arg)
739 {
740 struct pad_softc *sc = arg->context;
741 const aint_t *src;
742 aint_t *dst;
743 u_int sample_count;
744 u_int i;
745
746 src = arg->src;
747 dst = arg->dst;
748 sample_count = arg->count * arg->srcfmt->channels;
749 for (i = 0; i < sample_count; i++) {
750 aint2_t v = (aint2_t)(*src++);
751 v = v * sc->sc_swvol / 255;
752 *dst++ = (aint_t)v;
753 }
754 }
755
756 MODULE(MODULE_CLASS_DRIVER, pad, "audio");
757
758 #ifdef _MODULE
759
760 #include "ioconf.c"
761
762 devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
763
764 /*
765 * We need our own version of cfattach since config(1)'s ioconf does not
766 * generate what we need
767 */
768
769 static struct cfattach *pad_cfattachinit[] = { &pad_ca, NULL };
770
771 static struct cfattachinit pad_cfattach[] = {
772 { "pad", pad_cfattachinit },
773 { NULL, NULL }
774 };
775 #endif
776
777 static int
778 pad_modcmd(modcmd_t cmd, void *arg)
779 {
780 int error = 0;
781
782 switch (cmd) {
783 case MODULE_CMD_INIT:
784 #ifdef _MODULE
785 pad_cfattach[1] = cfattach_ioconf_pad[0];
786 error = config_init_component(cfdriver_ioconf_pad,
787 pad_cfattach, cfdata_ioconf_pad);
788 if (error)
789 break;
790
791 error = devsw_attach(pad_cd.cd_name, NULL, &bmajor,
792 &pad_cdevsw, &cmajor);
793 if (error) {
794 config_fini_component(cfdriver_ioconf_pad,
795 pad_cfattach, cfdata_ioconf_pad);
796 break;
797 }
798 mutex_init(&padconfig, MUTEX_DEFAULT, IPL_NONE);
799
800 #endif
801 break;
802
803 case MODULE_CMD_FINI:
804 #ifdef _MODULE
805 error = devsw_detach(NULL, &pad_cdevsw);
806 if (error)
807 break;
808
809 error = config_fini_component(cfdriver_ioconf_pad,
810 pad_cfattach, cfdata_ioconf_pad);
811 if (error) {
812 devsw_attach(pad_cd.cd_name, NULL, &bmajor,
813 &pad_cdevsw, &cmajor);
814 break;
815 }
816 mutex_destroy(&padconfig);
817 #endif
818 break;
819
820 default:
821 error = ENOTTY;
822 }
823
824 return error;
825 }
826