xref: /dragonfly/sys/dev/sound/pcm/sndstat.c (revision a68e0df0)
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 = (int)szmin(buf->uio_resid,
159 		       sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
160 	if (l > 0) {
161 		err = uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr,
162 			      l, buf);
163 	} else {
164 		err = 0;
165 	}
166 	sndstat_bufptr += l;
167 
168 	lockmgr(&sndstat_lock, LK_RELEASE);
169 	return err;
170 }
171 
172 /************************************************************************/
173 
174 static struct sndstat_entry *
175 sndstat_find(int type, int unit)
176 {
177 	struct sndstat_entry *ent;
178 
179 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
180 		if (ent->type == type && ent->unit == unit)
181 			return ent;
182 	}
183 
184 	return NULL;
185 }
186 
187 int
188 sndstat_acquire(void)
189 {
190 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
191 	if (sndstat_isopen) {
192 		lockmgr(&sndstat_lock, LK_RELEASE);
193 		return EBUSY;
194 	}
195 	sndstat_isopen = 1;
196 	lockmgr(&sndstat_lock, LK_RELEASE);
197 	return 0;
198 }
199 
200 int
201 sndstat_release(void)
202 {
203 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
204 	if (!sndstat_isopen) {
205 		lockmgr(&sndstat_lock, LK_RELEASE);
206 		return EBADF;
207 	}
208 	sndstat_isopen = 0;
209 	lockmgr(&sndstat_lock, LK_RELEASE);
210 	return 0;
211 }
212 
213 int
214 sndstat_register(device_t dev, char *str, sndstat_handler handler)
215 {
216 	struct sndstat_entry *ent;
217 	const char *devtype;
218 	int type, unit;
219 
220 	if (dev) {
221 		unit = device_get_unit(dev);
222 		devtype = device_get_name(dev);
223 		if (!strcmp(devtype, "pcm"))
224 			type = SS_TYPE_PCM;
225 		else if (!strcmp(devtype, "midi"))
226 			type = SS_TYPE_MIDI;
227 		else if (!strcmp(devtype, "sequencer"))
228 			type = SS_TYPE_SEQUENCER;
229 		else
230 			return EINVAL;
231 	} else {
232 		type = SS_TYPE_MODULE;
233 		unit = -1;
234 	}
235 
236 	ent = kmalloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
237 
238 	ent->dev = dev;
239 	ent->str = str;
240 	ent->type = type;
241 	ent->unit = unit;
242 	ent->handler = handler;
243 
244 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
245 	SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
246 	if (type == SS_TYPE_MODULE)
247 		sndstat_files++;
248 	sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
249 	lockmgr(&sndstat_lock, LK_RELEASE);
250 
251 	return 0;
252 }
253 
254 int
255 sndstat_registerfile(char *str)
256 {
257 	return sndstat_register(NULL, str, NULL);
258 }
259 
260 int
261 sndstat_unregister(device_t dev)
262 {
263 	struct sndstat_entry *ent;
264 
265 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
266 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
267 		if (ent->dev == dev) {
268 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
269 			lockmgr(&sndstat_lock, LK_RELEASE);
270 			kfree(ent, M_DEVBUF);
271 
272 			return 0;
273 		}
274 	}
275 	lockmgr(&sndstat_lock, LK_RELEASE);
276 
277 	return ENXIO;
278 }
279 
280 int
281 sndstat_unregisterfile(char *str)
282 {
283 	struct sndstat_entry *ent;
284 
285 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
286 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
287 		if (ent->dev == NULL && ent->str == str) {
288 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
289 			sndstat_files--;
290 			lockmgr(&sndstat_lock, LK_RELEASE);
291 			kfree(ent, M_DEVBUF);
292 
293 			return 0;
294 		}
295 	}
296 	lockmgr(&sndstat_lock, LK_RELEASE);
297 
298 	return ENXIO;
299 }
300 
301 /************************************************************************/
302 
303 static int
304 sndstat_prepare(struct sbuf *s)
305 {
306 	struct sndstat_entry *ent;
307     	int i, j;
308 
309 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
310 	if (SLIST_EMPTY(&sndstat_devlist)) {
311 		sbuf_printf(s, "No devices installed.\n");
312 		sbuf_finish(s);
313     		return sbuf_len(s);
314 	}
315 
316 	sbuf_printf(s, "Installed devices:\n");
317 
318     	for (i = 0; i <= sndstat_maxunit; i++) {
319 		for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
320 			ent = sndstat_find(j, i);
321 			if (!ent)
322 				continue;
323 			sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
324 			sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
325 			sbuf_printf(s, " %s", ent->str);
326 			if (ent->handler)
327 				ent->handler(s, ent->dev, sndstat_verbose);
328 			else
329 				sbuf_printf(s, " [no handler]");
330 			sbuf_printf(s, "\n");
331 		}
332     	}
333 
334 	if (sndstat_verbose >= 3 && sndstat_files > 0) {
335 		sbuf_printf(s, "\nFile Versions:\n");
336 
337 		SLIST_FOREACH(ent, &sndstat_devlist, link) {
338 			if (ent->dev == NULL && ent->str != NULL)
339 				sbuf_printf(s, "%s\n", ent->str);
340 		}
341 	}
342 
343 	sbuf_finish(s);
344     	return sbuf_len(s);
345 }
346 
347 static int
348 sndstat_init(void)
349 {
350 	lockinit(&sndstat_lock, "sndstat", 0, 0);
351 	if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
352 		     UID_ROOT, GID_WHEEL, 0444, "sndstat") == NULL) {
353 		return ENXIO;
354 	}
355 	return 0;
356 }
357 
358 static int
359 sndstat_uninit(void)
360 {
361 	lockmgr(&sndstat_lock, LK_EXCLUSIVE);
362 	if (sndstat_isopen) {
363 		lockmgr(&sndstat_lock, LK_RELEASE);
364 		return EBUSY;
365 	}
366 
367 	//dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS);
368 	dev_ops_remove_all(&sndstat_cdevsw);
369 	lockmgr(&sndstat_lock, LK_RELEASE);
370 	return 0;
371 }
372 
373 static void
374 sndstat_sysinit(void *p)
375 {
376 	sndstat_init();
377 }
378 
379 static void
380 sndstat_sysuninit(void *p)
381 {
382 	int error;
383 
384 	error = sndstat_uninit();
385 	KASSERT(error == 0, ("%s: error = %d", __func__, error));
386 }
387 
388 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
389 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
390 
391 
392