xref: /freebsd/sys/dev/sound/pcm/sndstat.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
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 
27 #include <dev/sound/pcm/sound.h>
28 #include <dev/sound/pcm/vchan.h>
29 
30 SND_DECLARE_FILE("$FreeBSD$");
31 
32 #define	SS_TYPE_MODULE		0
33 #define	SS_TYPE_FIRST		1
34 #define	SS_TYPE_PCM		1
35 #define	SS_TYPE_MIDI		2
36 #define	SS_TYPE_SEQUENCER	3
37 #define	SS_TYPE_LAST		3
38 
39 static d_open_t sndstat_open;
40 static d_close_t sndstat_close;
41 static d_read_t sndstat_read;
42 
43 static struct cdevsw sndstat_cdevsw = {
44 	.d_version =	D_VERSION,
45 	.d_flags =	D_NEEDGIANT,
46 	.d_open =	sndstat_open,
47 	.d_close =	sndstat_close,
48 	.d_read =	sndstat_read,
49 	.d_name =	"sndstat",
50 	.d_maj =	SND_CDEV_MAJOR,
51 };
52 
53 struct sndstat_entry {
54 	SLIST_ENTRY(sndstat_entry) link;
55 	device_t dev;
56 	char *str;
57 	sndstat_handler handler;
58 	int type, unit;
59 };
60 
61 #ifdef	USING_MUTEX
62 static struct mtx sndstat_lock;
63 #endif
64 static struct sbuf sndstat_sbuf;
65 static struct cdev *sndstat_dev = 0;
66 static int sndstat_isopen = 0;
67 static int sndstat_bufptr;
68 static int sndstat_maxunit = -1;
69 static int sndstat_files = 0;
70 
71 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
72 
73 static int sndstat_verbose = 1;
74 #ifdef	USING_MUTEX
75 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
76 #else
77 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
78 #endif
79 
80 static int sndstat_prepare(struct sbuf *s);
81 
82 static int
83 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
84 {
85 	intrmask_t s;
86 	int error, verbose;
87 
88 	verbose = sndstat_verbose;
89 	error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
90 	if (error == 0 && req->newptr != NULL) {
91 		s = spltty();
92 		mtx_lock(&sndstat_lock);
93 		if (verbose < 0 || verbose > 3)
94 			error = EINVAL;
95 		else
96 			sndstat_verbose = verbose;
97 		mtx_unlock(&sndstat_lock);
98 		splx(s);
99 	}
100 	return error;
101 }
102 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
103             0, sizeof(int), sysctl_hw_sndverbose, "I", "");
104 
105 static int
106 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
107 {
108 	intrmask_t s;
109 	int error;
110 
111 	s = spltty();
112 	mtx_lock(&sndstat_lock);
113 	if (sndstat_isopen) {
114 		mtx_unlock(&sndstat_lock);
115 		splx(s);
116 		return EBUSY;
117 	}
118 	sndstat_isopen = 1;
119 	mtx_unlock(&sndstat_lock);
120 	splx(s);
121 	if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
122 		error = ENXIO;
123 		goto out;
124 	}
125 	sndstat_bufptr = 0;
126 	error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
127 out:
128 	if (error) {
129 		s = spltty();
130 		mtx_lock(&sndstat_lock);
131 		sndstat_isopen = 0;
132 		mtx_unlock(&sndstat_lock);
133 		splx(s);
134 	}
135 	return (error);
136 }
137 
138 static int
139 sndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
140 {
141 	intrmask_t s;
142 
143 	s = spltty();
144 	mtx_lock(&sndstat_lock);
145 	if (!sndstat_isopen) {
146 		mtx_unlock(&sndstat_lock);
147 		splx(s);
148 		return EBADF;
149 	}
150 	sbuf_delete(&sndstat_sbuf);
151 	sndstat_isopen = 0;
152 
153 	mtx_unlock(&sndstat_lock);
154 	splx(s);
155 	return 0;
156 }
157 
158 static int
159 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
160 {
161 	intrmask_t s;
162 	int l, err;
163 
164 	s = spltty();
165 	mtx_lock(&sndstat_lock);
166 	if (!sndstat_isopen) {
167 		mtx_unlock(&sndstat_lock);
168 		splx(s);
169 		return EBADF;
170 	}
171     	l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
172 	err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
173 	sndstat_bufptr += l;
174 
175 	mtx_unlock(&sndstat_lock);
176 	splx(s);
177 	return err;
178 }
179 
180 /************************************************************************/
181 
182 static struct sndstat_entry *
183 sndstat_find(int type, int unit)
184 {
185 	struct sndstat_entry *ent;
186 
187 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
188 		if (ent->type == type && ent->unit == unit)
189 			return ent;
190 	}
191 
192 	return NULL;
193 }
194 
195 int
196 sndstat_register(device_t dev, char *str, sndstat_handler handler)
197 {
198 	intrmask_t s;
199 	struct sndstat_entry *ent;
200 	const char *devtype;
201 	int type, unit;
202 
203 	if (dev) {
204 		unit = device_get_unit(dev);
205 		devtype = device_get_name(dev);
206 		if (!strcmp(devtype, "pcm"))
207 			type = SS_TYPE_PCM;
208 		else if (!strcmp(devtype, "midi"))
209 			type = SS_TYPE_MIDI;
210 		else if (!strcmp(devtype, "sequencer"))
211 			type = SS_TYPE_SEQUENCER;
212 		else
213 			return EINVAL;
214 	} else {
215 		type = SS_TYPE_MODULE;
216 		unit = -1;
217 	}
218 
219 	ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
220 	if (!ent)
221 		return ENOSPC;
222 
223 	ent->dev = dev;
224 	ent->str = str;
225 	ent->type = type;
226 	ent->unit = unit;
227 	ent->handler = handler;
228 
229 	s = spltty();
230 	mtx_lock(&sndstat_lock);
231 	SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
232 	if (type == SS_TYPE_MODULE)
233 		sndstat_files++;
234 	sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
235 	mtx_unlock(&sndstat_lock);
236 	splx(s);
237 
238 	return 0;
239 }
240 
241 int
242 sndstat_registerfile(char *str)
243 {
244 	return sndstat_register(NULL, str, NULL);
245 }
246 
247 int
248 sndstat_unregister(device_t dev)
249 {
250 	intrmask_t s;
251 	struct sndstat_entry *ent;
252 
253 	s = spltty();
254 	mtx_lock(&sndstat_lock);
255 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
256 		if (ent->dev == dev) {
257 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
258 			mtx_unlock(&sndstat_lock);
259 			splx(s);
260 			free(ent, M_DEVBUF);
261 
262 			return 0;
263 		}
264 	}
265 	mtx_unlock(&sndstat_lock);
266 	splx(s);
267 
268 	return ENXIO;
269 }
270 
271 int
272 sndstat_unregisterfile(char *str)
273 {
274 	intrmask_t s;
275 	struct sndstat_entry *ent;
276 
277 	s = spltty();
278 	mtx_lock(&sndstat_lock);
279 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
280 		if (ent->dev == NULL && ent->str == str) {
281 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
282 			sndstat_files--;
283 			mtx_unlock(&sndstat_lock);
284 			splx(s);
285 			free(ent, M_DEVBUF);
286 
287 			return 0;
288 		}
289 	}
290 	mtx_unlock(&sndstat_lock);
291 	splx(s);
292 
293 	return ENXIO;
294 }
295 
296 /************************************************************************/
297 
298 static int
299 sndstat_prepare(struct sbuf *s)
300 {
301 	struct sndstat_entry *ent;
302     	int i, j;
303 
304 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
305 	if (SLIST_EMPTY(&sndstat_devlist)) {
306 		sbuf_printf(s, "No devices installed.\n");
307 		sbuf_finish(s);
308     		return sbuf_len(s);
309 	}
310 
311 	sbuf_printf(s, "Installed devices:\n");
312 
313     	for (i = 0; i <= sndstat_maxunit; i++) {
314 		for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
315 			ent = sndstat_find(j, i);
316 			if (!ent)
317 				continue;
318 			sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
319 			sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
320 			sbuf_printf(s, " %s", ent->str);
321 			if (ent->handler)
322 				ent->handler(s, ent->dev, sndstat_verbose);
323 			else
324 				sbuf_printf(s, " [no handler]");
325 			sbuf_printf(s, "\n");
326 		}
327     	}
328 
329 	if (sndstat_verbose >= 3 && sndstat_files > 0) {
330 		sbuf_printf(s, "\nFile Versions:\n");
331 
332 		SLIST_FOREACH(ent, &sndstat_devlist, link) {
333 			if (ent->dev == NULL && ent->str != NULL)
334 				sbuf_printf(s, "%s\n", ent->str);
335 		}
336 	}
337 
338 	sbuf_finish(s);
339     	return sbuf_len(s);
340 }
341 
342 static int
343 sndstat_init(void)
344 {
345 	mtx_init(&sndstat_lock, "sndstat", NULL, MTX_DEF);
346 	sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
347 
348 	return (sndstat_dev != 0)? 0 : ENXIO;
349 }
350 
351 static int
352 sndstat_uninit(void)
353 {
354 	intrmask_t s;
355 
356 	s = spltty();
357 	mtx_lock(&sndstat_lock);
358 	if (sndstat_isopen) {
359 		mtx_unlock(&sndstat_lock);
360 		splx(s);
361 		return EBUSY;
362 	}
363 
364 	if (sndstat_dev)
365 		destroy_dev(sndstat_dev);
366 	sndstat_dev = 0;
367 
368 	splx(s);
369 	mtx_destroy(&sndstat_lock);
370 	return 0;
371 }
372 
373 int
374 sndstat_busy(void)
375 {
376 	return (sndstat_isopen);
377 }
378 
379 static void
380 sndstat_sysinit(void *p)
381 {
382 	sndstat_init();
383 }
384 
385 static void
386 sndstat_sysuninit(void *p)
387 {
388 	sndstat_uninit();
389 }
390 
391 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
392 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
393 
394 
395