1 /*-
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <dev/sound/pcm/sound.h>
29 #include <dev/sound/pcm/vchan.h>
30 #include <dev/sound/pcm/dsp.h>
31 #include <sys/sysctl.h>
32
33 #include "feeder_if.h"
34
35 SND_DECLARE_FILE("$FreeBSD: releng/6.0/sys/dev/sound/pcm/sound.c 151715 2005-10-26 21:18:09Z ariff $");
36
37 devclass_t pcm_devclass;
38
39 int pcm_veto_load = 1;
40
41 #ifdef USING_DEVFS
42 int snd_unit = 0;
43 TUNABLE_INT("hw.snd.unit", &snd_unit);
44 #endif
45
46 int snd_maxautovchans = 0;
47 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48
49 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
50
51 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
52
53 struct sysctl_ctx_list *
54 snd_sysctl_tree(device_t dev)
55 {
56 struct snddev_info *d = device_get_softc(dev);
57
58 return &d->sysctl_tree;
59 }
60
61 struct sysctl_oid *
62 snd_sysctl_tree_top(device_t dev)
63 {
64 struct snddev_info *d = device_get_softc(dev);
65
66 return d->sysctl_tree_top;
67 }
68
69 void *
70 snd_mtxcreate(const char *desc, const char *type)
71 {
72 #ifdef USING_MUTEX
73 struct mtx *m;
74
75 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
76 if (m == NULL)
77 return NULL;
78 mtx_init(m, desc, type, MTX_DEF);
79 return m;
80 #else
81 return (void *)0xcafebabe;
82 #endif
83 }
84
85 void
86 snd_mtxfree(void *m)
87 {
88 #ifdef USING_MUTEX
89 struct mtx *mtx = m;
90
91 /* mtx_assert(mtx, MA_OWNED); */
92 mtx_destroy(mtx);
93 free(mtx, M_DEVBUF);
94 #endif
95 }
96
97 void
98 snd_mtxassert(void *m)
99 {
100 #ifdef USING_MUTEX
101 #ifdef INVARIANTS
102 struct mtx *mtx = m;
103
104 mtx_assert(mtx, MA_OWNED);
105 #endif
106 #endif
107 }
108 /*
109 void
110 snd_mtxlock(void *m)
111 {
112 #ifdef USING_MUTEX
113 struct mtx *mtx = m;
114
115 mtx_lock(mtx);
116 #endif
117 }
118
119 void
120 snd_mtxunlock(void *m)
121 {
122 #ifdef USING_MUTEX
123 struct mtx *mtx = m;
124
125 mtx_unlock(mtx);
126 #endif
127 }
128 */
129 int
130 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
131 {
132 #ifdef USING_MUTEX
133 flags &= INTR_MPSAFE;
134 flags |= INTR_TYPE_AV;
135 #else
136 flags = INTR_TYPE_AV;
137 #endif
138 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
139 }
140
141 #ifndef PCM_DEBUG_MTX
142 void
143 pcm_lock(struct snddev_info *d)
144 {
145 snd_mtxlock(d->lock);
146 }
147
148 void
149 pcm_unlock(struct snddev_info *d)
150 {
151 snd_mtxunlock(d->lock);
152 }
153 #endif
154
155 struct pcm_channel *
156 pcm_getfakechan(struct snddev_info *d)
157 {
158 return d->fakechan;
159 }
160
161 /* return a locked channel */
162 struct pcm_channel *
163 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
164 {
165 struct pcm_channel *c;
166 struct snddev_channel *sce;
167 int err;
168
169 snd_mtxassert(d->lock);
170
171 /* scan for a free channel */
172 SLIST_FOREACH(sce, &d->channels, link) {
173 c = sce->channel;
174 CHN_LOCK(c);
175 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
176 if (chnum == -1 || c->num == chnum) {
177 c->flags |= CHN_F_BUSY;
178 c->pid = pid;
179 return c;
180 }
181 }
182 CHN_UNLOCK(c);
183 }
184
185 /* no channel available */
186 if (direction == PCMDIR_PLAY) {
187 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
188 /* try to create a vchan */
189 SLIST_FOREACH(sce, &d->channels, link) {
190 c = sce->channel;
191 CHN_LOCK(c);
192 if (!SLIST_EMPTY(&c->children)) {
193 err = vchan_create(c);
194 CHN_UNLOCK(c);
195 if (!err)
196 return pcm_chnalloc(d, direction, pid, -1);
197 else
198 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
199 } else
200 CHN_UNLOCK(c);
201 }
202 }
203 }
204
205 return NULL;
206 }
207
208 /* release a locked channel and unlock it */
209 int
210 pcm_chnrelease(struct pcm_channel *c)
211 {
212 CHN_LOCKASSERT(c);
213 c->flags &= ~CHN_F_BUSY;
214 c->pid = -1;
215 CHN_UNLOCK(c);
216 return 0;
217 }
218
219 int
220 pcm_chnref(struct pcm_channel *c, int ref)
221 {
222 int r;
223
224 CHN_LOCKASSERT(c);
225 c->refcount += ref;
226 r = c->refcount;
227 return r;
228 }
229
230 int
231 pcm_inprog(struct snddev_info *d, int delta)
232 {
233 int r;
234
235 if (delta == 0)
236 return d->inprog;
237
238 /* backtrace(); */
239 pcm_lock(d);
240 d->inprog += delta;
241 r = d->inprog;
242 pcm_unlock(d);
243 return r;
244 }
245
246 static void
247 pcm_setmaxautovchans(struct snddev_info *d, int num)
248 {
249 struct pcm_channel *c;
250 struct snddev_channel *sce;
251 int err, done;
252
253 if (num > 0 && d->vchancount == 0) {
254 SLIST_FOREACH(sce, &d->channels, link) {
255 c = sce->channel;
256 CHN_LOCK(c);
257 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
258 c->flags |= CHN_F_BUSY;
259 err = vchan_create(c);
260 if (err) {
261 c->flags &= ~CHN_F_BUSY;
262 CHN_UNLOCK(c);
263 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
264 } else
265 CHN_UNLOCK(c);
266 return;
267 }
268 CHN_UNLOCK(c);
269 }
270 }
271 if (num == 0 && d->vchancount > 0) {
272 done = 0;
273 while (!done) {
274 done = 1;
275 SLIST_FOREACH(sce, &d->channels, link) {
276 c = sce->channel;
277 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
278 done = 0;
279 snd_mtxlock(d->lock);
280 err = vchan_destroy(c);
281 snd_mtxunlock(d->lock);
282 if (err)
283 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
284 break; /* restart */
285 }
286 }
287 }
288 }
289 }
290
291 #ifdef USING_DEVFS
292 static int
293 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
294 {
295 struct snddev_info *d;
296 int error, unit;
297
298 unit = snd_unit;
299 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
300 if (error == 0 && req->newptr != NULL) {
301 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
302 return EINVAL;
303 d = devclass_get_softc(pcm_devclass, unit);
304 if (d == NULL || SLIST_EMPTY(&d->channels))
305 return EINVAL;
306 snd_unit = unit;
307 }
308 return (error);
309 }
310 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
311 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
312 #endif
313
314 static int
315 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
316 {
317 struct snddev_info *d;
318 int i, v, error;
319
320 v = snd_maxautovchans;
321 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
322 if (error == 0 && req->newptr != NULL) {
323 if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL)
324 return EINVAL;
325 if (v != snd_maxautovchans) {
326 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
327 d = devclass_get_softc(pcm_devclass, i);
328 if (!d)
329 continue;
330 pcm_setmaxautovchans(d, v);
331 }
332 }
333 snd_maxautovchans = v;
334 }
335 return (error);
336 }
337 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
338 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
339
340 struct pcm_channel *
341 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
342 {
343 struct pcm_channel *ch;
344 char *dirs;
345 int direction, err, *pnum;
346
347 switch(dir) {
348 case PCMDIR_PLAY:
349 dirs = "play";
350 direction = PCMDIR_PLAY;
351 pnum = &d->playcount;
352 break;
353
354 case PCMDIR_REC:
355 dirs = "record";
356 direction = PCMDIR_REC;
357 pnum = &d->reccount;
358 break;
359
360 case PCMDIR_VIRTUAL:
361 dirs = "virtual";
362 direction = PCMDIR_PLAY;
363 pnum = &d->vchancount;
364 break;
365
366 default:
367 return NULL;
368 }
369
370 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
371 if (!ch)
372 return NULL;
373
374 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
375 if (!ch->methods) {
376 free(ch, M_DEVBUF);
377
378 return NULL;
379 }
380
381 snd_mtxlock(d->lock);
382 ch->num = (*pnum)++;
383 snd_mtxunlock(d->lock);
384
385 ch->pid = -1;
386 ch->parentsnddev = d;
387 ch->parentchannel = parent;
388 ch->dev = d->dev;
389 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
390
391 err = chn_init(ch, devinfo, dir, direction);
392 if (err) {
393 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
394 kobj_delete(ch->methods, M_DEVBUF);
395 free(ch, M_DEVBUF);
396 snd_mtxlock(d->lock);
397 (*pnum)--;
398 snd_mtxunlock(d->lock);
399
400 return NULL;
401 }
402
403 return ch;
404 }
405
406 int
407 pcm_chn_destroy(struct pcm_channel *ch)
408 {
409 struct snddev_info *d;
410 int err;
411
412 d = ch->parentsnddev;
413 err = chn_kill(ch);
414 if (err) {
415 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
416 return err;
417 }
418
419 kobj_delete(ch->methods, M_DEVBUF);
420 free(ch, M_DEVBUF);
421
422 return 0;
423 }
424
425 int
426 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
427 {
428 struct snddev_channel *sce, *tmp, *after;
429 int device = device_get_unit(d->dev);
430
431 /*
432 * Note it's confusing nomenclature.
433 * dev_t
434 * device -> pcm_device
435 * unit -> pcm_channel
436 * channel -> snddev_channel
437 * device_t
438 * unit -> pcm_device
439 */
440
441 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
442 if (!sce) {
443 return ENOMEM;
444 }
445
446 snd_mtxlock(d->lock);
447 sce->channel = ch;
448 sce->chan_num= d->devcount++;
449 if (SLIST_EMPTY(&d->channels)) {
450 SLIST_INSERT_HEAD(&d->channels, sce, link);
451 } else {
452 after = NULL;
453 SLIST_FOREACH(tmp, &d->channels, link) {
454 after = tmp;
455 }
456 SLIST_INSERT_AFTER(after, sce, link);
457 }
458 snd_mtxunlock(d->lock);
459 sce->dsp_devt= make_dev(&dsp_cdevsw,
460 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
461 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
462 device, sce->chan_num);
463
464 sce->dspW_devt= make_dev(&dsp_cdevsw,
465 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
466 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
467 device, sce->chan_num);
468
469 sce->audio_devt= make_dev(&dsp_cdevsw,
470 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
471 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
472 device, sce->chan_num);
473
474 if (ch->direction == PCMDIR_REC)
475 sce->dspr_devt = make_dev(&dsp_cdevsw,
476 PCMMKMINOR(device, SND_DEV_DSPREC,
477 sce->chan_num), UID_ROOT, GID_WHEEL,
478 0666, "dspr%d.%d", device, sce->chan_num);
479
480 return 0;
481 }
482
483 int
484 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
485 {
486 struct snddev_channel *sce;
487 #if 0
488 int ourlock;
489
490 ourlock = 0;
491 if (!mtx_owned(d->lock)) {
492 snd_mtxlock(d->lock);
493 ourlock = 1;
494 }
495 #endif
496
497 SLIST_FOREACH(sce, &d->channels, link) {
498 if (sce->channel == ch)
499 goto gotit;
500 }
501 #if 0
502 if (ourlock)
503 snd_mtxunlock(d->lock);
504 #endif
505 return EINVAL;
506 gotit:
507 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
508
509 if (ch->direction == PCMDIR_REC)
510 d->reccount--;
511 else if (ch->flags & CHN_F_VIRTUAL)
512 d->vchancount--;
513 else
514 d->playcount--;
515
516 #if 0
517 if (ourlock)
518 snd_mtxunlock(d->lock);
519 #endif
520 free(sce, M_DEVBUF);
521
522 return 0;
523 }
524
525 int
526 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
527 {
528 struct snddev_info *d = device_get_softc(dev);
529 struct pcm_channel *ch;
530 int err;
531
532 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
533 if (!ch) {
534 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
535 return ENODEV;
536 }
537
538 err = pcm_chn_add(d, ch);
539 if (err) {
540 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
541 pcm_chn_destroy(ch);
542 return err;
543 }
544
545 CHN_LOCK(ch);
546 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
547 ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
548 ch->flags |= CHN_F_BUSY;
549 err = vchan_create(ch);
550 if (err) {
551 ch->flags &= ~CHN_F_BUSY;
552 CHN_UNLOCK(ch);
553 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
554 return err;
555 }
556 }
557 CHN_UNLOCK(ch);
558
559 return err;
560 }
561
562 static int
563 pcm_killchan(device_t dev)
564 {
565 struct snddev_info *d = device_get_softc(dev);
566 struct snddev_channel *sce;
567 struct pcm_channel *ch;
568 int error = 0;
569
570 sce = SLIST_FIRST(&d->channels);
571 ch = sce->channel;
572
573 error = pcm_chn_remove(d, sce->channel);
574 if (error)
575 return (error);
576 return (pcm_chn_destroy(ch));
577 }
578
579 int
580 pcm_setstatus(device_t dev, char *str)
581 {
582 struct snddev_info *d = device_get_softc(dev);
583
584 snd_mtxlock(d->lock);
585 strncpy(d->status, str, SND_STATUSLEN);
586 snd_mtxunlock(d->lock);
587 return 0;
588 }
589
590 u_int32_t
591 pcm_getflags(device_t dev)
592 {
593 struct snddev_info *d = device_get_softc(dev);
594
595 return d->flags;
596 }
597
598 void
599 pcm_setflags(device_t dev, u_int32_t val)
600 {
601 struct snddev_info *d = device_get_softc(dev);
602
603 d->flags = val;
604 }
605
606 void *
607 pcm_getdevinfo(device_t dev)
608 {
609 struct snddev_info *d = device_get_softc(dev);
610
611 return d->devinfo;
612 }
613
614 unsigned int
615 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
616 {
617 struct snddev_info *d = device_get_softc(dev);
618 int sz, x;
619
620 sz = 0;
621 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
622 x = sz;
623 RANGE(sz, min, max);
624 if (x != sz)
625 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
626 x = min;
627 while (x < sz)
628 x <<= 1;
629 if (x > sz)
630 x >>= 1;
631 if (x != sz) {
632 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
633 sz = x;
634 }
635 } else {
636 sz = deflt;
637 }
638
639 d->bufsz = sz;
640
641 return sz;
642 }
643
644 int
645 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
646 {
647 struct snddev_info *d = device_get_softc(dev);
648
649 if (pcm_veto_load) {
650 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
651
652 return EINVAL;
653 }
654
655 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
656
657 d->flags = 0;
658 d->dev = dev;
659 d->devinfo = devinfo;
660 d->devcount = 0;
661 d->reccount = 0;
662 d->playcount = 0;
663 d->vchancount = 0;
664 d->inprog = 0;
665
666 SLIST_INIT(&d->channels);
667 SLIST_INIT(&d->channels);
668
669 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
670 d->flags |= SD_F_SIMPLEX;
671
672 d->fakechan = fkchan_setup(dev);
673 chn_init(d->fakechan, NULL, 0, 0);
674
675 #ifdef SND_DYNSYSCTL
676 sysctl_ctx_init(&d->sysctl_tree);
677 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
678 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
679 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
680 if (d->sysctl_tree_top == NULL) {
681 sysctl_ctx_free(&d->sysctl_tree);
682 goto no;
683 }
684 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
685 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
686 #endif
687 if (numplay > 0)
688 vchan_initsys(dev);
689 if (numplay == 1)
690 d->flags |= SD_F_AUTOVCHAN;
691
692 sndstat_register(dev, d->status, sndstat_prepare_pcm);
693 return 0;
694 no:
695 snd_mtxfree(d->lock);
696 return ENXIO;
697 }
698
699 int
700 pcm_unregister(device_t dev)
701 {
702 struct snddev_info *d = device_get_softc(dev);
703 struct snddev_channel *sce;
704 struct pcm_channel *ch;
705
706 snd_mtxlock(d->lock);
707 if (d->inprog) {
708 device_printf(dev, "unregister: operation in progress\n");
709 snd_mtxunlock(d->lock);
710 return EBUSY;
711 }
712 if (sndstat_busy() != 0) {
713 device_printf(dev, "unregister: sndstat busy\n");
714 snd_mtxunlock(d->lock);
715 return EBUSY;
716 }
717
718
719 SLIST_FOREACH(sce, &d->channels, link) {
720 ch = sce->channel;
721 if (ch->refcount > 0) {
722 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
723 snd_mtxunlock(d->lock);
724 return EBUSY;
725 }
726 }
727
728 if (mixer_uninit(dev)) {
729 device_printf(dev, "unregister: mixer busy\n");
730 snd_mtxunlock(d->lock);
731 return EBUSY;
732 }
733
734 SLIST_FOREACH(sce, &d->channels, link) {
735 if (sce->dsp_devt)
736 destroy_dev(sce->dsp_devt);
737 if (sce->dspW_devt)
738 destroy_dev(sce->dspW_devt);
739 if (sce->audio_devt)
740 destroy_dev(sce->audio_devt);
741 if (sce->dspr_devt)
742 destroy_dev(sce->dspr_devt);
743 }
744
745 #ifdef SND_DYNSYSCTL
746 d->sysctl_tree_top = NULL;
747 sysctl_ctx_free(&d->sysctl_tree);
748 #endif
749 while (!SLIST_EMPTY(&d->channels))
750 pcm_killchan(dev);
751
752 chn_kill(d->fakechan);
753 fkchan_kill(d->fakechan);
754
755 sndstat_unregister(dev);
756 snd_mtxunlock(d->lock);
757 snd_mtxfree(d->lock);
758 return 0;
759 }
760
761 /************************************************************************/
762
763 static int
764 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
765 {
766 struct snddev_info *d;
767 struct snddev_channel *sce;
768 struct pcm_channel *c;
769 struct pcm_feeder *f;
770 int pc, rc, vc;
771
772 if (verbose < 1)
773 return 0;
774
775 d = device_get_softc(dev);
776 if (!d)
777 return ENXIO;
778
779 snd_mtxlock(d->lock);
780 if (!SLIST_EMPTY(&d->channels)) {
781 pc = rc = vc = 0;
782 SLIST_FOREACH(sce, &d->channels, link) {
783 c = sce->channel;
784 if (c->direction == PCMDIR_PLAY) {
785 if (c->flags & CHN_F_VIRTUAL)
786 vc++;
787 else
788 pc++;
789 } else
790 rc++;
791 }
792 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
793 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
794 #ifdef USING_DEVFS
795 (device_get_unit(dev) == snd_unit)? " default" : ""
796 #else
797 ""
798 #endif
799 );
800
801 if (verbose <= 1) {
802 snd_mtxunlock(d->lock);
803 return 0;
804 }
805
806 SLIST_FOREACH(sce, &d->channels, link) {
807 c = sce->channel;
808 sbuf_printf(s, "\n\t");
809
810 /* it would be better to indent child channels */
811 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
812 sbuf_printf(s, "spd %d", c->speed);
813 if (c->speed != sndbuf_getspd(c->bufhard))
814 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
815 sbuf_printf(s, ", fmt 0x%08x", c->format);
816 if (c->format != sndbuf_getfmt(c->bufhard))
817 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
818 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
819 if (c->pid != -1)
820 sbuf_printf(s, ", pid %d", c->pid);
821 sbuf_printf(s, "\n\t");
822
823 if (c->bufhard != NULL && c->bufsoft != NULL) {
824 sbuf_printf(s, "interrupts %d, ", c->interrupts);
825 if (c->direction == PCMDIR_REC)
826 sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
827 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
828 else
829 sbuf_printf(s, "underruns %d, ready %d",
830 c->xruns, sndbuf_getready(c->bufsoft));
831 sbuf_printf(s, "\n\t");
832 }
833
834 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
835 sbuf_printf(s, " -> ");
836 f = c->feeder;
837 while (f->source != NULL)
838 f = f->source;
839 while (f != NULL) {
840 sbuf_printf(s, "%s", f->class->name);
841 if (f->desc->type == FEEDER_FMT)
842 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
843 if (f->desc->type == FEEDER_RATE)
844 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
845 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
846 sbuf_printf(s, "(0x%08x)", f->desc->out);
847 sbuf_printf(s, " -> ");
848 f = f->parent;
849 }
850 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
851 }
852 } else
853 sbuf_printf(s, " (mixer only)");
854 snd_mtxunlock(d->lock);
855
856 return 0;
857 }
858
859 /************************************************************************/
860
861 #ifdef SND_DYNSYSCTL
862 int
863 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
864 {
865 struct snddev_info *d;
866 struct snddev_channel *sce;
867 struct pcm_channel *c;
868 int err, newcnt, cnt, busy;
869 int x;
870
871 d = oidp->oid_arg1;
872
873 x = pcm_inprog(d, 1);
874 if (x != 1) {
875 pcm_inprog(d, -1);
876 return EINPROGRESS;
877 }
878
879 busy = 0;
880 cnt = 0;
881 SLIST_FOREACH(sce, &d->channels, link) {
882 c = sce->channel;
883 CHN_LOCK(c);
884 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
885 cnt++;
886 if (c->flags & CHN_F_BUSY)
887 busy++;
888 }
889 CHN_UNLOCK(c);
890 }
891
892 newcnt = cnt;
893
894 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
895
896 if (err == 0 && req->newptr != NULL) {
897
898 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
899 pcm_inprog(d, -1);
900 return E2BIG;
901 }
902
903 if (newcnt > cnt) {
904 /* add new vchans - find a parent channel first */
905 SLIST_FOREACH(sce, &d->channels, link) {
906 c = sce->channel;
907 CHN_LOCK(c);
908 /* not a candidate if not a play channel */
909 if (c->direction != PCMDIR_PLAY)
910 goto next;
911 /* not a candidate if a virtual channel */
912 if (c->flags & CHN_F_VIRTUAL)
913 goto next;
914 /* not a candidate if it's in use */
915 if (!(c->flags & CHN_F_BUSY) ||
916 !(SLIST_EMPTY(&c->children)))
917 /*
918 * if we get here we're a nonvirtual
919 * play channel, and either
920 * 1) not busy
921 * 2) busy with children, not directly
922 * open
923 *
924 * thus we can add children
925 */
926 goto addok;
927 next:
928 CHN_UNLOCK(c);
929 }
930 pcm_inprog(d, -1);
931 return EBUSY;
932 addok:
933 c->flags |= CHN_F_BUSY;
934 while (err == 0 && newcnt > cnt) {
935 err = vchan_create(c);
936 if (err == 0)
937 cnt++;
938 }
939 if (SLIST_EMPTY(&c->children))
940 c->flags &= ~CHN_F_BUSY;
941 CHN_UNLOCK(c);
942 } else if (newcnt < cnt) {
943 if (busy > newcnt) {
944 printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
945 pcm_inprog(d, -1);
946 return EBUSY;
947 }
948
949 snd_mtxlock(d->lock);
950 while (err == 0 && newcnt < cnt) {
951 SLIST_FOREACH(sce, &d->channels, link) {
952 c = sce->channel;
953 CHN_LOCK(c);
954 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
955 goto remok;
956
957 CHN_UNLOCK(c);
958 }
959 snd_mtxunlock(d->lock);
960 pcm_inprog(d, -1);
961 return EINVAL;
962 remok:
963 CHN_UNLOCK(c);
964 err = vchan_destroy(c);
965 if (err == 0)
966 cnt--;
967 }
968 snd_mtxunlock(d->lock);
969 }
970 }
971 pcm_inprog(d, -1);
972 return err;
973 }
974 #endif
975
976 /************************************************************************/
977
978 static int
979 sound_modevent(module_t mod, int type, void *data)
980 {
981 #if 0
982 return (midi_modevent(mod, type, data));
983 #else
984 return 0;
985 #endif
986 }
987
988 DEV_MODULE(sound, sound_modevent, NULL);
989 MODULE_VERSION(sound, SOUND_MODVER);
Cache object: 7a65cd4e6da61e38945c7e412c1bfba7
|