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