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