xref: /freebsd/sys/dev/sound/pcm/sndstat.c (revision 5d980fadf73df64a1e0eda40a93170ed76ce6f14)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
5  * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
6  * Copyright (c) 2020 The FreeBSD Foundation
7  * All rights reserved.
8  * Copyright (c) 2024 The FreeBSD Foundation
9  *
10  * Portions of this software were developed by Christos Margiolis
11  * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
12  *
13  * Portions of this software were developed by Ka Ho Ng
14  * under sponsorship from the FreeBSD Foundation.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifdef HAVE_KERNEL_OPTION_HEADERS
39 #include "opt_snd.h"
40 #endif
41 
42 #include <sys/param.h>
43 #include <sys/lock.h>
44 #include <sys/malloc.h>
45 #include <sys/nv.h>
46 #include <sys/dnv.h>
47 #include <sys/sx.h>
48 #ifdef COMPAT_FREEBSD32
49 #include <sys/sysent.h>
50 #endif
51 
52 #include <dev/sound/pcm/sound.h>
53 #include <dev/sound/pcm/pcm.h>
54 
55 #include "feeder_if.h"
56 
57 #define	SS_TYPE_PCM		1
58 #define	SS_TYPE_MIDI		2
59 #define	SS_TYPE_SEQUENCER	3
60 
61 static d_open_t sndstat_open;
62 static void sndstat_close(void *);
63 static d_read_t sndstat_read;
64 static d_write_t sndstat_write;
65 static d_ioctl_t sndstat_ioctl;
66 
67 static struct cdevsw sndstat_cdevsw = {
68 	.d_version =	D_VERSION,
69 	.d_open =	sndstat_open,
70 	.d_read =	sndstat_read,
71 	.d_write =	sndstat_write,
72 	.d_ioctl =	sndstat_ioctl,
73 	.d_name =	"sndstat",
74 	.d_flags =	D_TRACKCLOSE,
75 };
76 
77 struct sndstat_entry {
78 	TAILQ_ENTRY(sndstat_entry) link;
79 	device_t dev;
80 	char *str;
81 	int type, unit;
82 };
83 
84 struct sndstat_userdev {
85 	TAILQ_ENTRY(sndstat_userdev) link;
86 	char *provider;
87 	char *nameunit;
88 	char *devnode;
89 	char *desc;
90 	unsigned int pchan;
91 	unsigned int rchan;
92 	struct {
93 		uint32_t min_rate;
94 		uint32_t max_rate;
95 		uint32_t formats;
96 		uint32_t min_chn;
97 		uint32_t max_chn;
98 	} info_play, info_rec;
99 	nvlist_t *provider_nvl;
100 };
101 
102 struct sndstat_file {
103 	TAILQ_ENTRY(sndstat_file) entry;
104 	struct sbuf sbuf;
105 	struct sx lock;
106 	void *devs_nvlbuf;	/* (l) */
107 	size_t devs_nbytes;	/* (l) */
108 	TAILQ_HEAD(, sndstat_userdev) userdev_list;	/* (l) */
109 	int out_offset;
110   	int in_offset;
111 	int fflags;
112 };
113 
114 static struct sx sndstat_lock;
115 static struct cdev *sndstat_dev;
116 
117 #define	SNDSTAT_LOCK() sx_xlock(&sndstat_lock)
118 #define	SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock)
119 
120 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist);
121 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist);
122 
123 int snd_verbose = 0;
124 
125 static int sndstat_prepare(struct sndstat_file *);
126 static struct sndstat_userdev *
127 sndstat_line2userdev(struct sndstat_file *, const char *, int);
128 
129 static int
130 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
131 {
132 	int error, verbose;
133 
134 	verbose = snd_verbose;
135 	error = sysctl_handle_int(oidp, &verbose, 0, req);
136 	if (error == 0 && req->newptr != NULL) {
137 		if (verbose < 0 || verbose > 4)
138 			error = EINVAL;
139 		else
140 			snd_verbose = verbose;
141 	}
142 	return (error);
143 }
144 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose,
145     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
146     sysctl_hw_sndverbose, "I",
147     "verbosity level");
148 
149 static int
150 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
151 {
152 	struct sndstat_file *pf;
153 
154 	pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
155 
156 	if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
157 		free(pf, M_DEVBUF);
158 		return (ENOMEM);
159 	}
160 
161 	pf->fflags = flags;
162 	TAILQ_INIT(&pf->userdev_list);
163 	sx_init(&pf->lock, "sndstat_file");
164 
165 	SNDSTAT_LOCK();
166 	TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
167 	SNDSTAT_UNLOCK();
168 
169 	devfs_set_cdevpriv(pf, &sndstat_close);
170 
171 	return (0);
172 }
173 
174 /*
175  * Should only be called either when:
176  * * Closing
177  * * pf->lock held
178  */
179 static void
180 sndstat_remove_all_userdevs(struct sndstat_file *pf)
181 {
182 	struct sndstat_userdev *ud;
183 
184 	KASSERT(
185 	    sx_xlocked(&pf->lock), ("%s: Called without pf->lock", __func__));
186 	while ((ud = TAILQ_FIRST(&pf->userdev_list)) != NULL) {
187 		TAILQ_REMOVE(&pf->userdev_list, ud, link);
188 		free(ud->provider, M_DEVBUF);
189 		free(ud->desc, M_DEVBUF);
190 		free(ud->devnode, M_DEVBUF);
191 		free(ud->nameunit, M_DEVBUF);
192 		nvlist_destroy(ud->provider_nvl);
193 		free(ud, M_DEVBUF);
194 	}
195 }
196 
197 static void
198 sndstat_close(void *sndstat_file)
199 {
200 	struct sndstat_file *pf = (struct sndstat_file *)sndstat_file;
201 
202 	SNDSTAT_LOCK();
203 	sbuf_delete(&pf->sbuf);
204 	TAILQ_REMOVE(&sndstat_filelist, pf, entry);
205 	SNDSTAT_UNLOCK();
206 
207 	free(pf->devs_nvlbuf, M_NVLIST);
208 	sx_xlock(&pf->lock);
209 	sndstat_remove_all_userdevs(pf);
210 	sx_xunlock(&pf->lock);
211 	sx_destroy(&pf->lock);
212 
213 	free(pf, M_DEVBUF);
214 }
215 
216 static int
217 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
218 {
219 	struct sndstat_file *pf;
220 	int err;
221 	int len;
222 
223 	err = devfs_get_cdevpriv((void **)&pf);
224 	if (err != 0)
225 		return (err);
226 
227 	/* skip zero-length reads */
228 	if (buf->uio_resid == 0)
229 		return (0);
230 
231 	SNDSTAT_LOCK();
232 	if (pf->out_offset != 0) {
233 		/* don't allow both reading and writing */
234 		err = EINVAL;
235 		goto done;
236 	} else if (pf->in_offset == 0) {
237 		err = sndstat_prepare(pf);
238 		if (err <= 0) {
239 			err = ENOMEM;
240 			goto done;
241 		}
242 	}
243 	len = sbuf_len(&pf->sbuf) - pf->in_offset;
244 	if (len > buf->uio_resid)
245 		len = buf->uio_resid;
246 	if (len > 0)
247 		err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf);
248 	pf->in_offset += len;
249 done:
250 	SNDSTAT_UNLOCK();
251 	return (err);
252 }
253 
254 static int
255 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
256 {
257 	struct sndstat_file *pf;
258 	uint8_t temp[64];
259 	int err;
260 	int len;
261 
262 	err = devfs_get_cdevpriv((void **)&pf);
263 	if (err != 0)
264 		return (err);
265 
266 	/* skip zero-length writes */
267 	if (buf->uio_resid == 0)
268 		return (0);
269 
270 	/* don't allow writing more than 64Kbytes */
271 	if (buf->uio_resid > 65536)
272 		return (ENOMEM);
273 
274 	SNDSTAT_LOCK();
275 	if (pf->in_offset != 0) {
276 		/* don't allow both reading and writing */
277 		err = EINVAL;
278 	} else {
279 		/* only remember the last write - allows for updates */
280 		sx_xlock(&pf->lock);
281 		sndstat_remove_all_userdevs(pf);
282 		sx_xunlock(&pf->lock);
283 
284 		while (1) {
285 			len = sizeof(temp);
286 			if (len > buf->uio_resid)
287 				len = buf->uio_resid;
288 			if (len > 0) {
289 				err = uiomove(temp, len, buf);
290 				if (err)
291 					break;
292 			} else {
293 				break;
294 			}
295 			if (sbuf_bcat(&pf->sbuf, temp, len) < 0) {
296 				err = ENOMEM;
297 				break;
298 			}
299 		}
300 		sbuf_finish(&pf->sbuf);
301 
302 		if (err == 0) {
303 			char *line, *str;
304 
305 			str = sbuf_data(&pf->sbuf);
306 			while ((line = strsep(&str, "\n")) != NULL) {
307 				struct sndstat_userdev *ud;
308 
309 				ud = sndstat_line2userdev(pf, line, strlen(line));
310 				if (ud == NULL)
311 					continue;
312 
313 				sx_xlock(&pf->lock);
314 				TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
315 				sx_xunlock(&pf->lock);
316 			}
317 
318 			pf->out_offset = sbuf_len(&pf->sbuf);
319 		} else
320 			pf->out_offset = 0;
321 
322 		sbuf_clear(&pf->sbuf);
323 	}
324 	SNDSTAT_UNLOCK();
325 	return (err);
326 }
327 
328 static void
329 sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate,
330     uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
331 {
332 	struct pcm_channel *c;
333 	unsigned int encoding;
334 	int dir;
335 
336 	dir = play ? PCMDIR_PLAY : PCMDIR_REC;
337 
338 	if (play && d->pvchancount > 0) {
339 		*min_rate = *max_rate = d->pvchanrate;
340 		*fmts = AFMT_ENCODING(d->pvchanformat);
341 		*minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat);
342 		return;
343 	} else if (!play && d->rvchancount > 0) {
344 		*min_rate = *max_rate = d->rvchanrate;
345 		*fmts = AFMT_ENCODING(d->rvchanformat);
346 		*minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat);
347 		return;
348 	}
349 
350 	*min_rate = UINT32_MAX;
351 	*max_rate = 0;
352 	*minchn = UINT32_MAX;
353 	*maxchn = 0;
354 	encoding = 0;
355 	CHN_FOREACH(c, d, channels.pcm) {
356 		struct pcmchan_caps *caps;
357 		int i;
358 
359 		if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
360 			continue;
361 
362 		CHN_LOCK(c);
363 		caps = chn_getcaps(c);
364 		*min_rate = min(caps->minspeed, *min_rate);
365 		*max_rate = max(caps->maxspeed, *max_rate);
366 		for (i = 0; caps->fmtlist[i]; i++) {
367 			encoding |= AFMT_ENCODING(caps->fmtlist[i]);
368 			*minchn = min(AFMT_CHANNEL(encoding), *minchn);
369 			*maxchn = max(AFMT_CHANNEL(encoding), *maxchn);
370 		}
371 		CHN_UNLOCK(c);
372 	}
373 	if (*min_rate == UINT32_MAX)
374 		*min_rate = 0;
375 	if (*minchn == UINT32_MAX)
376 		*minchn = 0;
377 }
378 
379 static nvlist_t *
380 sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats,
381 	    uint32_t min_chn, uint32_t max_chn)
382 {
383 	nvlist_t *nv;
384 
385 	nv = nvlist_create(0);
386 	if (nv == NULL)
387 		return (NULL);
388 	nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_RATE, min_rate);
389 	nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_RATE, max_rate);
390 	nvlist_add_number(nv, SNDST_DSPS_INFO_FORMATS, formats);
391 	nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_CHN, min_chn);
392 	nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_CHN, max_chn);
393 	return (nv);
394 }
395 
396 static int
397 sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
398 {
399 	uint32_t maxrate, minrate, fmts, minchn, maxchn;
400 	nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL;
401 	int err;
402 
403 	di = nvlist_create(0);
404 	if (di == NULL) {
405 		err = ENOMEM;
406 		goto done;
407 	}
408 	sound4di = nvlist_create(0);
409 	if (sound4di == NULL) {
410 		err = ENOMEM;
411 		goto done;
412 	}
413 
414 	nvlist_add_bool(di, SNDST_DSPS_FROM_USER, false);
415 	nvlist_add_stringf(di, SNDST_DSPS_NAMEUNIT, "%s",
416 			device_get_nameunit(d->dev));
417 	nvlist_add_stringf(di, SNDST_DSPS_DEVNODE, "dsp%d",
418 			device_get_unit(d->dev));
419 	nvlist_add_string(
420 			di, SNDST_DSPS_DESC, device_get_desc(d->dev));
421 
422 	PCM_ACQUIRE_QUICK(d);
423 	nvlist_add_number(di, SNDST_DSPS_PCHAN, d->playcount);
424 	nvlist_add_number(di, SNDST_DSPS_RCHAN, d->reccount);
425 	if (d->playcount > 0) {
426 		sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn,
427 		    &maxchn);
428 		nvlist_add_number(di, "pminrate", minrate);
429 		nvlist_add_number(di, "pmaxrate", maxrate);
430 		nvlist_add_number(di, "pfmts", fmts);
431 		diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
432 		    minchn, maxchn);
433 		if (diinfo == NULL)
434 			nvlist_set_error(di, ENOMEM);
435 		else
436 			nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
437 	}
438 	if (d->reccount > 0) {
439 		sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn,
440 		    &maxchn);
441 		nvlist_add_number(di, "rminrate", minrate);
442 		nvlist_add_number(di, "rmaxrate", maxrate);
443 		nvlist_add_number(di, "rfmts", fmts);
444 		diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
445 		    minchn, maxchn);
446 		if (diinfo == NULL)
447 			nvlist_set_error(di, ENOMEM);
448 		else
449 			nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
450 	}
451 
452 	nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_UNIT,
453 			device_get_unit(d->dev)); // XXX: I want signed integer here
454 	nvlist_add_bool(
455 	    sound4di, SNDST_DSPS_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT);
456 	nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHAN, d->pvchancount);
457 	nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHAN, d->rvchancount);
458 	nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di);
459 	sound4di = NULL;
460 	PCM_RELEASE_QUICK(d);
461 	nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER);
462 
463 	err = nvlist_error(di);
464 	if (err)
465 		goto done;
466 
467 	*dip = di;
468 
469 done:
470 	if (err) {
471 		nvlist_destroy(sound4di);
472 		nvlist_destroy(di);
473 	}
474 	return (err);
475 }
476 
477 static int
478 sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip)
479 {
480 	nvlist_t *di, *diinfo;
481 	int err;
482 
483 	di = nvlist_create(0);
484 	if (di == NULL) {
485 		err = ENOMEM;
486 		goto done;
487 	}
488 
489 	nvlist_add_bool(di, SNDST_DSPS_FROM_USER, true);
490 	nvlist_add_number(di, SNDST_DSPS_PCHAN, ud->pchan);
491 	nvlist_add_number(di, SNDST_DSPS_RCHAN, ud->rchan);
492 	nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, ud->nameunit);
493 	nvlist_add_string(
494 			di, SNDST_DSPS_DEVNODE, ud->devnode);
495 	nvlist_add_string(di, SNDST_DSPS_DESC, ud->desc);
496 	if (ud->pchan != 0) {
497 		nvlist_add_number(di, "pminrate",
498 		    ud->info_play.min_rate);
499 		nvlist_add_number(di, "pmaxrate",
500 		    ud->info_play.max_rate);
501 		nvlist_add_number(di, "pfmts",
502 		    ud->info_play.formats);
503 		diinfo = sndstat_create_diinfo_nv(ud->info_play.min_rate,
504 		    ud->info_play.max_rate, ud->info_play.formats,
505 		    ud->info_play.min_chn, ud->info_play.max_chn);
506 		if (diinfo == NULL)
507 			nvlist_set_error(di, ENOMEM);
508 		else
509 			nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
510 	}
511 	if (ud->rchan != 0) {
512 		nvlist_add_number(di, "rminrate",
513 		    ud->info_rec.min_rate);
514 		nvlist_add_number(di, "rmaxrate",
515 		    ud->info_rec.max_rate);
516 		nvlist_add_number(di, "rfmts",
517 		    ud->info_rec.formats);
518 		diinfo = sndstat_create_diinfo_nv(ud->info_rec.min_rate,
519 		    ud->info_rec.max_rate, ud->info_rec.formats,
520 		    ud->info_rec.min_chn, ud->info_rec.max_chn);
521 		if (diinfo == NULL)
522 			nvlist_set_error(di, ENOMEM);
523 		else
524 			nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
525 	}
526 	nvlist_add_string(di, SNDST_DSPS_PROVIDER,
527 	    (ud->provider != NULL) ? ud->provider : "");
528 	if (ud->provider_nvl != NULL)
529 		nvlist_add_nvlist(
530 		    di, SNDST_DSPS_PROVIDER_INFO, ud->provider_nvl);
531 
532 	err = nvlist_error(di);
533 	if (err)
534 		goto done;
535 
536 	*dip = di;
537 
538 done:
539 	if (err)
540 		nvlist_destroy(di);
541 	return (err);
542 }
543 
544 /*
545  * Should only be called with the following locks held:
546  * * sndstat_lock
547  */
548 static int
549 sndstat_create_devs_nvlist(nvlist_t **nvlp)
550 {
551 	int err;
552 	nvlist_t *nvl;
553 	struct sndstat_entry *ent;
554 	struct sndstat_file *pf;
555 
556 	nvl = nvlist_create(0);
557 	if (nvl == NULL)
558 		return (ENOMEM);
559 
560 	TAILQ_FOREACH(ent, &sndstat_devlist, link) {
561 		struct snddev_info *d;
562 		nvlist_t *di;
563 
564 		d = device_get_softc(ent->dev);
565 		if (!PCM_REGISTERED(d))
566 			continue;
567 
568 		err = sndstat_build_sound4_nvlist(d, &di);
569 		if (err)
570 			goto done;
571 
572 		nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
573 		nvlist_destroy(di);
574 		err = nvlist_error(nvl);
575 		if (err)
576 			goto done;
577 	}
578 
579 	TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
580 		struct sndstat_userdev *ud;
581 
582 		sx_xlock(&pf->lock);
583 
584 		TAILQ_FOREACH(ud, &pf->userdev_list, link) {
585 			nvlist_t *di;
586 
587 			err = sndstat_build_userland_nvlist(ud, &di);
588 			if (err != 0) {
589 				sx_xunlock(&pf->lock);
590 				goto done;
591 			}
592 			nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
593 			nvlist_destroy(di);
594 
595 			err = nvlist_error(nvl);
596 			if (err != 0) {
597 				sx_xunlock(&pf->lock);
598 				goto done;
599 			}
600 		}
601 
602 		sx_xunlock(&pf->lock);
603 	}
604 
605 	*nvlp = nvl;
606 
607 done:
608 	if (err != 0)
609 		nvlist_destroy(nvl);
610 	return (err);
611 }
612 
613 static int
614 sndstat_refresh_devs(struct sndstat_file *pf)
615 {
616 	sx_xlock(&pf->lock);
617 	free(pf->devs_nvlbuf, M_NVLIST);
618 	pf->devs_nvlbuf = NULL;
619 	pf->devs_nbytes = 0;
620 	sx_unlock(&pf->lock);
621 
622 	return (0);
623 }
624 
625 static int
626 sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
627 {
628 	int err;
629 	struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
630 
631 	SNDSTAT_LOCK();
632 	sx_xlock(&pf->lock);
633 
634 	if (pf->devs_nvlbuf == NULL) {
635 		nvlist_t *nvl;
636 		void *nvlbuf;
637 		size_t nbytes;
638 		int err;
639 
640 		sx_xunlock(&pf->lock);
641 
642 		err = sndstat_create_devs_nvlist(&nvl);
643 		if (err) {
644 			SNDSTAT_UNLOCK();
645 			return (err);
646 		}
647 
648 		sx_xlock(&pf->lock);
649 
650 		nvlbuf = nvlist_pack(nvl, &nbytes);
651 		err = nvlist_error(nvl);
652 		nvlist_destroy(nvl);
653 		if (nvlbuf == NULL || err != 0) {
654 			SNDSTAT_UNLOCK();
655 			sx_xunlock(&pf->lock);
656 			if (err == 0)
657 				return (ENOMEM);
658 			return (err);
659 		}
660 
661 		free(pf->devs_nvlbuf, M_NVLIST);
662 		pf->devs_nvlbuf = nvlbuf;
663 		pf->devs_nbytes = nbytes;
664 	}
665 
666 	SNDSTAT_UNLOCK();
667 
668 	if (!arg->nbytes) {
669 		arg->nbytes = pf->devs_nbytes;
670 		err = 0;
671 		goto done;
672 	}
673 	if (arg->nbytes < pf->devs_nbytes) {
674 		arg->nbytes = 0;
675 		err = 0;
676 		goto done;
677 	}
678 
679 	err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes);
680 	if (err)
681 		goto done;
682 
683 	arg->nbytes = pf->devs_nbytes;
684 
685 	free(pf->devs_nvlbuf, M_NVLIST);
686 	pf->devs_nvlbuf = NULL;
687 	pf->devs_nbytes = 0;
688 
689 done:
690 	sx_unlock(&pf->lock);
691 	return (err);
692 }
693 
694 static int
695 sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
696 {
697 	void *nvlbuf;
698 	int err;
699 
700 	nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK);
701 	err = copyin(unvlbuf, nvlbuf, nbytes);
702 	if (err != 0) {
703 		free(nvlbuf, M_DEVBUF);
704 		return (err);
705 	}
706 	*nvl = nvlist_unpack(nvlbuf, nbytes, 0);
707 	free(nvlbuf, M_DEVBUF);
708 	if (*nvl == NULL) {
709 		return (EINVAL);
710 	}
711 
712 	return (0);
713 }
714 
715 static bool
716 sndstat_diinfo_is_sane(const nvlist_t *diinfo)
717 {
718 	if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) &&
719 	    nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) &&
720 	    nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) &&
721 	    nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) &&
722 	    nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN)))
723 		return (false);
724 	return (true);
725 }
726 
727 static bool
728 sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
729 {
730 	if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) &&
731 	    nvlist_exists_string(nvlist, SNDST_DSPS_DESC) &&
732 	    nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) &&
733 	    nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN)))
734 		return (false);
735 
736 	if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) {
737 		if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
738 			if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
739 			    SNDST_DSPS_INFO_PLAY)))
740 				return (false);
741 		} else if (!(nvlist_exists_number(nvlist, "pminrate") &&
742 		    nvlist_exists_number(nvlist, "pmaxrate") &&
743 		    nvlist_exists_number(nvlist, "pfmts")))
744 			return (false);
745 	}
746 
747 	if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) {
748 		if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
749 			if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
750 			    SNDST_DSPS_INFO_REC)))
751 				return (false);
752 		} else if (!(nvlist_exists_number(nvlist, "rminrate") &&
753 		    nvlist_exists_number(nvlist, "rmaxrate") &&
754 		    nvlist_exists_number(nvlist, "rfmts")))
755 			return (false);
756 	}
757 
758 	return (true);
759 
760 }
761 
762 static void
763 sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate,
764 	    uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn,
765 	    uint32_t *max_chn)
766 {
767 	*min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE);
768 	*max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE);
769 	*formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS);
770 	*min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN);
771 	*max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN);
772 }
773 
774 static int
775 sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
776 {
777 	const char *nameunit, *devnode, *desc;
778 	unsigned int pchan, rchan;
779 	uint32_t pminrate = 0, pmaxrate = 0;
780 	uint32_t rminrate = 0, rmaxrate = 0;
781 	uint32_t pfmts = 0, rfmts = 0;
782 	uint32_t pminchn = 0, pmaxchn = 0;
783 	uint32_t rminchn = 0, rmaxchn = 0;
784 	nvlist_t *provider_nvl = NULL;
785 	const nvlist_t *diinfo;
786 	const char *provider;
787 
788 	devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE);
789 	if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT))
790 		nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT);
791 	else
792 		nameunit = devnode;
793 	desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC);
794 	pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN);
795 	rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN);
796 	if (pchan != 0) {
797 		if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
798 			diinfo = nvlist_get_nvlist(nvlist,
799 			    SNDST_DSPS_INFO_PLAY);
800 			sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate,
801 			    &pfmts, &pminchn, &pmaxchn);
802 		} else {
803 			pminrate = nvlist_get_number(nvlist, "pminrate");
804 			pmaxrate = nvlist_get_number(nvlist, "pmaxrate");
805 			pfmts = nvlist_get_number(nvlist, "pfmts");
806 		}
807 	}
808 	if (rchan != 0) {
809 		if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
810 			diinfo = nvlist_get_nvlist(nvlist,
811 			    SNDST_DSPS_INFO_REC);
812 			sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate,
813 			    &rfmts, &rminchn, &rmaxchn);
814 		} else {
815 			rminrate = nvlist_get_number(nvlist, "rminrate");
816 			rmaxrate = nvlist_get_number(nvlist, "rmaxrate");
817 			rfmts = nvlist_get_number(nvlist, "rfmts");
818 		}
819 	}
820 
821 	provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, "");
822 	if (provider[0] == '\0')
823 		provider = NULL;
824 
825 	if (provider != NULL &&
826 	    nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) {
827 		provider_nvl = nvlist_clone(
828 		    nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO));
829 		if (provider_nvl == NULL)
830 			return (ENOMEM);
831 	}
832 
833 	ud->provider = (provider != NULL) ? strdup(provider, M_DEVBUF) : NULL;
834 	ud->devnode = strdup(devnode, M_DEVBUF);
835 	ud->nameunit = strdup(nameunit, M_DEVBUF);
836 	ud->desc = strdup(desc, M_DEVBUF);
837 	ud->pchan = pchan;
838 	ud->rchan = rchan;
839 	ud->info_play.min_rate = pminrate;
840 	ud->info_play.max_rate = pmaxrate;
841 	ud->info_play.formats = pfmts;
842 	ud->info_play.min_chn = pminchn;
843 	ud->info_play.max_chn = pmaxchn;
844 	ud->info_rec.min_rate = rminrate;
845 	ud->info_rec.max_rate = rmaxrate;
846 	ud->info_rec.formats = rfmts;
847 	ud->info_rec.min_chn = rminchn;
848 	ud->info_rec.max_chn = rmaxchn;
849 	ud->provider_nvl = provider_nvl;
850 	return (0);
851 }
852 
853 static int
854 sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
855 {
856 	int err;
857 	nvlist_t *nvl = NULL;
858 	const nvlist_t * const *dsps;
859 	size_t i, ndsps;
860 	struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
861 
862 	if ((pf->fflags & FWRITE) == 0) {
863 		err = EPERM;
864 		goto done;
865 	}
866 
867 	if (arg->nbytes > SNDST_UNVLBUF_MAX) {
868 		err = ENOMEM;
869 		goto done;
870 	}
871 
872 	err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl);
873 	if (err != 0)
874 		goto done;
875 
876 	if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) {
877 		err = EINVAL;
878 		goto done;
879 	}
880 	dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps);
881 	for (i = 0; i < ndsps; i++) {
882 		if (!sndstat_dsp_nvlist_is_sane(dsps[i])) {
883 			err = EINVAL;
884 			goto done;
885 		}
886 	}
887 	sx_xlock(&pf->lock);
888 	for (i = 0; i < ndsps; i++) {
889 		struct sndstat_userdev *ud =
890 		    malloc(sizeof(*ud), M_DEVBUF, M_WAITOK);
891 		err = sndstat_dsp_unpack_nvlist(dsps[i], ud);
892 		if (err) {
893 			sx_unlock(&pf->lock);
894 			goto done;
895 		}
896 		TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
897 	}
898 	sx_unlock(&pf->lock);
899 
900 done:
901 	nvlist_destroy(nvl);
902 	return (err);
903 }
904 
905 static int
906 sndstat_flush_user_devs(struct sndstat_file *pf)
907 {
908 	if ((pf->fflags & FWRITE) == 0)
909 		return (EPERM);
910 
911 	sx_xlock(&pf->lock);
912 	sndstat_remove_all_userdevs(pf);
913 	sx_xunlock(&pf->lock);
914 
915 	return (0);
916 }
917 
918 #ifdef COMPAT_FREEBSD32
919 static int
920 compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
921 {
922 	struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
923 	struct sndstioc_nv_arg arg;
924 	int err;
925 
926 	arg.buf = (void *)(uintptr_t)arg32->buf;
927 	arg.nbytes = arg32->nbytes;
928 
929 	err = sndstat_get_devs(pf, (caddr_t)&arg);
930 	if (err == 0) {
931 		arg32->buf = (uint32_t)(uintptr_t)arg.buf;
932 		arg32->nbytes = arg.nbytes;
933 	}
934 
935 	return (err);
936 }
937 
938 static int
939 compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
940 {
941 	struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
942 	struct sndstioc_nv_arg arg;
943 	int err;
944 
945 	arg.buf = (void *)(uintptr_t)arg32->buf;
946 	arg.nbytes = arg32->nbytes;
947 
948 	err = sndstat_add_user_devs(pf, (caddr_t)&arg);
949 	if (err == 0) {
950 		arg32->buf = (uint32_t)(uintptr_t)arg.buf;
951 		arg32->nbytes = arg.nbytes;
952 	}
953 
954 	return (err);
955 }
956 #endif
957 
958 static int
959 sndstat_ioctl(
960     struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
961 {
962 	int err;
963 	struct sndstat_file *pf;
964 
965 	err = devfs_get_cdevpriv((void **)&pf);
966 	if (err != 0)
967 		return (err);
968 
969 	switch (cmd) {
970 	case SNDSTIOC_GET_DEVS:
971 		err = sndstat_get_devs(pf, data);
972 		break;
973 #ifdef COMPAT_FREEBSD32
974 	case SNDSTIOC_GET_DEVS32:
975 		if (!SV_CURPROC_FLAG(SV_ILP32)) {
976 			err = ENODEV;
977 			break;
978 		}
979 		err = compat_sndstat_get_devs32(pf, data);
980 		break;
981 #endif
982 	case SNDSTIOC_ADD_USER_DEVS:
983 		err = sndstat_add_user_devs(pf, data);
984 		break;
985 #ifdef COMPAT_FREEBSD32
986 	case SNDSTIOC_ADD_USER_DEVS32:
987 		if (!SV_CURPROC_FLAG(SV_ILP32)) {
988 			err = ENODEV;
989 			break;
990 		}
991 		err = compat_sndstat_add_user_devs32(pf, data);
992 		break;
993 #endif
994 	case SNDSTIOC_REFRESH_DEVS:
995 		err = sndstat_refresh_devs(pf);
996 		break;
997 	case SNDSTIOC_FLUSH_USER_DEVS:
998 		err = sndstat_flush_user_devs(pf);
999 		break;
1000 	default:
1001 		err = ENODEV;
1002 	}
1003 
1004 	return (err);
1005 }
1006 
1007 static struct sndstat_userdev *
1008 sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
1009 {
1010 	struct sndstat_userdev *ud;
1011 	const char *e, *m;
1012 
1013 	ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO);
1014 
1015 	ud->provider = NULL;
1016 	ud->provider_nvl = NULL;
1017 	e = strchr(line, ':');
1018 	if (e == NULL)
1019 		goto fail;
1020 	ud->nameunit = strndup(line, e - line, M_DEVBUF);
1021 	ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO);
1022 	strlcat(ud->devnode, ud->nameunit, e - line + 1);
1023 	line = e + 1;
1024 
1025 	e = strchr(line, '<');
1026 	if (e == NULL)
1027 		goto fail;
1028 	line = e + 1;
1029 	e = strrchr(line, '>');
1030 	if (e == NULL)
1031 		goto fail;
1032 	ud->desc = strndup(line, e - line, M_DEVBUF);
1033 	line = e + 1;
1034 
1035 	e = strchr(line, '(');
1036 	if (e == NULL)
1037 		goto fail;
1038 	line = e + 1;
1039 	e = strrchr(line, ')');
1040 	if (e == NULL)
1041 		goto fail;
1042 	m = strstr(line, "play");
1043 	if (m != NULL && m < e)
1044 		ud->pchan = 1;
1045 	m = strstr(line, "rec");
1046 	if (m != NULL && m < e)
1047 		ud->rchan = 1;
1048 
1049 	return (ud);
1050 
1051 fail:
1052 	free(ud->nameunit, M_DEVBUF);
1053 	free(ud->devnode, M_DEVBUF);
1054 	free(ud->desc, M_DEVBUF);
1055 	free(ud, M_DEVBUF);
1056 	return (NULL);
1057 }
1058 
1059 /************************************************************************/
1060 
1061 int
1062 sndstat_register(device_t dev, char *str)
1063 {
1064 	struct sndstat_entry *ent;
1065 	struct sndstat_entry *pre;
1066 	const char *devtype;
1067 	int type, unit;
1068 
1069 	unit = device_get_unit(dev);
1070 	devtype = device_get_name(dev);
1071 	if (!strcmp(devtype, "pcm"))
1072 		type = SS_TYPE_PCM;
1073 	else if (!strcmp(devtype, "midi"))
1074 		type = SS_TYPE_MIDI;
1075 	else if (!strcmp(devtype, "sequencer"))
1076 		type = SS_TYPE_SEQUENCER;
1077 	else
1078 		return (EINVAL);
1079 
1080 	ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
1081 	ent->dev = dev;
1082 	ent->str = str;
1083 	ent->type = type;
1084 	ent->unit = unit;
1085 
1086 	SNDSTAT_LOCK();
1087 	/* sorted list insertion */
1088 	TAILQ_FOREACH(pre, &sndstat_devlist, link) {
1089 		if (pre->unit > unit)
1090 			break;
1091 		else if (pre->unit < unit)
1092 			continue;
1093 		if (pre->type > type)
1094 			break;
1095 		else if (pre->type < unit)
1096 			continue;
1097 	}
1098 	if (pre == NULL) {
1099 		TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
1100 	} else {
1101 		TAILQ_INSERT_BEFORE(pre, ent, link);
1102 	}
1103 	SNDSTAT_UNLOCK();
1104 
1105 	return (0);
1106 }
1107 
1108 int
1109 sndstat_unregister(device_t dev)
1110 {
1111 	struct sndstat_entry *ent;
1112 	int error = ENXIO;
1113 
1114 	SNDSTAT_LOCK();
1115 	TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1116 		if (ent->dev == dev) {
1117 			TAILQ_REMOVE(&sndstat_devlist, ent, link);
1118 			free(ent, M_DEVBUF);
1119 			error = 0;
1120 			break;
1121 		}
1122 	}
1123 	SNDSTAT_UNLOCK();
1124 
1125 	return (error);
1126 }
1127 
1128 /************************************************************************/
1129 
1130 static int
1131 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1132 {
1133 	struct snddev_info *d;
1134 	struct pcm_channel *c;
1135 	struct pcm_feeder *f;
1136 
1137 	d = device_get_softc(dev);
1138 	PCM_BUSYASSERT(d);
1139 
1140 	if (CHN_EMPTY(d, channels.pcm)) {
1141 		sbuf_printf(s, " (mixer only)");
1142 		return (0);
1143 	}
1144 
1145 	if (verbose < 1) {
1146 		sbuf_printf(s, " (%s%s%s",
1147 		    d->playcount ? "play" : "",
1148 		    (d->playcount && d->reccount) ? "/" : "",
1149 		    d->reccount ? "rec" : "");
1150 	} else {
1151 		sbuf_printf(s, " (%dp:%dv/%dr:%dv",
1152 		    d->playcount, d->pvchancount,
1153 		    d->reccount, d->rvchancount);
1154 	}
1155 	sbuf_printf(s, "%s)%s",
1156 	    ((d->playcount != 0 && d->reccount != 0) &&
1157 	    (d->flags & SD_F_SIMPLEX)) ? " simplex" : "",
1158 	    (device_get_unit(dev) == snd_unit) ? " default" : "");
1159 
1160 	if (verbose <= 1)
1161 		return (0);
1162 
1163 	sbuf_printf(s, "\n\t");
1164 	sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS);
1165 
1166 	CHN_FOREACH(c, d, channels.pcm) {
1167 		KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1168 		    ("hosed pcm channel setup"));
1169 
1170 		sbuf_printf(s, "\n\t");
1171 
1172 		sbuf_printf(s, "%s[%s]: ",
1173 		    (c->parentchannel != NULL) ?
1174 		    c->parentchannel->name : "", c->name);
1175 		sbuf_printf(s, "spd %d", c->speed);
1176 		if (c->speed != sndbuf_getspd(c->bufhard)) {
1177 			sbuf_printf(s, "/%d",
1178 			    sndbuf_getspd(c->bufhard));
1179 		}
1180 		sbuf_printf(s, ", fmt 0x%08x", c->format);
1181 		if (c->format != sndbuf_getfmt(c->bufhard)) {
1182 			sbuf_printf(s, "/0x%08x",
1183 			    sndbuf_getfmt(c->bufhard));
1184 		}
1185 		sbuf_printf(s, ", flags 0x%08x, 0x%08x",
1186 		    c->flags, c->feederflags);
1187 		if (c->pid != -1) {
1188 			sbuf_printf(s, ", pid %d (%s)",
1189 			    c->pid, c->comm);
1190 		}
1191 		sbuf_printf(s, "\n\t");
1192 
1193 		sbuf_printf(s, "interrupts %d, ", c->interrupts);
1194 
1195 		if (c->direction == PCMDIR_REC)	{
1196 			sbuf_printf(s,
1197 			    "overruns %d, feed %u, hfree %d, "
1198 			    "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1199 				c->xruns, c->feedcount,
1200 				sndbuf_getfree(c->bufhard),
1201 				sndbuf_getfree(c->bufsoft),
1202 				sndbuf_getsize(c->bufhard),
1203 				sndbuf_getblksz(c->bufhard),
1204 				sndbuf_getblkcnt(c->bufhard),
1205 				sndbuf_getsize(c->bufsoft),
1206 				sndbuf_getblksz(c->bufsoft),
1207 				sndbuf_getblkcnt(c->bufsoft));
1208 		} else {
1209 			sbuf_printf(s,
1210 			    "underruns %d, feed %u, ready %d "
1211 			    "[b:%d/%d/%d|bs:%d/%d/%d]",
1212 				c->xruns, c->feedcount,
1213 				sndbuf_getready(c->bufsoft),
1214 				sndbuf_getsize(c->bufhard),
1215 				sndbuf_getblksz(c->bufhard),
1216 				sndbuf_getblkcnt(c->bufhard),
1217 				sndbuf_getsize(c->bufsoft),
1218 				sndbuf_getblksz(c->bufsoft),
1219 				sndbuf_getblkcnt(c->bufsoft));
1220 		}
1221 		sbuf_printf(s, "\n\t");
1222 
1223 		sbuf_printf(s, "channel flags=0x%b", c->flags, CHN_F_BITS);
1224 		sbuf_printf(s, "\n\t");
1225 
1226 		sbuf_printf(s, "{%s}",
1227 		    (c->direction == PCMDIR_REC) ? "hardware" : "userland");
1228 		sbuf_printf(s, " -> ");
1229 		f = c->feeder;
1230 		while (f->source != NULL)
1231 			f = f->source;
1232 		while (f != NULL) {
1233 			sbuf_printf(s, "%s", f->class->name);
1234 			if (f->desc->type == FEEDER_FORMAT) {
1235 				sbuf_printf(s, "(0x%08x -> 0x%08x)",
1236 				    f->desc->in, f->desc->out);
1237 			} else if (f->desc->type == FEEDER_MATRIX) {
1238 				sbuf_printf(s, "(%d.%d -> %d.%d)",
1239 				    AFMT_CHANNEL(f->desc->in) -
1240 				    AFMT_EXTCHANNEL(f->desc->in),
1241 				    AFMT_EXTCHANNEL(f->desc->in),
1242 				    AFMT_CHANNEL(f->desc->out) -
1243 				    AFMT_EXTCHANNEL(f->desc->out),
1244 				    AFMT_EXTCHANNEL(f->desc->out));
1245 			} else if (f->desc->type == FEEDER_RATE) {
1246 				sbuf_printf(s,
1247 				    "(0x%08x q:%d %d -> %d)",
1248 				    f->desc->out,
1249 				    FEEDER_GET(f, FEEDRATE_QUALITY),
1250 				    FEEDER_GET(f, FEEDRATE_SRC),
1251 				    FEEDER_GET(f, FEEDRATE_DST));
1252 			} else {
1253 				sbuf_printf(s, "(0x%08x)",
1254 				    f->desc->out);
1255 			}
1256 			sbuf_printf(s, " -> ");
1257 			f = f->parent;
1258 		}
1259 		sbuf_printf(s, "{%s}",
1260 		    (c->direction == PCMDIR_REC) ? "userland" : "hardware");
1261 	}
1262 
1263 	return (0);
1264 }
1265 
1266 static int
1267 sndstat_prepare(struct sndstat_file *pf_self)
1268 {
1269 	struct sbuf *s = &pf_self->sbuf;
1270 	struct sndstat_entry *ent;
1271 	struct snddev_info *d;
1272 	struct sndstat_file *pf;
1273     	int k;
1274 
1275 	/* make sure buffer is reset */
1276 	sbuf_clear(s);
1277 
1278 	if (snd_verbose > 0)
1279 		sbuf_printf(s, "FreeBSD Audio Driver\n");
1280 
1281 	/* generate list of installed devices */
1282 	k = 0;
1283 	TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1284 		d = device_get_softc(ent->dev);
1285 		if (!PCM_REGISTERED(d))
1286 			continue;
1287 		if (!k++)
1288 			sbuf_printf(s, "Installed devices:\n");
1289 		sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
1290 		sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
1291 		if (snd_verbose > 0)
1292 			sbuf_printf(s, " %s", ent->str);
1293 		/* XXX Need Giant magic entry ??? */
1294 		PCM_ACQUIRE_QUICK(d);
1295 		sndstat_prepare_pcm(s, ent->dev, snd_verbose);
1296 		PCM_RELEASE_QUICK(d);
1297 		sbuf_printf(s, "\n");
1298 	}
1299 	if (k == 0)
1300 		sbuf_printf(s, "No devices installed.\n");
1301 
1302 	/* append any input from userspace */
1303 	k = 0;
1304 	TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
1305 		struct sndstat_userdev *ud;
1306 
1307 		if (pf == pf_self)
1308 			continue;
1309 		sx_xlock(&pf->lock);
1310 		if (TAILQ_EMPTY(&pf->userdev_list)) {
1311 			sx_unlock(&pf->lock);
1312 			continue;
1313 		}
1314 		if (!k++)
1315 			sbuf_printf(s, "Installed devices from userspace:\n");
1316 		TAILQ_FOREACH(ud, &pf->userdev_list, link) {
1317 			const char *caps = (ud->pchan && ud->rchan) ?
1318 			    "play/rec" :
1319 			    (ud->pchan ? "play" : (ud->rchan ? "rec" : ""));
1320 			sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc);
1321 			sbuf_printf(s, " (%s)", caps);
1322 			sbuf_printf(s, "\n");
1323 		}
1324 		sx_unlock(&pf->lock);
1325 	}
1326 	if (k == 0)
1327 		sbuf_printf(s, "No devices installed from userspace.\n");
1328 
1329 	sbuf_finish(s);
1330     	return (sbuf_len(s));
1331 }
1332 
1333 static void
1334 sndstat_sysinit(void *p)
1335 {
1336 	sx_init(&sndstat_lock, "sndstat lock");
1337 	sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
1338 	    UID_ROOT, GID_WHEEL, 0644, "sndstat");
1339 }
1340 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
1341 
1342 static void
1343 sndstat_sysuninit(void *p)
1344 {
1345 	if (sndstat_dev != NULL) {
1346 		/* destroy_dev() will wait for all references to go away */
1347 		destroy_dev(sndstat_dev);
1348 	}
1349 	sx_destroy(&sndstat_lock);
1350 }
1351 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
1352