xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 3d11b6c8f01e1fca5936a11d6996448467851a94)
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 static int
162 pcm_setvchans(struct snddev_info *d, int newcnt)
163 {
164 	struct snddev_channel *sce = NULL;
165 	struct pcm_channel *c = NULL;
166 	int err = 0, vcnt, dcnt, i;
167 
168 	pcm_inprog(d, 1);
169 
170 	if (!(d->flags & SD_F_AUTOVCHAN)) {
171 		err = EINVAL;
172 		goto setvchans_out;
173 	}
174 
175 	vcnt = d->vchancount;
176 	dcnt = d->playcount + d->reccount;
177 
178 	if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
179 		err = E2BIG;
180 		goto setvchans_out;
181 	}
182 
183 	dcnt += vcnt;
184 
185 	if (newcnt > vcnt) {
186 		/* add new vchans - find a parent channel first */
187 		SLIST_FOREACH(sce, &d->channels, link) {
188 			c = sce->channel;
189 			CHN_LOCK(c);
190 			if (c->direction == PCMDIR_PLAY &&
191 					((c->flags & CHN_F_HAS_VCHAN) ||
192 					(vcnt == 0 &&
193 					!(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
194 				goto addok;
195 			CHN_UNLOCK(c);
196 		}
197 		err = EBUSY;
198 		goto setvchans_out;
199 addok:
200 		c->flags |= CHN_F_BUSY;
201 		while (err == 0 && newcnt > vcnt) {
202 			if (dcnt > PCMMAXCHAN) {
203 				device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
204 				break;
205 			}
206 			err = vchan_create(c);
207 			if (err == 0) {
208 				vcnt++;
209 				dcnt++;
210 			} else if (err == E2BIG && newcnt > vcnt)
211 				device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
212 		}
213 		if (vcnt == 0)
214 			c->flags &= ~CHN_F_BUSY;
215 		CHN_UNLOCK(c);
216 	} else if (newcnt < vcnt) {
217 #define ORPHAN_CDEVT(cdevt) \
218 	((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
219 	(cdevt)->si_drv2 == NULL))
220 		while (err == 0 && newcnt < vcnt) {
221 			i = 0;
222 			SLIST_FOREACH(sce, &d->channels, link) {
223 				c = sce->channel;
224 				CHN_LOCK(c);
225 				if (c->direction == PCMDIR_PLAY &&
226 						(c->flags & CHN_F_VIRTUAL) &&
227 						(i++ == newcnt)) {
228 					if (!(c->flags & CHN_F_BUSY) &&
229 							ORPHAN_CDEVT(sce->dsp_devt) &&
230 							ORPHAN_CDEVT(sce->dspW_devt) &&
231 							ORPHAN_CDEVT(sce->audio_devt) &&
232 							ORPHAN_CDEVT(sce->dspr_devt))
233 						goto remok;
234 					/*
235 					 * Either we're busy, or our cdev
236 					 * has been stolen by dsp_clone().
237 					 * Skip, and increase newcnt.
238 					 */
239 					if (!(c->flags & CHN_F_BUSY))
240 						device_printf(d->dev,
241 							"%s: <%s> somebody steal my cdev!\n",
242 							__func__, c->name);
243 					newcnt++;
244 				}
245 				CHN_UNLOCK(c);
246 			}
247 			if (vcnt != newcnt)
248 				err = EBUSY;
249 			break;
250 remok:
251 			CHN_UNLOCK(c);
252 			err = vchan_destroy(c);
253 			if (err == 0)
254 				vcnt--;
255 			else
256 				device_printf(d->dev,
257 					"%s: WARNING: vchan_destroy() failed!",
258 					__func__);
259 		}
260 	}
261 
262 setvchans_out:
263 	pcm_inprog(d, -1);
264 	return err;
265 }
266 
267 /* return error status and a locked channel */
268 int
269 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
270 		pid_t pid, int chnum)
271 {
272 	struct pcm_channel *c;
273 	struct snddev_channel *sce;
274 	int err;
275 
276 retry_chnalloc:
277 	err = ENODEV;
278 	/* scan for a free channel */
279 	SLIST_FOREACH(sce, &d->channels, link) {
280 		c = sce->channel;
281 		CHN_LOCK(c);
282 		if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
283 			if (chnum < 0 || sce->chan_num == chnum) {
284 				c->flags |= CHN_F_BUSY;
285 				c->pid = pid;
286 				*ch = c;
287 				return 0;
288 			}
289 		}
290 		if (sce->chan_num == chnum) {
291 			if (c->direction != direction)
292 				err = EOPNOTSUPP;
293 			else if (c->flags & CHN_F_BUSY)
294 				err = EBUSY;
295 			else
296 				err = EINVAL;
297 			CHN_UNLOCK(c);
298 			return err;
299 		} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
300 			err = EBUSY;
301 		CHN_UNLOCK(c);
302 	}
303 
304 	/* no channel available */
305 	if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
306 			d->vchancount < snd_maxautovchans &&
307 			d->devcount <= PCMMAXCHAN) {
308 		err = pcm_setvchans(d, d->vchancount + 1);
309 		if (err == 0) {
310 			chnum = -2;
311 			goto retry_chnalloc;
312 		}
313 	}
314 
315 	return err;
316 }
317 
318 /* release a locked channel and unlock it */
319 int
320 pcm_chnrelease(struct pcm_channel *c)
321 {
322 	CHN_LOCKASSERT(c);
323 	c->flags &= ~CHN_F_BUSY;
324 	c->pid = -1;
325 	CHN_UNLOCK(c);
326 	return 0;
327 }
328 
329 int
330 pcm_chnref(struct pcm_channel *c, int ref)
331 {
332 	int r;
333 
334 	CHN_LOCKASSERT(c);
335 	c->refcount += ref;
336 	r = c->refcount;
337 	return r;
338 }
339 
340 int
341 pcm_inprog(struct snddev_info *d, int delta)
342 {
343 	int r;
344 
345 	if (delta == 0)
346 		return d->inprog;
347 
348 	/* backtrace(); */
349 	pcm_lock(d);
350 	d->inprog += delta;
351 	r = d->inprog;
352 	pcm_unlock(d);
353 	return r;
354 }
355 
356 static void
357 pcm_setmaxautovchans(struct snddev_info *d, int num)
358 {
359 	if (num > 0 && d->vchancount == 0)
360 		pcm_setvchans(d, 1);
361 	else if (num == 0 && d->vchancount > 0)
362 		pcm_setvchans(d, 0);
363 }
364 
365 #ifdef USING_DEVFS
366 static int
367 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
368 {
369 	struct snddev_info *d;
370 	int error, unit;
371 
372 	unit = snd_unit;
373 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
374 	if (error == 0 && req->newptr != NULL) {
375 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
376 			return EINVAL;
377 		d = devclass_get_softc(pcm_devclass, unit);
378 		if (d == NULL || SLIST_EMPTY(&d->channels))
379 			return EINVAL;
380 		snd_unit = unit;
381 	}
382 	return (error);
383 }
384 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
385             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
386 #endif
387 
388 static int
389 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
390 {
391 	struct snddev_info *d;
392 	int i, v, error;
393 
394 	v = snd_maxautovchans;
395 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
396 	if (error == 0 && req->newptr != NULL) {
397 		if (v < 0 || v > PCMMAXCHAN)
398 			return E2BIG;
399 		if (pcm_devclass != NULL && v != snd_maxautovchans) {
400 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
401 				d = devclass_get_softc(pcm_devclass, i);
402 				if (!d)
403 					continue;
404 				pcm_setmaxautovchans(d, v);
405 			}
406 		}
407 		snd_maxautovchans = v;
408 	}
409 	return (error);
410 }
411 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
412             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
413 
414 struct pcm_channel *
415 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
416 {
417 	struct snddev_channel *sce;
418 	struct pcm_channel *ch, *c;
419 	char *dirs;
420 	uint32_t flsearch = 0;
421 	int direction, err, rpnum, *pnum;
422 
423 	switch(dir) {
424 	case PCMDIR_PLAY:
425 		dirs = "play";
426 		direction = PCMDIR_PLAY;
427 		pnum = &d->playcount;
428 		break;
429 
430 	case PCMDIR_REC:
431 		dirs = "record";
432 		direction = PCMDIR_REC;
433 		pnum = &d->reccount;
434 		break;
435 
436 	case PCMDIR_VIRTUAL:
437 		dirs = "virtual";
438 		direction = PCMDIR_PLAY;
439 		pnum = &d->vchancount;
440 		flsearch = CHN_F_VIRTUAL;
441 		break;
442 
443 	default:
444 		return NULL;
445 	}
446 
447 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
448 	if (!ch)
449 		return NULL;
450 
451 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
452 	if (!ch->methods) {
453 		free(ch, M_DEVBUF);
454 
455 		return NULL;
456 	}
457 
458 	snd_mtxlock(d->lock);
459 	ch->num = 0;
460 	rpnum = 0;
461 	SLIST_FOREACH(sce, &d->channels, link) {
462 		c = sce->channel;
463 		if (direction != c->direction ||
464 				(c->flags & CHN_F_VIRTUAL) != flsearch)
465 			continue;
466 		if (ch->num == c->num)
467 			ch->num++;
468 		else {
469 #if 0
470 			device_printf(d->dev,
471 				"%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
472 				__func__, dirs, ch->num, c->num);
473 #endif
474 			goto retry_num_search;
475 		}
476 		rpnum++;
477 	}
478 	goto retry_num_search_out;
479 retry_num_search:
480 	rpnum = 0;
481 	SLIST_FOREACH(sce, &d->channels, link) {
482 		c = sce->channel;
483 		if (direction != c->direction ||
484 				(c->flags & CHN_F_VIRTUAL) != flsearch)
485 			continue;
486 		if (ch->num == c->num) {
487 			ch->num++;
488 			goto retry_num_search;
489 		}
490 		rpnum++;
491 	}
492 retry_num_search_out:
493 	if (*pnum != rpnum) {
494 		device_printf(d->dev,
495 			"%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
496 			__func__, dirs, *pnum, rpnum);
497 		*pnum = rpnum;
498 	}
499 	(*pnum)++;
500 	snd_mtxunlock(d->lock);
501 
502 	ch->pid = -1;
503 	ch->parentsnddev = d;
504 	ch->parentchannel = parent;
505 	ch->dev = d->dev;
506 	snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
507 
508 	err = chn_init(ch, devinfo, dir, direction);
509 	if (err) {
510 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
511 		kobj_delete(ch->methods, M_DEVBUF);
512 		free(ch, M_DEVBUF);
513 		snd_mtxlock(d->lock);
514 		(*pnum)--;
515 		snd_mtxunlock(d->lock);
516 
517 		return NULL;
518 	}
519 
520 	return ch;
521 }
522 
523 int
524 pcm_chn_destroy(struct pcm_channel *ch)
525 {
526 	struct snddev_info *d;
527 	int err;
528 
529 	d = ch->parentsnddev;
530 	err = chn_kill(ch);
531 	if (err) {
532 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
533 		return err;
534 	}
535 
536 	kobj_delete(ch->methods, M_DEVBUF);
537 	free(ch, M_DEVBUF);
538 
539 	return 0;
540 }
541 
542 int
543 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
544 {
545 	struct snddev_channel *sce, *tmp, *after;
546 	unsigned rdevcount;
547 	int device = device_get_unit(d->dev);
548 	size_t namelen;
549 
550 	/*
551 	 * Note it's confusing nomenclature.
552 	 * dev_t
553 	 * device -> pcm_device
554 	 * unit -> pcm_channel
555 	 * channel -> snddev_channel
556 	 * device_t
557 	 * unit -> pcm_device
558 	 */
559 
560 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
561 	if (!sce) {
562 		return ENOMEM;
563 	}
564 
565 	snd_mtxlock(d->lock);
566 	sce->channel = ch;
567 	sce->chan_num = 0;
568 	rdevcount = 0;
569 	after = NULL;
570 	SLIST_FOREACH(tmp, &d->channels, link) {
571 		if (sce->chan_num == tmp->chan_num)
572 			sce->chan_num++;
573 		else {
574 #if 0
575 			device_printf(d->dev,
576 				"%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
577 				__func__, sce->chan_num, tmp->chan_num);
578 #endif
579 			goto retry_chan_num_search;
580 		}
581 		after = tmp;
582 		rdevcount++;
583 	}
584 	goto retry_chan_num_search_out;
585 retry_chan_num_search:
586 	/*
587 	 * Look for possible channel numbering collision. This may not
588 	 * be optimized, but it will ensure that no collision occured.
589 	 * Can be considered cheap since none of the locking/unlocking
590 	 * operations involved.
591 	 */
592 	rdevcount = 0;
593 	after = NULL;
594 	SLIST_FOREACH(tmp, &d->channels, link) {
595 		if (sce->chan_num == tmp->chan_num) {
596 			sce->chan_num++;
597 			goto retry_chan_num_search;
598 		}
599 		if (sce->chan_num > tmp->chan_num)
600 			after = tmp;
601 		rdevcount++;
602 	}
603 retry_chan_num_search_out:
604 	/*
605 	 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
606 	 */
607 	if (sce->chan_num > PCMMAXCHAN) {
608 		snd_mtxunlock(d->lock);
609 		device_printf(d->dev,
610 			"%s: WARNING: sce->chan_num overflow! (%d)\n",
611 			__func__, sce->chan_num);
612 		free(sce, M_DEVBUF);
613 		return E2BIG;
614 	}
615 	if (d->devcount != rdevcount) {
616 		device_printf(d->dev,
617 			"%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
618 			__func__, d->devcount, rdevcount);
619 		d->devcount = rdevcount;
620 	}
621 	d->devcount++;
622 	if (after == NULL) {
623 		SLIST_INSERT_HEAD(&d->channels, sce, link);
624 	} else {
625 		SLIST_INSERT_AFTER(after, sce, link);
626 	}
627 #if 0
628 	if (1) {
629 		int cnum = 0;
630 		SLIST_FOREACH(tmp, &d->channels, link) {
631 			if (cnum != tmp->chan_num)
632 				device_printf(d->dev,
633 					"%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
634 					__func__, cnum, tmp->chan_num);
635 			cnum++;
636 		}
637 	}
638 #endif
639 
640 	namelen = strlen(ch->name);
641 	if ((CHN_NAMELEN - namelen) > 10) {	/* ":dspXX.YYY" */
642 		snprintf(ch->name + namelen,
643 			CHN_NAMELEN - namelen, ":dsp%d.%d",
644 			device, sce->chan_num);
645 	}
646 	snd_mtxunlock(d->lock);
647 
648 	/*
649 	 * I will revisit these someday, and nuke it mercilessly..
650 	 */
651 	sce->dsp_devt = make_dev(&dsp_cdevsw,
652 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
653 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
654 			device, sce->chan_num);
655 
656 	sce->dspW_devt = make_dev(&dsp_cdevsw,
657 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
658 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
659 			device, sce->chan_num);
660 
661 	sce->audio_devt = make_dev(&dsp_cdevsw,
662 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
663 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
664 			device, sce->chan_num);
665 
666 	if (ch->direction == PCMDIR_REC)
667 		sce->dspr_devt = make_dev(&dsp_cdevsw,
668 				PCMMKMINOR(device, SND_DEV_DSPREC,
669 					sce->chan_num), UID_ROOT, GID_WHEEL,
670 				0666, "dspr%d.%d", device, sce->chan_num);
671 
672 	return 0;
673 }
674 
675 int
676 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
677 {
678 	struct snddev_channel *sce;
679 #if 0
680 	int ourlock;
681 
682 	ourlock = 0;
683 	if (!mtx_owned(d->lock)) {
684 		snd_mtxlock(d->lock);
685 		ourlock = 1;
686 	}
687 #endif
688 
689 	SLIST_FOREACH(sce, &d->channels, link) {
690 		if (sce->channel == ch)
691 			goto gotit;
692 	}
693 #if 0
694 	if (ourlock)
695 		snd_mtxunlock(d->lock);
696 #endif
697 	return EINVAL;
698 gotit:
699 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
700 
701 	if (ch->flags & CHN_F_VIRTUAL)
702 		d->vchancount--;
703 	else if (ch->direction == PCMDIR_REC)
704 		d->reccount--;
705 	else
706 		d->playcount--;
707 
708 #if 0
709 	if (ourlock)
710 		snd_mtxunlock(d->lock);
711 #endif
712 	free(sce, M_DEVBUF);
713 
714 	return 0;
715 }
716 
717 int
718 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
719 {
720 	struct snddev_info *d = device_get_softc(dev);
721 	struct pcm_channel *ch;
722 	int err;
723 
724 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
725 	if (!ch) {
726 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
727 		return ENODEV;
728 	}
729 
730 	err = pcm_chn_add(d, ch);
731 	if (err) {
732 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
733 		pcm_chn_destroy(ch);
734 		return err;
735 	}
736 
737 	return err;
738 }
739 
740 static int
741 pcm_killchan(device_t dev)
742 {
743 	struct snddev_info *d = device_get_softc(dev);
744 	struct snddev_channel *sce;
745 	struct pcm_channel *ch;
746 	int error = 0;
747 
748 	sce = SLIST_FIRST(&d->channels);
749 	ch = sce->channel;
750 
751 	error = pcm_chn_remove(d, sce->channel);
752 	if (error)
753 		return (error);
754 	return (pcm_chn_destroy(ch));
755 }
756 
757 int
758 pcm_setstatus(device_t dev, char *str)
759 {
760 	struct snddev_info *d = device_get_softc(dev);
761 
762 	snd_mtxlock(d->lock);
763 	strncpy(d->status, str, SND_STATUSLEN);
764 	snd_mtxunlock(d->lock);
765 	if (snd_maxautovchans > 0)
766 		pcm_setvchans(d, 1);
767 	return 0;
768 }
769 
770 uint32_t
771 pcm_getflags(device_t dev)
772 {
773 	struct snddev_info *d = device_get_softc(dev);
774 
775 	return d->flags;
776 }
777 
778 void
779 pcm_setflags(device_t dev, uint32_t val)
780 {
781 	struct snddev_info *d = device_get_softc(dev);
782 
783 	d->flags = val;
784 }
785 
786 void *
787 pcm_getdevinfo(device_t dev)
788 {
789 	struct snddev_info *d = device_get_softc(dev);
790 
791 	return d->devinfo;
792 }
793 
794 unsigned int
795 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
796 {
797 	struct snddev_info *d = device_get_softc(dev);
798 	int sz, x;
799 
800 	sz = 0;
801 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
802 		x = sz;
803 		RANGE(sz, min, max);
804 		if (x != sz)
805 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
806 		x = min;
807 		while (x < sz)
808 			x <<= 1;
809 		if (x > sz)
810 			x >>= 1;
811 		if (x != sz) {
812 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
813 			sz = x;
814 		}
815 	} else {
816 		sz = deflt;
817 	}
818 
819 	d->bufsz = sz;
820 
821 	return sz;
822 }
823 
824 int
825 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
826 {
827 	struct snddev_info *d = device_get_softc(dev);
828 
829 	if (pcm_veto_load) {
830 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
831 
832 		return EINVAL;
833 	}
834 
835 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
836 
837 #if 0
838 	/*
839 	 * d->flags should be cleared by the allocator of the softc.
840 	 * We cannot clear this field here because several devices set
841 	 * this flag before calling pcm_register().
842 	 */
843 	d->flags = 0;
844 #endif
845 	d->dev = dev;
846 	d->devinfo = devinfo;
847 	d->devcount = 0;
848 	d->reccount = 0;
849 	d->playcount = 0;
850 	d->vchancount = 0;
851 	d->inprog = 0;
852 
853 	SLIST_INIT(&d->channels);
854 
855 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
856 		d->flags |= SD_F_SIMPLEX;
857 
858 	d->fakechan = fkchan_setup(dev);
859 	chn_init(d->fakechan, NULL, 0, 0);
860 
861 #ifdef SND_DYNSYSCTL
862 	sysctl_ctx_init(&d->sysctl_tree);
863 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
864 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
865 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
866 	if (d->sysctl_tree_top == NULL) {
867 		sysctl_ctx_free(&d->sysctl_tree);
868 		goto no;
869 	}
870 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
871             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
872 #endif
873 	if (numplay > 0) {
874 		d->flags |= SD_F_AUTOVCHAN;
875 		vchan_initsys(dev);
876 	}
877 
878 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
879 	return 0;
880 no:
881 	snd_mtxfree(d->lock);
882 	return ENXIO;
883 }
884 
885 int
886 pcm_unregister(device_t dev)
887 {
888 	struct snddev_info *d = device_get_softc(dev);
889 	struct snddev_channel *sce;
890 	struct pcmchan_children *pce;
891 	struct pcm_channel *ch;
892 
893 	if (sndstat_acquire() != 0) {
894 		device_printf(dev, "unregister: sndstat busy\n");
895 		return EBUSY;
896 	}
897 
898 	snd_mtxlock(d->lock);
899 	if (d->inprog) {
900 		device_printf(dev, "unregister: operation in progress\n");
901 		snd_mtxunlock(d->lock);
902 		sndstat_release();
903 		return EBUSY;
904 	}
905 
906 	SLIST_FOREACH(sce, &d->channels, link) {
907 		ch = sce->channel;
908 		if (ch->refcount > 0) {
909 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
910 			snd_mtxunlock(d->lock);
911 			sndstat_release();
912 			return EBUSY;
913 		}
914 	}
915 
916 	if (mixer_uninit(dev) == EBUSY) {
917 		device_printf(dev, "unregister: mixer busy\n");
918 		snd_mtxunlock(d->lock);
919 		sndstat_release();
920 		return EBUSY;
921 	}
922 
923 	SLIST_FOREACH(sce, &d->channels, link) {
924 		if (sce->dsp_devt) {
925 			destroy_dev(sce->dsp_devt);
926 			sce->dsp_devt = NULL;
927 		}
928 		if (sce->dspW_devt) {
929 			destroy_dev(sce->dspW_devt);
930 			sce->dspW_devt = NULL;
931 		}
932 		if (sce->audio_devt) {
933 			destroy_dev(sce->audio_devt);
934 			sce->audio_devt = NULL;
935 		}
936 		if (sce->dspr_devt) {
937 			destroy_dev(sce->dspr_devt);
938 			sce->dspr_devt = NULL;
939 		}
940 		d->devcount--;
941 		ch = sce->channel;
942 		if (ch == NULL)
943 			continue;
944 		pce = SLIST_FIRST(&ch->children);
945 		while (pce != NULL) {
946 #if 0
947 			device_printf(d->dev, "<%s> removing <%s>\n",
948 				ch->name, (pce->channel != NULL) ?
949 					pce->channel->name : "unknown");
950 #endif
951 			SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
952 			free(pce, M_DEVBUF);
953 			pce = SLIST_FIRST(&ch->children);
954 		}
955 	}
956 
957 #ifdef SND_DYNSYSCTL
958 	d->sysctl_tree_top = NULL;
959 	sysctl_ctx_free(&d->sysctl_tree);
960 #endif
961 
962 #if 0
963 	SLIST_FOREACH(sce, &d->channels, link) {
964 		ch = sce->channel;
965 		if (ch == NULL)
966 			continue;
967 		if (!SLIST_EMPTY(&ch->children))
968 			device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
969 				__func__, ch->name);
970 	}
971 #endif
972 	while (!SLIST_EMPTY(&d->channels))
973 		pcm_killchan(dev);
974 
975 	chn_kill(d->fakechan);
976 	fkchan_kill(d->fakechan);
977 
978 #if 0
979 	device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
980 		"reccount=%u, vchancount=%u\n",
981 		__func__, d->devcount, d->playcount, d->reccount,
982 		d->vchancount);
983 #endif
984 	snd_mtxunlock(d->lock);
985 	snd_mtxfree(d->lock);
986 	sndstat_unregister(dev);
987 	sndstat_release();
988 	return 0;
989 }
990 
991 /************************************************************************/
992 
993 static int
994 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
995 {
996 	struct snddev_info *d;
997 	struct snddev_channel *sce;
998 	struct pcm_channel *c;
999 	struct pcm_feeder *f;
1000 	int pc, rc, vc;
1001 
1002 	if (verbose < 1)
1003 		return 0;
1004 
1005 	d = device_get_softc(dev);
1006 	if (!d)
1007 		return ENXIO;
1008 
1009 	snd_mtxlock(d->lock);
1010 	if (!SLIST_EMPTY(&d->channels)) {
1011 		pc = rc = vc = 0;
1012 		SLIST_FOREACH(sce, &d->channels, link) {
1013 			c = sce->channel;
1014 			if (c->direction == PCMDIR_PLAY) {
1015 				if (c->flags & CHN_F_VIRTUAL)
1016 					vc++;
1017 				else
1018 					pc++;
1019 			} else
1020 				rc++;
1021 		}
1022 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1023 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
1024 #ifdef USING_DEVFS
1025 				(device_get_unit(dev) == snd_unit)? " default" : ""
1026 #else
1027 				""
1028 #endif
1029 				);
1030 
1031 		if (verbose <= 1) {
1032 			snd_mtxunlock(d->lock);
1033 			return 0;
1034 		}
1035 
1036 		SLIST_FOREACH(sce, &d->channels, link) {
1037 			c = sce->channel;
1038 
1039 			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1040 				("hosed pcm channel setup"));
1041 
1042 			sbuf_printf(s, "\n\t");
1043 
1044 			/* it would be better to indent child channels */
1045 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1046 			sbuf_printf(s, "spd %d", c->speed);
1047 			if (c->speed != sndbuf_getspd(c->bufhard))
1048 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1049 			sbuf_printf(s, ", fmt 0x%08x", c->format);
1050 			if (c->format != sndbuf_getfmt(c->bufhard))
1051 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1052 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1053 			if (c->pid != -1)
1054 				sbuf_printf(s, ", pid %d", c->pid);
1055 			sbuf_printf(s, "\n\t");
1056 
1057 			sbuf_printf(s, "interrupts %d, ", c->interrupts);
1058 			if (c->direction == PCMDIR_REC)
1059 				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1060 					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1061 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1062 					sndbuf_getblkcnt(c->bufhard),
1063 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1064 					sndbuf_getblkcnt(c->bufsoft));
1065 			else
1066 				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1067 					c->xruns, sndbuf_getready(c->bufsoft),
1068 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1069 					sndbuf_getblkcnt(c->bufhard),
1070 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1071 					sndbuf_getblkcnt(c->bufsoft));
1072 			sbuf_printf(s, "\n\t");
1073 
1074 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1075 			sbuf_printf(s, " -> ");
1076 			f = c->feeder;
1077 			while (f->source != NULL)
1078 				f = f->source;
1079 			while (f != NULL) {
1080 				sbuf_printf(s, "%s", f->class->name);
1081 				if (f->desc->type == FEEDER_FMT)
1082 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1083 				if (f->desc->type == FEEDER_RATE)
1084 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1085 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1086 						f->desc->type == FEEDER_VOLUME)
1087 					sbuf_printf(s, "(0x%08x)", f->desc->out);
1088 				sbuf_printf(s, " -> ");
1089 				f = f->parent;
1090 			}
1091 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1092 		}
1093 	} else
1094 		sbuf_printf(s, " (mixer only)");
1095 	snd_mtxunlock(d->lock);
1096 
1097 	return 0;
1098 }
1099 
1100 /************************************************************************/
1101 
1102 #ifdef SND_DYNSYSCTL
1103 int
1104 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1105 {
1106 	struct snddev_info *d;
1107 	int err, newcnt;
1108 
1109 	d = oidp->oid_arg1;
1110 
1111 	newcnt = d->vchancount;
1112 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1113 
1114 	if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1115 		err = pcm_setvchans(d, newcnt);
1116 
1117 	return err;
1118 }
1119 #endif
1120 
1121 /************************************************************************/
1122 
1123 static int
1124 sound_modevent(module_t mod, int type, void *data)
1125 {
1126 #if 0
1127 	return (midi_modevent(mod, type, data));
1128 #else
1129 	return 0;
1130 #endif
1131 }
1132 
1133 DEV_MODULE(sound, sound_modevent, NULL);
1134 MODULE_VERSION(sound, SOUND_MODVER);
1135