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