xref: /freebsd/sys/dev/sound/pcm/sound.c (revision c98323078dede7579020518ec84cdcb478e5c142)
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);
987