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