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