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