FreeBSD kernel sound device code
sndstat.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 *
9 * Portions of this software were developed by Ka Ho Ng
10 * under sponsorship from the FreeBSD Foundation.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifdef HAVE_KERNEL_OPTION_HEADERS
35#include "opt_snd.h"
36#endif
37
38#include <sys/param.h>
39#include <sys/lock.h>
40#include <sys/malloc.h>
41#include <sys/nv.h>
42#include <sys/dnv.h>
43#include <sys/sx.h>
44#ifdef COMPAT_FREEBSD32
45#include <sys/sysent.h>
46#endif
47
48#include <dev/sound/pcm/sound.h>
49#include <dev/sound/pcm/pcm.h>
50#include <dev/sound/version.h>
51
52
53SND_DECLARE_FILE("$FreeBSD$");
54
55#define SS_TYPE_MODULE 0
56#define SS_TYPE_PCM 1
57#define SS_TYPE_MIDI 2
58#define SS_TYPE_SEQUENCER 3
59
60static d_open_t sndstat_open;
61static void sndstat_close(void *);
62static d_read_t sndstat_read;
63static d_write_t sndstat_write;
64static d_ioctl_t sndstat_ioctl;
65
66static struct cdevsw sndstat_cdevsw = {
67 .d_version = D_VERSION,
68 .d_open = sndstat_open,
69 .d_read = sndstat_read,
70 .d_write = sndstat_write,
71 .d_ioctl = sndstat_ioctl,
72 .d_name = "sndstat",
73 .d_flags = D_TRACKCLOSE,
74};
75
77 TAILQ_ENTRY(sndstat_entry) link;
78 device_t dev;
79 char *str;
80 sndstat_handler handler;
81 int type, unit;
82};
83
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;
99 nvlist_t *provider_nvl;
100};
101
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
114static struct sx sndstat_lock;
115static struct cdev *sndstat_dev;
116
117#define SNDSTAT_LOCK() sx_xlock(&sndstat_lock)
118#define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock)
119
120static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist);
121static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist);
122
123int snd_verbose = 0;
124
125static int sndstat_prepare(struct sndstat_file *);
126static struct sndstat_userdev *
127sndstat_line2userdev(struct sndstat_file *, const char *, int);
128
129static int
130sysctl_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}
144SYSCTL_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
149static int
150sndstat_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);
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 */
179static void
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
197static void
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);
206
207 free(pf->devs_nvlbuf, M_NVLIST);
208 sx_xlock(&pf->lock);
210 sx_xunlock(&pf->lock);
211 sx_destroy(&pf->lock);
212
213 free(pf, M_DEVBUF);
214}
215
216static int
217sndstat_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;
249done:
251 return (err);
252}
253
254static int
255sndstat_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);
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 }
325 return (err);
326}
327
328static void
329sndstat_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;
341 *minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat);
342 return;
343 } else if (!play && d->rvchancount > 0) {
344 *min_rate = *max_rate = d->rvchanrate;
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
379static nvlist_t *
380sndstat_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
396static int
397sndstat_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
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;
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
469done:
470 if (err) {
471 nvlist_destroy(sound4di);
472 nvlist_destroy(di);
473 }
474 return (err);
475}
476
477static int
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
538done:
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 */
548static int
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 if (ent->dev == NULL)
565 continue;
566 d = device_get_softc(ent->dev);
567 if (!PCM_REGISTERED(d))
568 continue;
569
570 err = sndstat_build_sound4_nvlist(d, &di);
571 if (err)
572 goto done;
573
574 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
575 nvlist_destroy(di);
576 err = nvlist_error(nvl);
577 if (err)
578 goto done;
579 }
580
581 TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
582 struct sndstat_userdev *ud;
583
584 sx_xlock(&pf->lock);
585
586 TAILQ_FOREACH(ud, &pf->userdev_list, link) {
587 nvlist_t *di;
588
589 err = sndstat_build_userland_nvlist(ud, &di);
590 if (err != 0) {
591 sx_xunlock(&pf->lock);
592 goto done;
593 }
594 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
595 nvlist_destroy(di);
596
597 err = nvlist_error(nvl);
598 if (err != 0) {
599 sx_xunlock(&pf->lock);
600 goto done;
601 }
602 }
603
604 sx_xunlock(&pf->lock);
605 }
606
607 *nvlp = nvl;
608
609done:
610 if (err != 0)
611 nvlist_destroy(nvl);
612 return (err);
613}
614
615static int
617{
618 sx_xlock(&pf->lock);
619 free(pf->devs_nvlbuf, M_NVLIST);
620 pf->devs_nvlbuf = NULL;
621 pf->devs_nbytes = 0;
622 sx_unlock(&pf->lock);
623
624 return (0);
625}
626
627static int
629{
630 int err;
631 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
632
633 SNDSTAT_LOCK();
634 sx_xlock(&pf->lock);
635
636 if (pf->devs_nvlbuf == NULL) {
637 nvlist_t *nvl;
638 void *nvlbuf;
639 size_t nbytes;
640 int err;
641
642 sx_xunlock(&pf->lock);
643
644 err = sndstat_create_devs_nvlist(&nvl);
645 if (err) {
647 return (err);
648 }
649
650 sx_xlock(&pf->lock);
651
652 nvlbuf = nvlist_pack(nvl, &nbytes);
653 err = nvlist_error(nvl);
654 nvlist_destroy(nvl);
655 if (nvlbuf == NULL || err != 0) {
657 sx_xunlock(&pf->lock);
658 if (err == 0)
659 return (ENOMEM);
660 return (err);
661 }
662
663 free(pf->devs_nvlbuf, M_NVLIST);
664 pf->devs_nvlbuf = nvlbuf;
665 pf->devs_nbytes = nbytes;
666 }
667
669
670 if (!arg->nbytes) {
671 arg->nbytes = pf->devs_nbytes;
672 err = 0;
673 goto done;
674 }
675 if (arg->nbytes < pf->devs_nbytes) {
676 arg->nbytes = 0;
677 err = 0;
678 goto done;
679 }
680
681 err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes);
682 if (err)
683 goto done;
684
685 arg->nbytes = pf->devs_nbytes;
686
687 free(pf->devs_nvlbuf, M_NVLIST);
688 pf->devs_nvlbuf = NULL;
689 pf->devs_nbytes = 0;
690
691done:
692 sx_unlock(&pf->lock);
693 return (err);
694}
695
696static int
697sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
698{
699 void *nvlbuf;
700 int err;
701
702 nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK);
703 err = copyin(unvlbuf, nvlbuf, nbytes);
704 if (err != 0) {
705 free(nvlbuf, M_DEVBUF);
706 return (err);
707 }
708 *nvl = nvlist_unpack(nvlbuf, nbytes, 0);
709 free(nvlbuf, M_DEVBUF);
710 if (nvl == NULL) {
711 return (EINVAL);
712 }
713
714 return (0);
715}
716
717static bool
718sndstat_diinfo_is_sane(const nvlist_t *diinfo)
719{
720 if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) &&
721 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) &&
722 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) &&
723 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) &&
724 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN)))
725 return (false);
726 return (true);
727}
728
729static bool
730sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
731{
732 if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) &&
733 nvlist_exists_string(nvlist, SNDST_DSPS_DESC) &&
734 nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) &&
735 nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN)))
736 return (false);
737
738 if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) {
739 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
740 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
741 SNDST_DSPS_INFO_PLAY)))
742 return (false);
743 } else if (!(nvlist_exists_number(nvlist, "pminrate") &&
744 nvlist_exists_number(nvlist, "pmaxrate") &&
745 nvlist_exists_number(nvlist, "pfmts")))
746 return (false);
747 }
748
749 if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) {
750 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
751 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
752 SNDST_DSPS_INFO_REC)))
753 return (false);
754 } else if (!(nvlist_exists_number(nvlist, "rminrate") &&
755 nvlist_exists_number(nvlist, "rmaxrate") &&
756 nvlist_exists_number(nvlist, "rfmts")))
757 return (false);
758 }
759
760 return (true);
761
762}
763
764static void
765sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate,
766 uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn,
767 uint32_t *max_chn)
768{
769 *min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE);
770 *max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE);
771 *formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS);
772 *min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN);
773 *max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN);
774}
775
776static int
777sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
778{
779 const char *nameunit, *devnode, *desc;
780 unsigned int pchan, rchan;
781 uint32_t pminrate = 0, pmaxrate = 0;
782 uint32_t rminrate = 0, rmaxrate = 0;
783 uint32_t pfmts = 0, rfmts = 0;
784 uint32_t pminchn = 0, pmaxchn = 0;
785 uint32_t rminchn = 0, rmaxchn = 0;
786 nvlist_t *provider_nvl = NULL;
787 const nvlist_t *diinfo;
788 const char *provider;
789
790 devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE);
791 if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT))
792 nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT);
793 else
794 nameunit = devnode;
795 desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC);
796 pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN);
797 rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN);
798 if (pchan != 0) {
799 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
800 diinfo = nvlist_get_nvlist(nvlist,
801 SNDST_DSPS_INFO_PLAY);
802 sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate,
803 &pfmts, &pminchn, &pmaxchn);
804 } else {
805 pminrate = nvlist_get_number(nvlist, "pminrate");
806 pmaxrate = nvlist_get_number(nvlist, "pmaxrate");
807 pfmts = nvlist_get_number(nvlist, "pfmts");
808 }
809 }
810 if (rchan != 0) {
811 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
812 diinfo = nvlist_get_nvlist(nvlist,
813 SNDST_DSPS_INFO_REC);
814 sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate,
815 &rfmts, &rminchn, &rmaxchn);
816 } else {
817 rminrate = nvlist_get_number(nvlist, "rminrate");
818 rmaxrate = nvlist_get_number(nvlist, "rmaxrate");
819 rfmts = nvlist_get_number(nvlist, "rfmts");
820 }
821 }
822
823 provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, "");
824 if (provider[0] == '\0')
825 provider = NULL;
826
827 if (provider != NULL &&
828 nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) {
829 provider_nvl = nvlist_clone(
830 nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO));
831 if (provider_nvl == NULL)
832 return (ENOMEM);
833 }
834
835 ud->provider = (provider != NULL) ? strdup(provider, M_DEVBUF) : NULL;
836 ud->devnode = strdup(devnode, M_DEVBUF);
837 ud->nameunit = strdup(nameunit, M_DEVBUF);
838 ud->desc = strdup(desc, M_DEVBUF);
839 ud->pchan = pchan;
840 ud->rchan = rchan;
841 ud->info_play.min_rate = pminrate;
842 ud->info_play.max_rate = pmaxrate;
843 ud->info_play.formats = pfmts;
844 ud->info_play.min_chn = pminchn;
845 ud->info_play.max_chn = pmaxchn;
846 ud->info_rec.min_rate = rminrate;
847 ud->info_rec.max_rate = rmaxrate;
848 ud->info_rec.formats = rfmts;
849 ud->info_rec.min_chn = rminchn;
850 ud->info_rec.max_chn = rmaxchn;
851 ud->provider_nvl = provider_nvl;
852 return (0);
853}
854
855static int
857{
858 int err;
859 nvlist_t *nvl = NULL;
860 const nvlist_t * const *dsps;
861 size_t i, ndsps;
862 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
863
864 if ((pf->fflags & FWRITE) == 0) {
865 err = EPERM;
866 goto done;
867 }
868
869 err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl);
870 if (err != 0)
871 goto done;
872
873 if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) {
874 err = EINVAL;
875 goto done;
876 }
877 dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps);
878 for (i = 0; i < ndsps; i++) {
879 if (!sndstat_dsp_nvlist_is_sane(dsps[i])) {
880 err = EINVAL;
881 goto done;
882 }
883 }
884 sx_xlock(&pf->lock);
885 for (i = 0; i < ndsps; i++) {
886 struct sndstat_userdev *ud =
887 malloc(sizeof(*ud), M_DEVBUF, M_WAITOK);
888 err = sndstat_dsp_unpack_nvlist(dsps[i], ud);
889 if (err) {
890 sx_unlock(&pf->lock);
891 goto done;
892 }
893 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
894 }
895 sx_unlock(&pf->lock);
896
897done:
898 nvlist_destroy(nvl);
899 return (err);
900}
901
902static int
904{
905 if ((pf->fflags & FWRITE) == 0)
906 return (EPERM);
907
908 sx_xlock(&pf->lock);
910 sx_xunlock(&pf->lock);
911
912 return (0);
913}
914
915#ifdef COMPAT_FREEBSD32
916static int
917compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
918{
919 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
920 struct sndstioc_nv_arg arg;
921 int err;
922
923 arg.buf = (void *)(uintptr_t)arg32->buf;
924 arg.nbytes = arg32->nbytes;
925
926 err = sndstat_get_devs(pf, (caddr_t)&arg);
927 if (err == 0) {
928 arg32->buf = (uint32_t)(uintptr_t)arg.buf;
929 arg32->nbytes = arg.nbytes;
930 }
931
932 return (err);
933}
934
935static int
936compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
937{
938 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
939 struct sndstioc_nv_arg arg;
940 int err;
941
942 arg.buf = (void *)(uintptr_t)arg32->buf;
943 arg.nbytes = arg32->nbytes;
944
945 err = sndstat_add_user_devs(pf, (caddr_t)&arg);
946 if (err == 0) {
947 arg32->buf = (uint32_t)(uintptr_t)arg.buf;
948 arg32->nbytes = arg.nbytes;
949 }
950
951 return (err);
952}
953#endif
954
955static int
957 struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
958{
959 int err;
960 struct sndstat_file *pf;
961
962 err = devfs_get_cdevpriv((void **)&pf);
963 if (err != 0)
964 return (err);
965
966 switch (cmd) {
967 case SNDSTIOC_GET_DEVS:
968 err = sndstat_get_devs(pf, data);
969 break;
970#ifdef COMPAT_FREEBSD32
971 case SNDSTIOC_GET_DEVS32:
972 if (!SV_CURPROC_FLAG(SV_ILP32)) {
973 err = ENODEV;
974 break;
975 }
976 err = compat_sndstat_get_devs32(pf, data);
977 break;
978#endif
979 case SNDSTIOC_ADD_USER_DEVS:
981 break;
982#ifdef COMPAT_FREEBSD32
983 case SNDSTIOC_ADD_USER_DEVS32:
984 if (!SV_CURPROC_FLAG(SV_ILP32)) {
985 err = ENODEV;
986 break;
987 }
988 err = compat_sndstat_add_user_devs32(pf, data);
989 break;
990#endif
991 case SNDSTIOC_REFRESH_DEVS:
993 break;
994 case SNDSTIOC_FLUSH_USER_DEVS:
996 break;
997 default:
998 err = ENODEV;
999 }
1000
1001 return (err);
1002}
1003
1004static struct sndstat_userdev *
1005sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
1006{
1007 struct sndstat_userdev *ud;
1008 const char *e, *m;
1009
1010 ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO);
1011
1012 ud->provider = NULL;
1013 ud->provider_nvl = NULL;
1014 e = strchr(line, ':');
1015 if (e == NULL)
1016 goto fail;
1017 ud->nameunit = strndup(line, e - line, M_DEVBUF);
1018 ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO);
1019 strlcat(ud->devnode, ud->nameunit, e - line + 1);
1020 line = e + 1;
1021
1022 e = strchr(line, '<');
1023 if (e == NULL)
1024 goto fail;
1025 line = e + 1;
1026 e = strrchr(line, '>');
1027 if (e == NULL)
1028 goto fail;
1029 ud->desc = strndup(line, e - line, M_DEVBUF);
1030 line = e + 1;
1031
1032 e = strchr(line, '(');
1033 if (e == NULL)
1034 goto fail;
1035 line = e + 1;
1036 e = strrchr(line, ')');
1037 if (e == NULL)
1038 goto fail;
1039 m = strstr(line, "play");
1040 if (m != NULL && m < e)
1041 ud->pchan = 1;
1042 m = strstr(line, "rec");
1043 if (m != NULL && m < e)
1044 ud->rchan = 1;
1045
1046 return (ud);
1047
1048fail:
1049 free(ud->nameunit, M_DEVBUF);
1050 free(ud->devnode, M_DEVBUF);
1051 free(ud->desc, M_DEVBUF);
1052 free(ud, M_DEVBUF);
1053 return (NULL);
1054}
1055
1056/************************************************************************/
1057
1058int
1059sndstat_register(device_t dev, char *str, sndstat_handler handler)
1060{
1061 struct sndstat_entry *ent;
1062 struct sndstat_entry *pre;
1063 const char *devtype;
1064 int type, unit;
1065
1066 if (dev) {
1067 unit = device_get_unit(dev);
1068 devtype = device_get_name(dev);
1069 if (!strcmp(devtype, "pcm"))
1070 type = SS_TYPE_PCM;
1071 else if (!strcmp(devtype, "midi"))
1073 else if (!strcmp(devtype, "sequencer"))
1075 else
1076 return (EINVAL);
1077 } else {
1079 unit = -1;
1080 }
1081
1082 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
1083 ent->dev = dev;
1084 ent->str = str;
1085 ent->type = type;
1086 ent->unit = unit;
1087 ent->handler = handler;
1088
1089 SNDSTAT_LOCK();
1090 /* sorted list insertion */
1091 TAILQ_FOREACH(pre, &sndstat_devlist, link) {
1092 if (pre->unit > unit)
1093 break;
1094 else if (pre->unit < unit)
1095 continue;
1096 if (pre->type > type)
1097 break;
1098 else if (pre->type < unit)
1099 continue;
1100 }
1101 if (pre == NULL) {
1102 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
1103 } else {
1104 TAILQ_INSERT_BEFORE(pre, ent, link);
1105 }
1107
1108 return (0);
1109}
1110
1111int
1113{
1114 return (sndstat_register(NULL, str, NULL));
1115}
1116
1117int
1119{
1120 struct sndstat_entry *ent;
1121 int error = ENXIO;
1122
1123 SNDSTAT_LOCK();
1124 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1125 if (ent->dev == dev) {
1126 TAILQ_REMOVE(&sndstat_devlist, ent, link);
1127 free(ent, M_DEVBUF);
1128 error = 0;
1129 break;
1130 }
1131 }
1133
1134 return (error);
1135}
1136
1137int
1139{
1140 struct sndstat_entry *ent;
1141 int error = ENXIO;
1142
1143 SNDSTAT_LOCK();
1144 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1145 if (ent->dev == NULL && ent->str == str) {
1146 TAILQ_REMOVE(&sndstat_devlist, ent, link);
1147 free(ent, M_DEVBUF);
1148 error = 0;
1149 break;
1150 }
1151 }
1153
1154 return (error);
1155}
1156
1157/************************************************************************/
1158
1159static int
1161{
1162 struct sbuf *s = &pf_self->sbuf;
1163 struct sndstat_entry *ent;
1164 struct snddev_info *d;
1165 struct sndstat_file *pf;
1166 int k;
1167
1168 /* make sure buffer is reset */
1169 sbuf_clear(s);
1170
1171 if (snd_verbose > 0) {
1172 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
1173 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
1174 MACHINE_ARCH);
1175 }
1176
1177 /* generate list of installed devices */
1178 k = 0;
1179 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1180 if (ent->dev == NULL)
1181 continue;
1182 d = device_get_softc(ent->dev);
1183 if (!PCM_REGISTERED(d))
1184 continue;
1185 if (!k++)
1186 sbuf_printf(s, "Installed devices:\n");
1187 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
1188 sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
1189 if (snd_verbose > 0)
1190 sbuf_printf(s, " %s", ent->str);
1191 if (ent->handler) {
1192 /* XXX Need Giant magic entry ??? */
1194 ent->handler(s, ent->dev, snd_verbose);
1196 }
1197 sbuf_printf(s, "\n");
1198 }
1199 if (k == 0)
1200 sbuf_printf(s, "No devices installed.\n");
1201
1202 /* append any input from userspace */
1203 k = 0;
1204 TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
1205 struct sndstat_userdev *ud;
1206
1207 if (pf == pf_self)
1208 continue;
1209 sx_xlock(&pf->lock);
1210 if (TAILQ_EMPTY(&pf->userdev_list)) {
1211 sx_unlock(&pf->lock);
1212 continue;
1213 }
1214 if (!k++)
1215 sbuf_printf(s, "Installed devices from userspace:\n");
1216 TAILQ_FOREACH(ud, &pf->userdev_list, link) {
1217 const char *caps = (ud->pchan && ud->rchan) ?
1218 "play/rec" :
1219 (ud->pchan ? "play" : (ud->rchan ? "rec" : ""));
1220 sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc);
1221 sbuf_printf(s, " (%s)", caps);
1222 sbuf_printf(s, "\n");
1223 }
1224 sx_unlock(&pf->lock);
1225 }
1226 if (k == 0)
1227 sbuf_printf(s, "No devices installed from userspace.\n");
1228
1229 /* append any file versions */
1230 if (snd_verbose >= 3) {
1231 k = 0;
1232 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
1233 if (ent->dev == NULL && ent->str != NULL) {
1234 if (!k++)
1235 sbuf_printf(s, "\nFile Versions:\n");
1236 sbuf_printf(s, "%s\n", ent->str);
1237 }
1238 }
1239 if (k == 0)
1240 sbuf_printf(s, "\nNo file versions.\n");
1241 }
1242 sbuf_finish(s);
1243 return (sbuf_len(s));
1244}
1245
1246static void
1248{
1249 sx_init(&sndstat_lock, "sndstat lock");
1251 UID_ROOT, GID_WHEEL, 0644, "sndstat");
1252}
1253SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
1254
1255static void
1257{
1258 if (sndstat_dev != NULL) {
1259 /* destroy_dev() will wait for all references to go away */
1260 destroy_dev(sndstat_dev);
1261 }
1262 sx_destroy(&sndstat_lock);
1263}
1264SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
u_int32_t data
Definition: ac97_if.m:60
char * desc
Definition: atiixp.c:174
struct pcmchan_caps * chn_getcaps(struct pcm_channel *c)
Definition: channel.c:2277
#define CHN_UNLOCK(c)
Definition: channel.h:322
#define CHN_LOCK(c)
Definition: channel.h:321
#define PCMDIR_PLAY
Definition: channel.h:339
#define CHN_FOREACH(x, y, z)
Definition: channel.h:181
#define PCMDIR_REC
Definition: channel.h:341
#define CHN_F_VIRTUAL
Definition: channel.h:376
struct pcm_channel * c
Definition: channel_if.m:106
METHOD int free
Definition: channel_if.m:110
struct pcmchan_matrix * m
Definition: channel_if.m:232
int type
Definition: dsp.c:386
int max
Definition: dsp.c:392
uint16_t len
static u_int32_t fmts[]
Definition: fm801.c:112
int dir
Definition: hdac_if.m:45
bus_addr_t buf
Definition: hdac_if.m:63
uint8_t k
uint16_t fail
struct @109 error
unsigned dev
Definition: mixer_if.m:59
METHOD const char * provider
Definition: mpu_if.m:60
device_t pf
int32_t intpcm32_t
Definition: pcm.h:70
SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_MPSAFE, 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level")
SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL)
static int sndstat_prepare(struct sndstat_file *pf_self)
Definition: sndstat.c:1160
static int sndstat_refresh_devs(struct sndstat_file *pf)
Definition: sndstat.c:616
#define SS_TYPE_MODULE
Definition: sndstat.c:55
#define SS_TYPE_MIDI
Definition: sndstat.c:57
static int sndstat_create_devs_nvlist(nvlist_t **nvlp)
Definition: sndstat.c:549
static struct cdev * sndstat_dev
Definition: sndstat.c:115
#define SS_TYPE_SEQUENCER
Definition: sndstat.c:58
static int sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
Definition: sndstat.c:397
#define SNDSTAT_UNLOCK()
Definition: sndstat.c:118
static int sndstat_flush_user_devs(struct sndstat_file *pf)
Definition: sndstat.c:903
static struct sndstat_userdev * sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
Definition: sndstat.c:1005
static nvlist_t * sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats, uint32_t min_chn, uint32_t max_chn)
Definition: sndstat.c:380
static void sndstat_sysuninit(void *p)
Definition: sndstat.c:1256
static int sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
Definition: sndstat.c:628
static void sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate, uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn, uint32_t *max_chn)
Definition: sndstat.c:765
static bool sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
Definition: sndstat.c:730
#define SS_TYPE_PCM
Definition: sndstat.c:56
static int sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
Definition: sndstat.c:697
int sndstat_unregisterfile(char *str)
Definition: sndstat.c:1138
static TAILQ_HEAD(sndstat_entry)
Definition: sndstat.c:120
SND_DECLARE_FILE("$FreeBSD$")
int sndstat_registerfile(char *str)
Definition: sndstat.c:1112
static d_ioctl_t sndstat_ioctl
Definition: sndstat.c:64
static d_write_t sndstat_write
Definition: sndstat.c:63
static void sndstat_close(void *)
Definition: sndstat.c:198
static int sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip)
Definition: sndstat.c:478
int sndstat_unregister(device_t dev)
Definition: sndstat.c:1118
static struct cdevsw sndstat_cdevsw
Definition: sndstat.c:66
static bool sndstat_diinfo_is_sane(const nvlist_t *diinfo)
Definition: sndstat.c:718
static int sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
Definition: sndstat.c:856
static d_open_t sndstat_open
Definition: sndstat.c:60
static d_read_t sndstat_read
Definition: sndstat.c:62
int sndstat_register(device_t dev, char *str, sndstat_handler handler)
Definition: sndstat.c:1059
static void sndstat_sysinit(void *p)
Definition: sndstat.c:1247
static struct sx sndstat_lock
Definition: sndstat.c:114
static void sndstat_remove_all_userdevs(struct sndstat_file *pf)
Definition: sndstat.c:180
SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL)
static void sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate, uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
Definition: sndstat.c:329
static int sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
Definition: sndstat.c:777
#define SNDSTAT_LOCK()
Definition: sndstat.c:117
#define AFMT_ENCODING(v)
Definition: sound.h:222
#define SND_DEV_STATUS
Definition: sound.h:267
#define PCM_RELEASE_QUICK(x)
Definition: sound.h:573
#define SD_F_BITPERFECT
Definition: sound.h:140
#define AFMT_CHANNEL(v)
Definition: sound.h:227
int snd_verbose
#define PCM_ACQUIRE_QUICK(x)
Definition: sound.h:565
int(* sndstat_handler)(struct sbuf *s, device_t dev, int verbose)
Definition: sound.h:350
#define PCM_REGISTERED(x)
Definition: sound.h:178
u_int32_t flags
Definition: channel.h:96
struct pcm_channel::@28 channels
int direction
Definition: channel.h:100
u_int32_t caps
Definition: channel.h:36
unsigned playcount
Definition: sound.h:395
unsigned rvchancount
Definition: sound.h:395
unsigned flags
Definition: sound.h:396
unsigned pvchancount
Definition: sound.h:395
uint32_t pvchanformat
Definition: sound.h:404
unsigned reccount
Definition: sound.h:395
uint32_t rvchanformat
Definition: sound.h:405
uint32_t rvchanrate
Definition: sound.h:405
uint32_t pvchanrate
Definition: sound.h:404
device_t dev
Definition: sound.h:400
Definition: sndstat.c:76
TAILQ_ENTRY(sndstat_userdev)
Definition: sndstat.c:85
nvlist_t * provider_nvl
Definition: sndstat.c:99
const void * req
#define SND_DRV_VERSION
Definition: version.h:42