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