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