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