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