2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
5 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #ifdef HAVE_KERNEL_OPTION_HEADERS
34 #include <dev/sound/pcm/sound.h>
35 #include <dev/sound/pcm/pcm.h>
36 #include <dev/sound/version.h>
39 SND_DECLARE_FILE("$FreeBSD$");
41 #define SS_TYPE_MODULE 0
43 #define SS_TYPE_MIDI 2
44 #define SS_TYPE_SEQUENCER 3
46 static d_open_t sndstat_open;
47 static void sndstat_close(void *);
48 static d_read_t sndstat_read;
49 static d_write_t sndstat_write;
51 static struct cdevsw sndstat_cdevsw = {
52 .d_version = D_VERSION,
53 .d_open = sndstat_open,
54 .d_read = sndstat_read,
55 .d_write = sndstat_write,
57 .d_flags = D_TRACKCLOSE,
60 struct sndstat_entry {
61 TAILQ_ENTRY(sndstat_entry) link;
64 sndstat_handler handler;
69 TAILQ_ENTRY(sndstat_file) entry;
75 static struct sx sndstat_lock;
76 static struct cdev *sndstat_dev;
78 #define SNDSTAT_LOCK() sx_xlock(&sndstat_lock)
79 #define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock)
81 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist);
82 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist);
86 static int sndstat_prepare(struct sndstat_file *);
89 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
93 verbose = snd_verbose;
94 error = sysctl_handle_int(oidp, &verbose, 0, req);
95 if (error == 0 && req->newptr != NULL) {
96 if (verbose < 0 || verbose > 4)
99 snd_verbose = verbose;
103 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RWTUN,
104 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level");
107 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
109 struct sndstat_file *pf;
111 pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
114 if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
119 TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
122 devfs_set_cdevpriv(pf, &sndstat_close);
128 sndstat_close(void *sndstat_file)
130 struct sndstat_file *pf = (struct sndstat_file *)sndstat_file;
133 sbuf_delete(&pf->sbuf);
134 TAILQ_REMOVE(&sndstat_filelist, pf, entry);
141 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
143 struct sndstat_file *pf;
147 err = devfs_get_cdevpriv((void **)&pf);
151 /* skip zero-length reads */
152 if (buf->uio_resid == 0)
156 if (pf->out_offset != 0) {
157 /* don't allow both reading and writing */
160 } else if (pf->in_offset == 0) {
161 err = sndstat_prepare(pf);
167 len = sbuf_len(&pf->sbuf) - pf->in_offset;
168 if (len > buf->uio_resid)
169 len = buf->uio_resid;
171 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf);
172 pf->in_offset += len;
179 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
181 struct sndstat_file *pf;
186 err = devfs_get_cdevpriv((void **)&pf);
190 /* skip zero-length writes */
191 if (buf->uio_resid == 0)
194 /* don't allow writing more than 64Kbytes */
195 if (buf->uio_resid > 65536)
199 if (pf->in_offset != 0) {
200 /* don't allow both reading and writing */
203 /* only remember the last write - allows for updates */
204 sbuf_clear(&pf->sbuf);
207 if (len > buf->uio_resid)
208 len = buf->uio_resid;
210 err = uiomove(temp, len, buf);
216 if (sbuf_bcat(&pf->sbuf, temp, len) < 0) {
221 sbuf_finish(&pf->sbuf);
223 pf->out_offset = sbuf_len(&pf->sbuf);
231 /************************************************************************/
234 sndstat_register(device_t dev, char *str, sndstat_handler handler)
236 struct sndstat_entry *ent;
237 struct sndstat_entry *pre;
242 unit = device_get_unit(dev);
243 devtype = device_get_name(dev);
244 if (!strcmp(devtype, "pcm"))
246 else if (!strcmp(devtype, "midi"))
248 else if (!strcmp(devtype, "sequencer"))
249 type = SS_TYPE_SEQUENCER;
253 type = SS_TYPE_MODULE;
257 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
262 ent->handler = handler;
265 /* sorted list insertion */
266 TAILQ_FOREACH(pre, &sndstat_devlist, link) {
267 if (pre->unit > unit)
269 else if (pre->unit < unit)
271 if (pre->type > type)
273 else if (pre->type < unit)
277 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
279 TAILQ_INSERT_BEFORE(pre, ent, link);
287 sndstat_registerfile(char *str)
289 return (sndstat_register(NULL, str, NULL));
293 sndstat_unregister(device_t dev)
295 struct sndstat_entry *ent;
299 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
300 if (ent->dev == dev) {
301 TAILQ_REMOVE(&sndstat_devlist, ent, link);
313 sndstat_unregisterfile(char *str)
315 struct sndstat_entry *ent;
319 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
320 if (ent->dev == NULL && ent->str == str) {
321 TAILQ_REMOVE(&sndstat_devlist, ent, link);
332 /************************************************************************/
335 sndstat_prepare(struct sndstat_file *pf_self)
337 struct sbuf *s = &pf_self->sbuf;
338 struct sndstat_entry *ent;
339 struct snddev_info *d;
340 struct sndstat_file *pf;
343 /* make sure buffer is reset */
346 if (snd_verbose > 0) {
347 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
348 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
352 /* generate list of installed devices */
354 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
355 if (ent->dev == NULL)
357 d = device_get_softc(ent->dev);
358 if (!PCM_REGISTERED(d))
361 sbuf_printf(s, "Installed devices:\n");
362 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
363 sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
365 sbuf_printf(s, " %s", ent->str);
367 /* XXX Need Giant magic entry ??? */
368 PCM_ACQUIRE_QUICK(d);
369 ent->handler(s, ent->dev, snd_verbose);
370 PCM_RELEASE_QUICK(d);
372 sbuf_printf(s, "\n");
375 sbuf_printf(s, "No devices installed.\n");
377 /* append any input from userspace */
379 TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
382 if (pf->out_offset == 0)
385 sbuf_printf(s, "Installed devices from userspace:\n");
386 sbuf_bcat(s, sbuf_data(&pf->sbuf),
387 sbuf_len(&pf->sbuf));
390 sbuf_printf(s, "No devices installed from userspace.\n");
392 /* append any file versions */
393 if (snd_verbose >= 3) {
395 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
396 if (ent->dev == NULL && ent->str != NULL) {
398 sbuf_printf(s, "\nFile Versions:\n");
399 sbuf_printf(s, "%s\n", ent->str);
403 sbuf_printf(s, "\nNo file versions.\n");
406 return (sbuf_len(s));
410 sndstat_sysinit(void *p)
412 sx_init(&sndstat_lock, "sndstat lock");
413 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
414 UID_ROOT, GID_WHEEL, 0644, "sndstat");
416 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
419 sndstat_sysuninit(void *p)
421 if (sndstat_dev != NULL) {
422 /* destroy_dev() will wait for all references to go away */
423 destroy_dev(sndstat_dev);
425 sx_destroy(&sndstat_lock);
427 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);