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