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