xref: /dragonfly/sys/dev/sound/pcm/sndstat.c (revision f02303f9)
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.11 2007/01/04 21:47:03 corecode 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.11 2007/01/04 21:47:03 corecode 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 	if (!ent)
232 		return ENOSPC;
233 
234 	ent->dev = dev;
235 	ent->str = str;
236 	ent->type = type;
237 	ent->unit = unit;
238 	ent->handler = handler;
239 
240 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
241 	SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
242 	if (type == SS_TYPE_MODULE)
243 		sndstat_files++;
244 	sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
245 	lockmgr(&sndstat_lock, LK_RELEASE);
246 
247 	return 0;
248 }
249 
250 int
251 sndstat_registerfile(char *str)
252 {
253 	return sndstat_register(NULL, str, NULL);
254 }
255 
256 int
257 sndstat_unregister(device_t dev)
258 {
259 	struct sndstat_entry *ent;
260 
261 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
262 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
263 		if (ent->dev == dev) {
264 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
265 			lockmgr(&sndstat_lock, LK_RELEASE);
266 			kfree(ent, M_DEVBUF);
267 
268 			return 0;
269 		}
270 	}
271 	lockmgr(&sndstat_lock, LK_RELEASE);
272 
273 	return ENXIO;
274 }
275 
276 int
277 sndstat_unregisterfile(char *str)
278 {
279 	struct sndstat_entry *ent;
280 
281 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
282 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
283 		if (ent->dev == NULL && ent->str == str) {
284 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
285 			sndstat_files--;
286 			lockmgr(&sndstat_lock, LK_RELEASE);
287 			kfree(ent, M_DEVBUF);
288 
289 			return 0;
290 		}
291 	}
292 	lockmgr(&sndstat_lock, LK_RELEASE);
293 
294 	return ENXIO;
295 }
296 
297 /************************************************************************/
298 
299 static int
300 sndstat_prepare(struct sbuf *s)
301 {
302 	struct sndstat_entry *ent;
303     	int i, j;
304 
305 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
306 	if (SLIST_EMPTY(&sndstat_devlist)) {
307 		sbuf_printf(s, "No devices installed.\n");
308 		sbuf_finish(s);
309     		return sbuf_len(s);
310 	}
311 
312 	sbuf_printf(s, "Installed devices:\n");
313 
314     	for (i = 0; i <= sndstat_maxunit; i++) {
315 		for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
316 			ent = sndstat_find(j, i);
317 			if (!ent)
318 				continue;
319 			sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
320 			sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
321 			sbuf_printf(s, " %s", ent->str);
322 			if (ent->handler)
323 				ent->handler(s, ent->dev, sndstat_verbose);
324 			else
325 				sbuf_printf(s, " [no handler]");
326 			sbuf_printf(s, "\n");
327 		}
328     	}
329 
330 	if (sndstat_verbose >= 3 && sndstat_files > 0) {
331 		sbuf_printf(s, "\nFile Versions:\n");
332 
333 		SLIST_FOREACH(ent, &sndstat_devlist, link) {
334 			if (ent->dev == NULL && ent->str != NULL)
335 				sbuf_printf(s, "%s\n", ent->str);
336 		}
337 	}
338 
339 	sbuf_finish(s);
340     	return sbuf_len(s);
341 }
342 
343 static int
344 sndstat_init(void)
345 {
346 	lockinit(&sndstat_lock, "sndstat", 0, 0);
347 	if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
348 		     UID_ROOT, GID_WHEEL, 0444, "sndstat") == NOCDEV)
349 		return ENXIO;
350 	dev_ops_add(&sndstat_cdevsw, -1, SND_DEV_STATUS);
351 
352 	return 0;
353 }
354 
355 static int
356 sndstat_uninit(void)
357 {
358 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
359 	if (sndstat_isopen) {
360 		lockmgr(&sndstat_lock, LK_RELEASE);
361 		return EBUSY;
362 	}
363 
364 	dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS);
365 
366 	lockmgr(&sndstat_lock, LK_RELEASE);
367 	return 0;
368 }
369 
370 static void
371 sndstat_sysinit(void *p)
372 {
373 	sndstat_init();
374 }
375 
376 static void
377 sndstat_sysuninit(void *p)
378 {
379 	int error;
380 
381 	error = sndstat_uninit();
382 	KASSERT(error == 0, ("%s: error = %d", __func__, error));
383 }
384 
385 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
386 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
387 
388 
389