xref: /dragonfly/sys/dev/sound/pcm/sndstat.c (revision 3641b7ca)
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  * $FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.20.2.2 2005/12/30 19:55:54 netchild Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.13 2008/01/06 16:55:51 swildner Exp $
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/vchan.h>
32 #ifdef	USING_MUTEX
33 #include <sys/lock.h>
34 #endif
35 
36 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.13 2008/01/06 16:55:51 swildner Exp $");
37 
38 #define	SS_TYPE_MODULE		0
39 #define	SS_TYPE_FIRST		1
40 #define	SS_TYPE_PCM		1
41 #define	SS_TYPE_MIDI		2
42 #define	SS_TYPE_SEQUENCER	3
43 #define	SS_TYPE_LAST		3
44 
45 static d_open_t sndstat_open;
46 static d_close_t sndstat_close;
47 static d_read_t sndstat_read;
48 
49 static struct dev_ops sndstat_cdevsw = {
50 	{ "sndstat", SND_CDEV_MAJOR, 0 },
51 	/* .d_flags =	D_NEEDGIANT, */
52 	.d_open =	sndstat_open,
53 	.d_close =	sndstat_close,
54 	.d_read =	sndstat_read,
55 };
56 
57 struct sndstat_entry {
58 	SLIST_ENTRY(sndstat_entry) link;
59 	device_t dev;
60 	char *str;
61 	sndstat_handler handler;
62 	int type, unit;
63 };
64 
65 #ifdef	USING_MUTEX
66 static struct lock sndstat_lock;
67 #endif
68 static struct sbuf sndstat_sbuf;
69 static int sndstat_isopen = 0;
70 static int sndstat_bufptr;
71 static int sndstat_maxunit = -1;
72 static int sndstat_files = 0;
73 
74 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
75 
76 static int sndstat_verbose = 1;
77 #ifdef	USING_MUTEX
78 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
79 #else
80 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
81 #endif
82 
83 static int sndstat_prepare(struct sbuf *s);
84 
85 static int
86 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
87 {
88 	int error, verbose;
89 
90 	verbose = sndstat_verbose;
91 	error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
92 	if (error == 0 && req->newptr != NULL) {
93 		lockmgr(&sndstat_lock, LK_EXCLUSIVE);
94 		if (verbose < 0 || verbose > 3)
95 			error = EINVAL;
96 		else
97 			sndstat_verbose = verbose;
98 		lockmgr(&sndstat_lock, LK_RELEASE);
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 dev_open_args *ap)
107 {
108 	int error;
109 
110 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
111 	if (sndstat_isopen) {
112 		lockmgr(&sndstat_lock, LK_RELEASE);
113 		return EBUSY;
114 	}
115 	sndstat_isopen = 1;
116 	lockmgr(&sndstat_lock, LK_RELEASE);
117 	if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
118 		error = ENXIO;
119 		goto out;
120 	}
121 	sndstat_bufptr = 0;
122 	error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
123 out:
124 	if (error) {
125 		lockmgr(&sndstat_lock, LK_EXCLUSIVE);
126 		sndstat_isopen = 0;
127 		lockmgr(&sndstat_lock, LK_RELEASE);
128 	}
129 	return (error);
130 }
131 
132 static int
133 sndstat_close(struct dev_close_args *ap)
134 {
135 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
136 	if (!sndstat_isopen) {
137 		lockmgr(&sndstat_lock, LK_RELEASE);
138 		return EBADF;
139 	}
140 	sbuf_delete(&sndstat_sbuf);
141 	sndstat_isopen = 0;
142 
143 	lockmgr(&sndstat_lock, LK_RELEASE);
144 	return 0;
145 }
146 
147 static int
148 sndstat_read(struct dev_read_args *ap)
149 {
150 	struct uio *buf = ap->a_uio;
151 	int l, err;
152 
153 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
154 	if (!sndstat_isopen) {
155 		lockmgr(&sndstat_lock, LK_RELEASE);
156 		return EBADF;
157 	}
158     	l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
159 	err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
160 	sndstat_bufptr += l;
161 
162 	lockmgr(&sndstat_lock, LK_RELEASE);
163 	return err;
164 }
165 
166 /************************************************************************/
167 
168 static struct sndstat_entry *
169 sndstat_find(int type, int unit)
170 {
171 	struct sndstat_entry *ent;
172 
173 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
174 		if (ent->type == type && ent->unit == unit)
175 			return ent;
176 	}
177 
178 	return NULL;
179 }
180 
181 int
182 sndstat_acquire(void)
183 {
184 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
185 	if (sndstat_isopen) {
186 		lockmgr(&sndstat_lock, LK_RELEASE);
187 		return EBUSY;
188 	}
189 	sndstat_isopen = 1;
190 	lockmgr(&sndstat_lock, LK_RELEASE);
191 	return 0;
192 }
193 
194 int
195 sndstat_release(void)
196 {
197 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
198 	if (!sndstat_isopen) {
199 		lockmgr(&sndstat_lock, LK_RELEASE);
200 		return EBADF;
201 	}
202 	sndstat_isopen = 0;
203 	lockmgr(&sndstat_lock, LK_RELEASE);
204 	return 0;
205 }
206 
207 int
208 sndstat_register(device_t dev, char *str, sndstat_handler handler)
209 {
210 	struct sndstat_entry *ent;
211 	const char *devtype;
212 	int type, unit;
213 
214 	if (dev) {
215 		unit = device_get_unit(dev);
216 		devtype = device_get_name(dev);
217 		if (!strcmp(devtype, "pcm"))
218 			type = SS_TYPE_PCM;
219 		else if (!strcmp(devtype, "midi"))
220 			type = SS_TYPE_MIDI;
221 		else if (!strcmp(devtype, "sequencer"))
222 			type = SS_TYPE_SEQUENCER;
223 		else
224 			return EINVAL;
225 	} else {
226 		type = SS_TYPE_MODULE;
227 		unit = -1;
228 	}
229 
230 	ent = kmalloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
231 
232 	ent->dev = dev;
233 	ent->str = str;
234 	ent->type = type;
235 	ent->unit = unit;
236 	ent->handler = handler;
237 
238 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
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 	lockmgr(&sndstat_lock, LK_RELEASE);
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 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
260 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
261 		if (ent->dev == dev) {
262 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
263 			lockmgr(&sndstat_lock, LK_RELEASE);
264 			kfree(ent, M_DEVBUF);
265 
266 			return 0;
267 		}
268 	}
269 	lockmgr(&sndstat_lock, LK_RELEASE);
270 
271 	return ENXIO;
272 }
273 
274 int
275 sndstat_unregisterfile(char *str)
276 {
277 	struct sndstat_entry *ent;
278 
279 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
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 			lockmgr(&sndstat_lock, LK_RELEASE);
285 			kfree(ent, M_DEVBUF);
286 
287 			return 0;
288 		}
289 	}
290 	lockmgr(&sndstat_lock, LK_RELEASE);
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)\n");
304 	if (SLIST_EMPTY(&sndstat_devlist)) {
305 		sbuf_printf(s, "No devices installed.\n");
306 		sbuf_finish(s);
307     		return sbuf_len(s);
308 	}
309 
310 	sbuf_printf(s, "Installed devices:\n");
311 
312     	for (i = 0; i <= sndstat_maxunit; i++) {
313 		for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
314 			ent = sndstat_find(j, i);
315 			if (!ent)
316 				continue;
317 			sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
318 			sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
319 			sbuf_printf(s, " %s", ent->str);
320 			if (ent->handler)
321 				ent->handler(s, ent->dev, sndstat_verbose);
322 			else
323 				sbuf_printf(s, " [no handler]");
324 			sbuf_printf(s, "\n");
325 		}
326     	}
327 
328 	if (sndstat_verbose >= 3 && sndstat_files > 0) {
329 		sbuf_printf(s, "\nFile Versions:\n");
330 
331 		SLIST_FOREACH(ent, &sndstat_devlist, link) {
332 			if (ent->dev == NULL && ent->str != NULL)
333 				sbuf_printf(s, "%s\n", ent->str);
334 		}
335 	}
336 
337 	sbuf_finish(s);
338     	return sbuf_len(s);
339 }
340 
341 static int
342 sndstat_init(void)
343 {
344 	lockinit(&sndstat_lock, "sndstat", 0, 0);
345 	if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
346 		     UID_ROOT, GID_WHEEL, 0444, "sndstat") == NULL)
347 		return ENXIO;
348 	dev_ops_add(&sndstat_cdevsw, -1, SND_DEV_STATUS);
349 
350 	return 0;
351 }
352 
353 static int
354 sndstat_uninit(void)
355 {
356 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
357 	if (sndstat_isopen) {
358 		lockmgr(&sndstat_lock, LK_RELEASE);
359 		return EBUSY;
360 	}
361 
362 	dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS);
363 
364 	lockmgr(&sndstat_lock, LK_RELEASE);
365 	return 0;
366 }
367 
368 static void
369 sndstat_sysinit(void *p)
370 {
371 	sndstat_init();
372 }
373 
374 static void
375 sndstat_sysuninit(void *p)
376 {
377 	int error;
378 
379 	error = sndstat_uninit();
380 	KASSERT(error == 0, ("%s: error = %d", __func__, error));
381 }
382 
383 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
384 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
385 
386 
387