xref: /freebsd/sys/dev/sound/pcm/sndstat.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <dev/sound/pcm/sound.h>
30 #include <dev/sound/pcm/vchan.h>
31 #include <sys/sbuf.h>
32 
33 #include "feeder_if.h"
34 
35 static d_open_t sndstat_open;
36 static d_close_t sndstat_close;
37 static d_read_t sndstat_read;
38 
39 static 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 
55 static struct sbuf sndstat_sbuf;
56 static dev_t sndstat_dev = 0;
57 static int sndstat_isopen = 0;
58 static int sndstat_bufptr;
59 
60 static int sndstat_verbose = 0;
61 #ifdef	USING_MUTEX
62 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
63 #else
64 TUNABLE_INT_DECL("hw.snd.verbose", 0, sndstat_verbose);
65 #endif
66 
67 static int sndstat_prepare(struct sbuf *s);
68 
69 static int
70 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
71 {
72 	int error, verbose;
73 
74 	verbose = sndstat_verbose;
75 	error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
76 	if (error == 0 && req->newptr != NULL) {
77 		if (verbose == 0 || verbose == 1)
78 			sndstat_verbose = verbose;
79 		else
80 			error = EINVAL;
81 	}
82 	return error;
83 }
84 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
85             0, sizeof(int), sysctl_hw_sndverbose, "I", "");
86 
87 static int
88 sndstat_open(dev_t i_dev, int flags, int mode, struct proc *p)
89 {
90 	intrmask_t s;
91 	int err;
92 
93 	s = spltty();
94 	if (sndstat_isopen) {
95 		splx(s);
96 		return EBUSY;
97 	}
98 	if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
99 		splx(s);
100 		return ENXIO;
101 	}
102 	sndstat_bufptr = 0;
103 	err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM;
104 	if (!err)
105 		sndstat_isopen = 1;
106 
107 	splx(s);
108 	return err;
109 }
110 
111 static int
112 sndstat_close(dev_t i_dev, int flags, int mode, struct proc *p)
113 {
114 	intrmask_t s;
115 
116 	s = spltty();
117 	if (!sndstat_isopen) {
118 		splx(s);
119 		return EBADF;
120 	}
121 	sbuf_delete(&sndstat_sbuf);
122 	sndstat_isopen = 0;
123 
124 	splx(s);
125 	return 0;
126 }
127 
128 static int
129 sndstat_read(dev_t i_dev, struct uio *buf, int flag)
130 {
131 	intrmask_t s;
132 	int l, err;
133 
134 	s = spltty();
135 	if (!sndstat_isopen) {
136 		splx(s);
137 		return EBADF;
138 	}
139     	l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
140 	err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
141 	sndstat_bufptr += l;
142 
143 	splx(s);
144 	return err;
145 }
146 
147 static int
148 sndstat_prepare(struct sbuf *s)
149 {
150     	int i, pc, rc, vc;
151     	device_t dev;
152     	struct snddev_info *d;
153     	struct snddev_channel *sce;
154 	struct pcm_channel *c;
155 	struct pcm_feeder *f;
156 
157 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\n", __DATE__, __TIME__);
158 	if (!pcm_devclass || devclass_get_maxunit(pcm_devclass) == 0) {
159 		sbuf_printf(s, "No devices installed.\n");
160 		sbuf_finish(s);
161     		return sbuf_len(s);
162 	} else
163 		sbuf_printf(s, "Installed devices:\n");
164 
165     	for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
166 		d = devclass_get_softc(pcm_devclass, i);
167 		if (!d)
168 			continue;
169 		snd_mtxlock(d->lock);
170 		dev = devclass_get_device(pcm_devclass, i);
171 		sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
172 		if (!SLIST_EMPTY(&d->channels)) {
173 			pc = rc = vc = 0;
174 			SLIST_FOREACH(sce, &d->channels, link) {
175 				c = sce->channel;
176 				if (c->direction == PCMDIR_PLAY) {
177 					if (c->flags & CHN_F_VIRTUAL)
178 						vc++;
179 					else
180 						pc++;
181 				} else
182 					rc++;
183 			}
184 			sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
185 					(d->flags & SD_F_SIMPLEX)? "" : " duplex",
186 #ifdef USING_DEVFS
187 					(i == snd_unit)? " default" : ""
188 #else
189 					""
190 #endif
191 					);
192 			if (!sndstat_verbose)
193 				goto skipverbose;
194 			SLIST_FOREACH(sce, &d->channels, link) {
195 				c = sce->channel;
196 				sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
197 					c->parentchannel? c->parentchannel->name : "",
198 					c->name, c->speed, c->format, c->flags);
199 				if (c->pid != -1)
200 					sbuf_printf(s, ", pid %d", c->pid);
201 				sbuf_printf(s, "\n\t");
202 				f = c->feeder;
203 				while (f) {
204 					sbuf_printf(s, "%s", f->class->name);
205 					if (f->desc->type == FEEDER_FMT)
206 						sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
207 					if (f->desc->type == FEEDER_RATE)
208 						sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
209 					if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
210 						sbuf_printf(s, "(%08x)", f->desc->out);
211 					if (f->source)
212 						sbuf_printf(s, " <- ");
213 					f = f->source;
214 				}
215 				sbuf_printf(s, "\n");
216 			}
217 skipverbose:
218 		} else
219 			sbuf_printf(s, " (mixer only)\n");
220 		snd_mtxunlock(d->lock);
221     	}
222 	sbuf_finish(s);
223     	return sbuf_len(s);
224 }
225 
226 static int
227 sndstat_init(void)
228 {
229 	sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
230 
231 	return (sndstat_dev != 0)? 0 : ENXIO;
232 }
233 
234 static int
235 sndstat_uninit(void)
236 {
237 	intrmask_t s;
238 
239 	s = spltty();
240 	if (sndstat_isopen) {
241 		splx(s);
242 		return EBUSY;
243 	}
244 
245 	if (sndstat_dev)
246 		destroy_dev(sndstat_dev);
247 	sndstat_dev = 0;
248 
249 	splx(s);
250 	return 0;
251 }
252 
253 static void
254 sndstat_sysinit(void *p)
255 {
256 	sndstat_init();
257 }
258 
259 static void
260 sndstat_sysuninit(void *p)
261 {
262 	sndstat_uninit();
263 }
264 
265 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysinit, NULL);
266 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysuninit, NULL);
267 
268 
269