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