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