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