xref: /dragonfly/sys/dev/sound/pcm/sound.c (revision 3f5e28f4)
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  * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.3 2006/04/04 17:43:48 ariff Exp $
28  * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $
29  */
30 
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include <dev/sound/pcm/dsp.h>
34 #include <sys/sysctl.h>
35 
36 #include "feeder_if.h"
37 
38 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $");
39 
40 devclass_t pcm_devclass;
41 
42 int pcm_veto_load = 1;
43 
44 #ifdef USING_DEVFS
45 int snd_unit = 0;
46 TUNABLE_INT("hw.snd.unit", &snd_unit);
47 #endif
48 
49 int snd_maxautovchans = 0;
50 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
51 
52 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
53 
54 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
55 
56 struct sysctl_ctx_list *
57 snd_sysctl_tree(device_t dev)
58 {
59 	struct snddev_info *d = device_get_softc(dev);
60 
61 	return &d->sysctl_tree;
62 }
63 
64 struct sysctl_oid *
65 snd_sysctl_tree_top(device_t dev)
66 {
67 	struct snddev_info *d = device_get_softc(dev);
68 
69 	return d->sysctl_tree_top;
70 }
71 
72 void *
73 snd_mtxcreate(const char *desc, const char *type)
74 {
75 #ifdef USING_MUTEX
76 	struct spinlock *m;
77 
78 	m = kmalloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
79 	if (m == NULL)
80 		return NULL;
81 	spin_init(m);
82 	return m;
83 #else
84 	return (void *)0xcafebabe;
85 #endif
86 }
87 
88 void
89 snd_mtxfree(void *m)
90 {
91 #ifdef USING_MUTEX
92 	struct spinlock *mtx = m;
93 
94 	/* mtx_assert(mtx, MA_OWNED); */
95 	spin_uninit(mtx);
96 	kfree(mtx, M_DEVBUF);
97 #endif
98 }
99 
100 void
101 snd_mtxassert(void *m)
102 {
103 #ifdef USING_MUTEX
104 #ifdef INVARIANTS
105 	/* XXX can't assert spinlocks */
106 #endif
107 #endif
108 }
109 /*
110 void
111 snd_mtxlock(void *m)
112 {
113 #ifdef USING_MUTEX
114 	struct spinlock *mtx = m;
115 
116 	spin_lock_wr(mtx);
117 #endif
118 }
119 
120 void
121 snd_mtxunlock(void *m)
122 {
123 #ifdef USING_MUTEX
124 	struct spinlock *mtx = m;
125 
126 	spin_unlock_wr(mtx);
127 #endif
128 }
129 */
130 int
131 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
132 {
133 #ifdef USING_MUTEX
134 	flags &= INTR_MPSAFE;
135 	flags |= INTR_TYPE_AV;
136 #else
137 	flags = INTR_TYPE_AV;
138 #endif
139 	return bus_setup_intr(dev, res, flags, hand, param, cookiep, NULL);
140 }
141 
142 #ifndef	PCM_DEBUG_MTX
143 void
144 pcm_lock(struct snddev_info *d)
145 {
146 	snd_mtxlock(d->lock);
147 }
148 
149 void
150 pcm_unlock(struct snddev_info *d)
151 {
152 	snd_mtxunlock(d->lock);
153 }
154 #endif
155 
156 struct pcm_channel *
157 pcm_getfakechan(struct snddev_info *d)
158 {
159 	return d->fakechan;
160 }
161 
162 static int
163 pcm_setvchans(struct snddev_info *d, int newcnt)
164 {
165 	struct snddev_channel *sce = NULL;
166 	struct pcm_channel *c = NULL;
167 	int err = 0, vcnt, dcnt, i;
168 
169 	pcm_inprog(d, 1);
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->dspr_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_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 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
386             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
387 #endif
388 
389 static int
390 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
391 {
392 	struct snddev_info *d;
393 	int i, v, error;
394 
395 	v = snd_maxautovchans;
396 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
397 	if (error == 0 && req->newptr != NULL) {
398 		if (v < 0 || v > PCMMAXCHAN)
399 			return E2BIG;
400 		if (pcm_devclass != NULL && v != snd_maxautovchans) {
401 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
402 				d = devclass_get_softc(pcm_devclass, i);
403 				if (!d)
404 					continue;
405 				pcm_setmaxautovchans(d, v);
406 			}
407 		}
408 		snd_maxautovchans = v;
409 	}
410 	return (error);
411 }
412 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
413             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
414 
415 struct pcm_channel *
416 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
417 {
418 	struct snddev_channel *sce;
419 	struct pcm_channel *ch, *c;
420 	char *dirs;
421 	uint32_t flsearch = 0;
422 	int direction, err, rpnum, *pnum;
423 
424 	switch(dir) {
425 	case PCMDIR_PLAY:
426 		dirs = "play";
427 		direction = PCMDIR_PLAY;
428 		pnum = &d->playcount;
429 		break;
430 
431 	case PCMDIR_REC:
432 		dirs = "record";
433 		direction = PCMDIR_REC;
434 		pnum = &d->reccount;
435 		break;
436 
437 	case PCMDIR_VIRTUAL:
438 		dirs = "virtual";
439 		direction = PCMDIR_PLAY;
440 		pnum = &d->vchancount;
441 		flsearch = CHN_F_VIRTUAL;
442 		break;
443 
444 	default:
445 		return NULL;
446 	}
447 
448 	ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
449 	if (!ch)
450 		return NULL;
451 
452 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
453 	if (!ch->methods) {
454 		kfree(ch, M_DEVBUF);
455 
456 		return NULL;
457 	}
458 
459 	snd_mtxlock(d->lock);
460 	ch->num = 0;
461 	rpnum = 0;
462 	SLIST_FOREACH(sce, &d->channels, link) {
463 		c = sce->channel;
464 		if (direction != c->direction ||
465 				(c->flags & CHN_F_VIRTUAL) != flsearch)
466 			continue;
467 		if (ch->num == c->num)
468 			ch->num++;
469 		else {
470 #if 0
471 			device_printf(d->dev,
472 				"%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
473 				__func__, dirs, ch->num, c->num);
474 #endif
475 			goto retry_num_search;
476 		}
477 		rpnum++;
478 	}
479 	goto retry_num_search_out;
480 retry_num_search:
481 	rpnum = 0;
482 	SLIST_FOREACH(sce, &d->channels, link) {
483 		c = sce->channel;
484 		if (direction != c->direction ||
485 				(c->flags & CHN_F_VIRTUAL) != flsearch)
486 			continue;
487 		if (ch->num == c->num) {
488 			ch->num++;
489 			goto retry_num_search;
490 		}
491 		rpnum++;
492 	}
493 retry_num_search_out:
494 	if (*pnum != rpnum) {
495 		device_printf(d->dev,
496 			"%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
497 			__func__, dirs, *pnum, rpnum);
498 		*pnum = rpnum;
499 	}
500 	(*pnum)++;
501 	snd_mtxunlock(d->lock);
502 
503 	ch->pid = -1;
504 	ch->parentsnddev = d;
505 	ch->parentchannel = parent;
506 	ch->dev = d->dev;
507 	ksnprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
508 
509 	err = chn_init(ch, devinfo, dir, direction);
510 	if (err) {
511 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
512 		kobj_delete(ch->methods, M_DEVBUF);
513 		kfree(ch, M_DEVBUF);
514 		snd_mtxlock(d->lock);
515 		(*pnum)--;
516 		snd_mtxunlock(d->lock);
517 
518 		return NULL;
519 	}
520 
521 	return ch;
522 }
523 
524 int
525 pcm_chn_destroy(struct pcm_channel *ch)
526 {
527 	struct snddev_info *d;
528 	int err;
529 
530 	d = ch->parentsnddev;
531 	err = chn_kill(ch);
532 	if (err) {
533 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
534 		return err;
535 	}
536 
537 	kobj_delete(ch->methods, M_DEVBUF);
538 	kfree(ch, M_DEVBUF);
539 
540 	return 0;
541 }
542 
543 int
544 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
545 {
546 	struct snddev_channel *sce, *tmp, *after;
547 	unsigned rdevcount;
548 	int device = device_get_unit(d->dev);
549 	size_t namelen;
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 = kmalloc(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 		kfree(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 	namelen = strlen(ch->name);
642 	if ((CHN_NAMELEN - namelen) > 10) {	/* ":dspXX.YYY" */
643 		ksnprintf(ch->name + namelen,
644 			CHN_NAMELEN - namelen, ":dsp%d.%d",
645 			device, sce->chan_num);
646 	}
647 	snd_mtxunlock(d->lock);
648 
649 	/*
650 	 * I will revisit these someday, and nuke it mercilessly..
651 	 */
652 	dev_ops_add(&dsp_cdevsw,
653 		    PCMMKMINOR(-1, -1, 0),
654 		    PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
655 	sce->dsp_devt = make_dev(&dsp_cdevsw,
656 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
657 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
658 			device, sce->chan_num);
659 	reference_dev(sce->dsp_devt);
660 
661 	dev_ops_add(&dsp_cdevsw,
662 		    PCMMKMINOR(-1, -1, 0),
663 		    PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num));
664 	sce->dspW_devt = make_dev(&dsp_cdevsw,
665 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
666 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
667 			device, sce->chan_num);
668 	reference_dev(sce->dspW_devt);
669 
670 	/*
671 	dev_ops_add(&dsp_cdevsw,
672 		    PCMMKMINOR(-1, -1, 0),
673 		    PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
674 	sce->audio_devt = make_dev(&dsp_cdevsw,
675 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
676 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
677 			device, sce->chan_num);
678 	*/
679 	sce->audio_devt = sce->dsp_devt;
680 	reference_dev(sce->audio_devt);
681 
682 	if (ch->direction == PCMDIR_REC) {
683 		dev_ops_add(&dsp_cdevsw,
684 			    PCMMKMINOR(-1, -1, 0),
685 			    PCMMKMINOR(device, SND_DEV_DSPREC, sce->chan_num));
686 		sce->dspr_devt = make_dev(&dsp_cdevsw,
687 				PCMMKMINOR(device, SND_DEV_DSPREC,
688 					sce->chan_num), UID_ROOT, GID_WHEEL,
689 				0666, "dspr%d.%d", device, sce->chan_num);
690 		reference_dev(sce->dspr_devt);
691 	}
692 
693 	return 0;
694 }
695 
696 int
697 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
698 {
699 	struct snddev_channel *sce;
700 #if 0
701 	int ourlock;
702 
703 	ourlock = 0;
704 	if (!mtx_owned(d->lock)) {
705 		snd_mtxlock(d->lock);
706 		ourlock = 1;
707 	}
708 #endif
709 
710 	SLIST_FOREACH(sce, &d->channels, link) {
711 		if (sce->channel == ch)
712 			goto gotit;
713 	}
714 #if 0
715 	if (ourlock)
716 		snd_mtxunlock(d->lock);
717 #endif
718 	return EINVAL;
719 gotit:
720 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
721 
722 	if (ch->flags & CHN_F_VIRTUAL)
723 		d->vchancount--;
724 	else if (ch->direction == PCMDIR_REC)
725 		d->reccount--;
726 	else
727 		d->playcount--;
728 
729 #if 0
730 	if (ourlock)
731 		snd_mtxunlock(d->lock);
732 #endif
733 	kfree(sce, M_DEVBUF);
734 
735 	return 0;
736 }
737 
738 int
739 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
740 {
741 	struct snddev_info *d = device_get_softc(dev);
742 	struct pcm_channel *ch;
743 	int err;
744 
745 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
746 	if (!ch) {
747 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
748 		return ENODEV;
749 	}
750 
751 	err = pcm_chn_add(d, ch);
752 	if (err) {
753 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
754 		pcm_chn_destroy(ch);
755 		return err;
756 	}
757 
758 	return err;
759 }
760 
761 static int
762 pcm_killchan(device_t dev)
763 {
764 	struct snddev_info *d = device_get_softc(dev);
765 	struct snddev_channel *sce;
766 	struct pcm_channel *ch;
767 	int error = 0;
768 
769 	sce = SLIST_FIRST(&d->channels);
770 	ch = sce->channel;
771 
772 	error = pcm_chn_remove(d, sce->channel);
773 	if (error)
774 		return (error);
775 	return (pcm_chn_destroy(ch));
776 }
777 
778 int
779 pcm_setstatus(device_t dev, char *str)
780 {
781 	struct snddev_info *d = device_get_softc(dev);
782 
783 	snd_mtxlock(d->lock);
784 	strncpy(d->status, str, SND_STATUSLEN);
785 	snd_mtxunlock(d->lock);
786 	if (snd_maxautovchans > 0)
787 		pcm_setvchans(d, 1);
788 	return 0;
789 }
790 
791 uint32_t
792 pcm_getflags(device_t dev)
793 {
794 	struct snddev_info *d = device_get_softc(dev);
795 
796 	return d->flags;
797 }
798 
799 void
800 pcm_setflags(device_t dev, uint32_t val)
801 {
802 	struct snddev_info *d = device_get_softc(dev);
803 
804 	d->flags = val;
805 }
806 
807 void *
808 pcm_getdevinfo(device_t dev)
809 {
810 	struct snddev_info *d = device_get_softc(dev);
811 
812 	return d->devinfo;
813 }
814 
815 unsigned int
816 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
817 {
818 	struct snddev_info *d = device_get_softc(dev);
819 	int sz, x;
820 
821 	sz = 0;
822 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
823 		x = sz;
824 		RANGE(sz, min, max);
825 		if (x != sz)
826 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
827 		x = min;
828 		while (x < sz)
829 			x <<= 1;
830 		if (x > sz)
831 			x >>= 1;
832 		if (x != sz) {
833 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
834 			sz = x;
835 		}
836 	} else {
837 		sz = deflt;
838 	}
839 
840 	d->bufsz = sz;
841 
842 	return sz;
843 }
844 
845 int
846 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
847 {
848 	struct snddev_info *d = device_get_softc(dev);
849 
850 	if (pcm_veto_load) {
851 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
852 
853 		return EINVAL;
854 	}
855 
856 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
857 
858 #if 0
859 	/*
860 	 * d->flags should be cleared by the allocator of the softc.
861 	 * We cannot clear this field here because several devices set
862 	 * this flag before calling pcm_register().
863 	 */
864 	d->flags = 0;
865 #endif
866 	d->dev = dev;
867 	d->devinfo = devinfo;
868 	d->devcount = 0;
869 	d->reccount = 0;
870 	d->playcount = 0;
871 	d->vchancount = 0;
872 	d->inprog = 0;
873 
874 	SLIST_INIT(&d->channels);
875 
876 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
877 		d->flags |= SD_F_SIMPLEX;
878 
879 	d->fakechan = fkchan_setup(dev);
880 	chn_init(d->fakechan, NULL, 0, 0);
881 
882 #ifdef SND_DYNSYSCTL
883 	sysctl_ctx_init(&d->sysctl_tree);
884 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
885 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
886 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
887 	if (d->sysctl_tree_top == NULL) {
888 		sysctl_ctx_free(&d->sysctl_tree);
889 		goto no;
890 	}
891 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
892             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
893 #endif
894 	if (numplay > 0) {
895 		d->flags |= SD_F_AUTOVCHAN;
896 		vchan_initsys(dev);
897 	}
898 
899 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
900 	return 0;
901 no:
902 	snd_mtxfree(d->lock);
903 	return ENXIO;
904 }
905 
906 int
907 pcm_unregister(device_t dev)
908 {
909 	struct snddev_info *d = device_get_softc(dev);
910 	struct snddev_channel *sce;
911 	struct pcmchan_children *pce;
912 	struct pcm_channel *ch;
913 
914 	if (sndstat_acquire() != 0) {
915 		device_printf(dev, "unregister: sndstat busy\n");
916 		return EBUSY;
917 	}
918 
919 	snd_mtxlock(d->lock);
920 	if (d->inprog) {
921 		device_printf(dev, "unregister: operation in progress\n");
922 		snd_mtxunlock(d->lock);
923 		sndstat_release();
924 		return EBUSY;
925 	}
926 
927 	SLIST_FOREACH(sce, &d->channels, link) {
928 		ch = sce->channel;
929 		if (ch->refcount > 0) {
930 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
931 			snd_mtxunlock(d->lock);
932 			sndstat_release();
933 			return EBUSY;
934 		}
935 	}
936 
937 	if (mixer_uninit(dev) == EBUSY) {
938 		device_printf(dev, "unregister: mixer busy\n");
939 		snd_mtxunlock(d->lock);
940 		sndstat_release();
941 		return EBUSY;
942 	}
943 
944 	SLIST_FOREACH(sce, &d->channels, link) {
945 		int unit = device_get_unit(d->dev);
946 
947 		if (sce->dsp_devt) {
948 			release_dev(sce->dsp_devt);
949 			dev_ops_remove(&dsp_cdevsw,
950 				    PCMMKMINOR(-1, -1, 0),
951 				    PCMMKMINOR(unit, SND_DEV_DSP, sce->chan_num));
952 			sce->dsp_devt = NULL;
953 		}
954 		if (sce->dspW_devt) {
955 			release_dev(sce->dspW_devt);
956 			dev_ops_remove(&dsp_cdevsw,
957 				    PCMMKMINOR(-1, -1, 0),
958 				    PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
959 			sce->dspW_devt = NULL;
960 		}
961 		if (sce->audio_devt) {
962 			release_dev(sce->audio_devt);
963 			/*
964 			dev_ops_remove(&dsp_cdevsw,
965 				    PCMMKMINOR(-1, -1, 0),
966 				    PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
967 			*/
968 			sce->audio_devt = NULL;
969 		}
970 		if (sce->dspr_devt) {
971 			release_dev(sce->dspr_devt);
972 			dev_ops_remove(&dsp_cdevsw,
973 				    PCMMKMINOR(-1, -1, 0),
974 				    PCMMKMINOR(unit, SND_DEV_DSPREC, sce->chan_num));
975 			sce->dspr_devt = NULL;
976 		}
977 		d->devcount--;
978 		ch = sce->channel;
979 		if (ch == NULL)
980 			continue;
981 		pce = SLIST_FIRST(&ch->children);
982 		while (pce != NULL) {
983 #if 0
984 			device_printf(d->dev, "<%s> removing <%s>\n",
985 				ch->name, (pce->channel != NULL) ?
986 					pce->channel->name : "unknown");
987 #endif
988 			SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
989 			kfree(pce, M_DEVBUF);
990 			pce = SLIST_FIRST(&ch->children);
991 		}
992 	}
993 
994 #ifdef SND_DYNSYSCTL
995 	d->sysctl_tree_top = NULL;
996 	sysctl_ctx_free(&d->sysctl_tree);
997 #endif
998 
999 #if 0
1000 	SLIST_FOREACH(sce, &d->channels, link) {
1001 		ch = sce->channel;
1002 		if (ch == NULL)
1003 			continue;
1004 		if (!SLIST_EMPTY(&ch->children))
1005 			device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
1006 				__func__, ch->name);
1007 	}
1008 #endif
1009 	while (!SLIST_EMPTY(&d->channels))
1010 		pcm_killchan(dev);
1011 
1012 	chn_kill(d->fakechan);
1013 	fkchan_kill(d->fakechan);
1014 
1015 #if 0
1016 	device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
1017 		"reccount=%u, vchancount=%u\n",
1018 		__func__, d->devcount, d->playcount, d->reccount,
1019 		d->vchancount);
1020 #endif
1021 	snd_mtxunlock(d->lock);
1022 	snd_mtxfree(d->lock);
1023 	sndstat_unregister(dev);
1024 	sndstat_release();
1025 	return 0;
1026 }
1027 
1028 /************************************************************************/
1029 
1030 static int
1031 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1032 {
1033 	struct snddev_info *d;
1034 	struct snddev_channel *sce;
1035 	struct pcm_channel *c;
1036 	struct pcm_feeder *f;
1037 	int pc, rc, vc;
1038 
1039 	if (verbose < 1)
1040 		return 0;
1041 
1042 	d = device_get_softc(dev);
1043 	if (!d)
1044 		return ENXIO;
1045 
1046 	snd_mtxlock(d->lock);
1047 	if (!SLIST_EMPTY(&d->channels)) {
1048 		pc = rc = vc = 0;
1049 		SLIST_FOREACH(sce, &d->channels, link) {
1050 			c = sce->channel;
1051 			if (c->direction == PCMDIR_PLAY) {
1052 				if (c->flags & CHN_F_VIRTUAL)
1053 					vc++;
1054 				else
1055 					pc++;
1056 			} else
1057 				rc++;
1058 		}
1059 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1060 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
1061 #ifdef USING_DEVFS
1062 				(device_get_unit(dev) == snd_unit)? " default" : ""
1063 #else
1064 				""
1065 #endif
1066 				);
1067 
1068 		if (verbose <= 1) {
1069 			snd_mtxunlock(d->lock);
1070 			return 0;
1071 		}
1072 
1073 		SLIST_FOREACH(sce, &d->channels, link) {
1074 			c = sce->channel;
1075 
1076 			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1077 				("hosed pcm channel setup"));
1078 
1079 			sbuf_printf(s, "\n\t");
1080 
1081 			/* it would be better to indent child channels */
1082 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1083 			sbuf_printf(s, "spd %d", c->speed);
1084 			if (c->speed != sndbuf_getspd(c->bufhard))
1085 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1086 			sbuf_printf(s, ", fmt 0x%08x", c->format);
1087 			if (c->format != sndbuf_getfmt(c->bufhard))
1088 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1089 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1090 			if (c->pid != -1)
1091 				sbuf_printf(s, ", pid %d", c->pid);
1092 			sbuf_printf(s, "\n\t");
1093 
1094 			sbuf_printf(s, "interrupts %d, ", c->interrupts);
1095 			if (c->direction == PCMDIR_REC)
1096 				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1097 					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1098 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1099 					sndbuf_getblkcnt(c->bufhard),
1100 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1101 					sndbuf_getblkcnt(c->bufsoft));
1102 			else
1103 				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1104 					c->xruns, sndbuf_getready(c->bufsoft),
1105 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1106 					sndbuf_getblkcnt(c->bufhard),
1107 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1108 					sndbuf_getblkcnt(c->bufsoft));
1109 			sbuf_printf(s, "\n\t");
1110 
1111 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1112 			sbuf_printf(s, " -> ");
1113 			f = c->feeder;
1114 			while (f->source != NULL)
1115 				f = f->source;
1116 			while (f != NULL) {
1117 				sbuf_printf(s, "%s", f->class->name);
1118 				if (f->desc->type == FEEDER_FMT)
1119 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1120 				if (f->desc->type == FEEDER_RATE)
1121 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1122 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1123 						f->desc->type == FEEDER_VOLUME)
1124 					sbuf_printf(s, "(0x%08x)", f->desc->out);
1125 				sbuf_printf(s, " -> ");
1126 				f = f->parent;
1127 			}
1128 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1129 		}
1130 	} else
1131 		sbuf_printf(s, " (mixer only)");
1132 	snd_mtxunlock(d->lock);
1133 
1134 	return 0;
1135 }
1136 
1137 /************************************************************************/
1138 
1139 #ifdef SND_DYNSYSCTL
1140 int
1141 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1142 {
1143 	struct snddev_info *d;
1144 	int err, newcnt;
1145 
1146 	d = oidp->oid_arg1;
1147 
1148 	newcnt = d->vchancount;
1149 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1150 
1151 	if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1152 		err = pcm_setvchans(d, newcnt);
1153 
1154 	return err;
1155 }
1156 #endif
1157 
1158 /************************************************************************/
1159 
1160 static int
1161 sound_modevent(module_t mod, int type, void *data)
1162 {
1163 #if 0
1164 	return (midi_modevent(mod, type, data));
1165 #else
1166 	return 0;
1167 #endif
1168 }
1169 
1170 DEV_MODULE(sound, sound_modevent, NULL);
1171 MODULE_VERSION(sound, SOUND_MODVER);
1172