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