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