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,
104 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
105 sysctl_hw_sndverbose, "I",
109 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
111 struct sndstat_file *pf;
113 pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
116 if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
121 TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
124 devfs_set_cdevpriv(pf, &sndstat_close);
130 sndstat_close(void *sndstat_file)
132 struct sndstat_file *pf = (struct sndstat_file *)sndstat_file;
135 sbuf_delete(&pf->sbuf);
136 TAILQ_REMOVE(&sndstat_filelist, pf, entry);
143 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
145 struct sndstat_file *pf;
149 err = devfs_get_cdevpriv((void **)&pf);
153 /* skip zero-length reads */
154 if (buf->uio_resid == 0)
158 if (pf->out_offset != 0) {
159 /* don't allow both reading and writing */
162 } else if (pf->in_offset == 0) {
163 err = sndstat_prepare(pf);
169 len = sbuf_len(&pf->sbuf) - pf->in_offset;
170 if (len > buf->uio_resid)
171 len = buf->uio_resid;
173 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf);
174 pf->in_offset += len;
181 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
183 struct sndstat_file *pf;
188 err = devfs_get_cdevpriv((void **)&pf);
192 /* skip zero-length writes */
193 if (buf->uio_resid == 0)
196 /* don't allow writing more than 64Kbytes */
197 if (buf->uio_resid > 65536)
201 if (pf->in_offset != 0) {
202 /* don't allow both reading and writing */
205 /* only remember the last write - allows for updates */
206 sbuf_clear(&pf->sbuf);
209 if (len > buf->uio_resid)
210 len = buf->uio_resid;
212 err = uiomove(temp, len, buf);
218 if (sbuf_bcat(&pf->sbuf, temp, len) < 0) {
223 sbuf_finish(&pf->sbuf);
225 pf->out_offset = sbuf_len(&pf->sbuf);
233 /************************************************************************/
236 sndstat_register(device_t dev, char *str, sndstat_handler handler)
238 struct sndstat_entry *ent;
239 struct sndstat_entry *pre;
244 unit = device_get_unit(dev);
245 devtype = device_get_name(dev);
246 if (!strcmp(devtype, "pcm"))
248 else if (!strcmp(devtype, "midi"))
250 else if (!strcmp(devtype, "sequencer"))
251 type = SS_TYPE_SEQUENCER;
255 type = SS_TYPE_MODULE;
259 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
264 ent->handler = handler;
267 /* sorted list insertion */
268 TAILQ_FOREACH(pre, &sndstat_devlist, link) {
269 if (pre->unit > unit)
271 else if (pre->unit < unit)
273 if (pre->type > type)
275 else if (pre->type < unit)
279 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
281 TAILQ_INSERT_BEFORE(pre, ent, link);
289 sndstat_registerfile(char *str)
291 return (sndstat_register(NULL, str, NULL));
295 sndstat_unregister(device_t dev)
297 struct sndstat_entry *ent;
301 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
302 if (ent->dev == dev) {
303 TAILQ_REMOVE(&sndstat_devlist, ent, link);
315 sndstat_unregisterfile(char *str)
317 struct sndstat_entry *ent;
321 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
322 if (ent->dev == NULL && ent->str == str) {
323 TAILQ_REMOVE(&sndstat_devlist, ent, link);
334 /************************************************************************/
337 sndstat_prepare(struct sndstat_file *pf_self)
339 struct sbuf *s = &pf_self->sbuf;
340 struct sndstat_entry *ent;
341 struct snddev_info *d;
342 struct sndstat_file *pf;
345 /* make sure buffer is reset */
348 if (snd_verbose > 0) {
349 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
350 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
354 /* generate list of installed devices */
356 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
357 if (ent->dev == NULL)
359 d = device_get_softc(ent->dev);
360 if (!PCM_REGISTERED(d))
363 sbuf_printf(s, "Installed devices:\n");
364 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
365 sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
367 sbuf_printf(s, " %s", ent->str);
369 /* XXX Need Giant magic entry ??? */
370 PCM_ACQUIRE_QUICK(d);
371 ent->handler(s, ent->dev, snd_verbose);
372 PCM_RELEASE_QUICK(d);
374 sbuf_printf(s, "\n");
377 sbuf_printf(s, "No devices installed.\n");
379 /* append any input from userspace */
381 TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
384 if (pf->out_offset == 0)
387 sbuf_printf(s, "Installed devices from userspace:\n");
388 sbuf_bcat(s, sbuf_data(&pf->sbuf),
389 sbuf_len(&pf->sbuf));
392 sbuf_printf(s, "No devices installed from userspace.\n");
394 /* append any file versions */
395 if (snd_verbose >= 3) {
397 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
398 if (ent->dev == NULL && ent->str != NULL) {
400 sbuf_printf(s, "\nFile Versions:\n");
401 sbuf_printf(s, "%s\n", ent->str);
405 sbuf_printf(s, "\nNo file versions.\n");
408 return (sbuf_len(s));
412 sndstat_sysinit(void *p)
414 sx_init(&sndstat_lock, "sndstat lock");
415 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
416 UID_ROOT, GID_WHEEL, 0644, "sndstat");
418 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
421 sndstat_sysuninit(void *p)
423 if (sndstat_dev != NULL) {
424 /* destroy_dev() will wait for all references to go away */
425 destroy_dev(sndstat_dev);
427 sx_destroy(&sndstat_lock);
429 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);