sndstat.c revision 78396
1133066Sdfr/*
2133066Sdfr * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
3133066Sdfr * All rights reserved.
4133066Sdfr *
5133066Sdfr * Redistribution and use in source and binary forms, with or without
6133066Sdfr * modification, are permitted provided that the following conditions
7133066Sdfr * are met:
8133066Sdfr * 1. Redistributions of source code must retain the above copyright
9133066Sdfr *    notice, this list of conditions and the following disclaimer.
10133066Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11133066Sdfr *    notice, this list of conditions and the following disclaimer in the
12133066Sdfr *    documentation and/or other materials provided with the distribution.
13133066Sdfr *
14133066Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15133066Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133066Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133066Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18133066Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133066Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133066Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133066Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133066Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133066Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133066Sdfr * SUCH DAMAGE.
25133066Sdfr *
26133066Sdfr * $FreeBSD: head/sys/dev/sound/pcm/sndstat.c 78396 2001-06-18 00:10:47Z cg $
27133066Sdfr */
28133066Sdfr
29133066Sdfr#include <dev/sound/pcm/sound.h>
30133066Sdfr#include <dev/sound/pcm/vchan.h>
31133066Sdfr#include <sys/sbuf.h>
32133066Sdfr
33133066Sdfr#include "feeder_if.h"
34133066Sdfr
35133066Sdfrstatic d_open_t sndstat_open;
36133066Sdfrstatic d_close_t sndstat_close;
37static d_read_t sndstat_read;
38
39static struct cdevsw sndstat_cdevsw = {
40	/* open */	sndstat_open,
41	/* close */	sndstat_close,
42	/* read */	sndstat_read,
43	/* write */	nowrite,
44	/* ioctl */	noioctl,
45	/* poll */	nopoll,
46	/* mmap */	nommap,
47	/* strategy */	nostrategy,
48	/* name */	"sndstat",
49	/* maj */	SND_CDEV_MAJOR,
50	/* dump */	nodump,
51	/* psize */	nopsize,
52	/* flags */	0,
53};
54
55static struct sbuf sndstat_sbuf;
56static dev_t sndstat_dev = 0;
57static int sndstat_isopen = 0;
58static int sndstat_bufptr;
59
60static int sndstat_verbose = 0;
61TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
62
63static int sndstat_prepare(struct sbuf *s);
64
65static int
66sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
67{
68	int error, verbose;
69
70	verbose = sndstat_verbose;
71	error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
72	if (error == 0 && req->newptr != NULL) {
73		if (verbose == 0 || verbose == 1)
74			sndstat_verbose = verbose;
75		else
76			error = EINVAL;
77	}
78	return error;
79}
80SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
81            0, sizeof(int), sysctl_hw_sndverbose, "I", "");
82
83static int
84sndstat_open(dev_t i_dev, int flags, int mode, struct proc *p)
85{
86	int err;
87
88	if (sndstat_isopen)
89		return EBUSY;
90	if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL)
91		return ENXIO;
92	sndstat_bufptr = 0;
93	err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM;
94	if (!err)
95		sndstat_isopen = 1;
96
97	return err;
98}
99
100static int
101sndstat_close(dev_t i_dev, int flags, int mode, struct proc *p)
102{
103	if (!sndstat_isopen)
104		return EBADF;
105	sbuf_delete(&sndstat_sbuf);
106	sndstat_isopen = 0;
107
108	return 0;
109}
110
111static int
112sndstat_read(dev_t i_dev, struct uio *buf, int flag)
113{
114	int l, err;
115
116	if (!sndstat_isopen)
117		return EBADF;
118    	l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
119	err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
120	sndstat_bufptr += l;
121
122	return err;
123}
124
125static int
126sndstat_prepare(struct sbuf *s)
127{
128    	int i, pc, rc, vc;
129    	device_t dev;
130    	struct snddev_info *d;
131    	struct snddev_channel *sce;
132	struct pcm_channel *c;
133	struct pcm_feeder *f;
134
135	sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\n", __DATE__, __TIME__);
136	if (!pcm_devclass || devclass_get_maxunit(pcm_devclass) == 0) {
137		sbuf_printf(s, "No devices installed.\n");
138		sbuf_finish(s);
139    		return sbuf_len(s);
140	} else
141		sbuf_printf(s, "Installed devices:\n");
142
143    	for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
144		d = devclass_get_softc(pcm_devclass, i);
145		if (!d)
146			continue;
147		snd_mtxlock(d->lock);
148		dev = devclass_get_device(pcm_devclass, i);
149		sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
150		if (!SLIST_EMPTY(&d->channels)) {
151			pc = rc = vc = 0;
152			SLIST_FOREACH(sce, &d->channels, link) {
153				c = sce->channel;
154				if (c->direction == PCMDIR_PLAY) {
155					if (c->flags & CHN_F_VIRTUAL)
156						vc++;
157					else
158						pc++;
159				} else
160					rc++;
161			}
162			sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
163					(d->flags & SD_F_SIMPLEX)? "" : " duplex",
164#ifdef USING_DEVFS
165					(i == snd_unit)? " default" : ""
166#else
167					""
168#endif
169					);
170			if (!sndstat_verbose)
171				goto skipverbose;
172			SLIST_FOREACH(sce, &d->channels, link) {
173				c = sce->channel;
174				sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
175					c->parentchannel? c->parentchannel->name : "",
176					c->name, c->speed, c->format, c->flags);
177				if (c->pid != -1)
178					sbuf_printf(s, ", pid %d", c->pid);
179				sbuf_printf(s, "\n\t");
180				f = c->feeder;
181				while (f) {
182					sbuf_printf(s, "%s", f->class->name);
183					if (f->desc->type == FEEDER_FMT)
184						sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
185					if (f->desc->type == FEEDER_RATE)
186						sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
187					if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
188						sbuf_printf(s, "(%08x)", f->desc->out);
189					if (f->source)
190						sbuf_printf(s, " <- ");
191					f = f->source;
192				}
193				sbuf_printf(s, "\n");
194			}
195skipverbose:
196		} else
197			sbuf_printf(s, " (mixer only)\n");
198		snd_mtxunlock(d->lock);
199    	}
200	sbuf_finish(s);
201    	return sbuf_len(s);
202}
203
204static int
205sndstat_init(void)
206{
207	sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
208
209	return (sndstat_dev != 0)? 0 : ENXIO;
210}
211
212static int
213sndstat_uninit(void)
214{
215	if (sndstat_isopen)
216		return EBUSY;
217
218	if (sndstat_dev)
219		destroy_dev(sndstat_dev);
220	sndstat_dev = 0;
221
222	return 0;
223}
224
225static void
226sndstat_sysinit(void *p)
227{
228	sndstat_init();
229}
230
231static void
232sndstat_sysuninit(void *p)
233{
234	sndstat_uninit();
235}
236
237SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysinit, NULL);
238SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysuninit, NULL);
239
240
241