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