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