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