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