xref: /dragonfly/sys/dev/sound/pcm/sndstat.c (revision 509221ae)
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  * $FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.4.2.2 2002/04/22 15:49:36 cg Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.8 2005/06/10 23:07:01 dillon Exp $
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/vchan.h>
32 
33 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.8 2005/06/10 23:07:01 dillon Exp $");
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 	/* name */	"sndstat",
48 	/* maj */	SND_CDEV_MAJOR,
49 	/* flags */	0,
50 	/* port */	NULL,
51 	/* clone */	NULL,
52 
53 	/* open */	sndstat_open,
54 	/* close */	sndstat_close,
55 	/* read */	sndstat_read,
56 	/* write */	nowrite,
57 	/* ioctl */	noioctl,
58 	/* poll */	nopoll,
59 	/* mmap */	nommap,
60 	/* strategy */	nostrategy,
61 	/* dump */	nodump,
62 	/* psize */	nopsize
63 };
64 
65 struct sndstat_entry {
66 	SLIST_ENTRY(sndstat_entry) link;
67 	device_t dev;
68 	char *str;
69 	sndstat_handler handler;
70 	int type, unit;
71 };
72 
73 static struct sbuf sndstat_sbuf;
74 static int sndstat_isopen = 0;
75 static int sndstat_bufptr;
76 static int sndstat_maxunit = -1;
77 static int sndstat_files = 0;
78 
79 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
80 
81 static int sndstat_verbose = 1;
82 #ifdef	USING_MUTEX
83 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
84 #else
85 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
86 #endif
87 
88 static int sndstat_prepare(struct sbuf *s);
89 
90 static int
91 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
92 {
93 	int error, verbose;
94 
95 	verbose = sndstat_verbose;
96 	error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
97 	if (error == 0 && req->newptr != NULL) {
98 		crit_enter();
99 		if (verbose < 0 || verbose > 3)
100 			error = EINVAL;
101 		else
102 			sndstat_verbose = verbose;
103 		crit_exit();
104 	}
105 	return error;
106 }
107 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
108             0, sizeof(int), sysctl_hw_sndverbose, "I", "");
109 
110 static int
111 sndstat_open(dev_t i_dev, int flags, int mode, struct thread *td)
112 {
113 	int err;
114 
115 	crit_enter();
116 	if (sndstat_isopen) {
117 		crit_exit();
118 		return EBUSY;
119 	}
120 	if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
121 		crit_exit();
122 		return ENXIO;
123 	}
124 	sndstat_bufptr = 0;
125 	err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM;
126 	if (!err)
127 		sndstat_isopen = 1;
128 
129 	crit_exit();
130 	return err;
131 }
132 
133 static int
134 sndstat_close(dev_t i_dev, int flags, int mode, struct thread *td)
135 {
136 	crit_enter();
137 	if (!sndstat_isopen) {
138 		crit_exit();
139 		return EBADF;
140 	}
141 	sbuf_delete(&sndstat_sbuf);
142 	sndstat_isopen = 0;
143 
144 	crit_exit();
145 	return 0;
146 }
147 
148 static int
149 sndstat_read(dev_t i_dev, struct uio *buf, int flag)
150 {
151 	int l, err;
152 
153 	crit_enter();
154 	if (!sndstat_isopen) {
155 		crit_exit();
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 	crit_exit();
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_register(device_t dev, char *str, sndstat_handler handler)
183 {
184 	struct sndstat_entry *ent;
185 	const char *devtype;
186 	int type, unit;
187 
188 	if (dev) {
189 		unit = device_get_unit(dev);
190 		devtype = device_get_name(dev);
191 		if (!strcmp(devtype, "pcm"))
192 			type = SS_TYPE_PCM;
193 		else if (!strcmp(devtype, "midi"))
194 			type = SS_TYPE_MIDI;
195 		else if (!strcmp(devtype, "sequencer"))
196 			type = SS_TYPE_SEQUENCER;
197 		else
198 			return EINVAL;
199 	} else {
200 		type = SS_TYPE_MODULE;
201 		unit = -1;
202 	}
203 
204 	ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
205 	if (!ent)
206 		return ENOSPC;
207 
208 	ent->dev = dev;
209 	ent->str = str;
210 	ent->type = type;
211 	ent->unit = unit;
212 	ent->handler = handler;
213 
214 	crit_enter();
215 	SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
216 	if (type == SS_TYPE_MODULE)
217 		sndstat_files++;
218 	sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
219 	crit_exit();
220 
221 	return 0;
222 }
223 
224 int
225 sndstat_registerfile(char *str)
226 {
227 	return sndstat_register(NULL, str, NULL);
228 }
229 
230 int
231 sndstat_unregister(device_t dev)
232 {
233 	struct sndstat_entry *ent;
234 
235 	crit_enter();
236 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
237 		if (ent->dev == dev) {
238 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
239 			free(ent, M_DEVBUF);
240 			crit_exit();
241 
242 			return 0;
243 		}
244 	}
245 	crit_exit();
246 
247 	return ENXIO;
248 }
249 
250 int
251 sndstat_unregisterfile(char *str)
252 {
253 	struct sndstat_entry *ent;
254 
255 	crit_enter();
256 	SLIST_FOREACH(ent, &sndstat_devlist, link) {
257 		if (ent->dev == NULL && ent->str == str) {
258 			SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
259 			free(ent, M_DEVBUF);
260 			sndstat_files--;
261 			crit_exit();
262 
263 			return 0;
264 		}
265 	}
266 	crit_exit();
267 
268 	return ENXIO;
269 }
270 
271 /************************************************************************/
272 
273 static int
274 sndstat_prepare(struct sbuf *s)
275 {
276 	struct sndstat_entry *ent;
277     	int i, j;
278 
279 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
280 	if (SLIST_EMPTY(&sndstat_devlist)) {
281 		sbuf_printf(s, "No devices installed.\n");
282 		sbuf_finish(s);
283     		return sbuf_len(s);
284 	}
285 
286 	sbuf_printf(s, "Installed devices:\n");
287 
288     	for (i = 0; i <= sndstat_maxunit; i++) {
289 		for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
290 			ent = sndstat_find(j, i);
291 			if (!ent)
292 				continue;
293 			sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
294 			sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
295 			sbuf_printf(s, " %s", ent->str);
296 			if (ent->handler)
297 				ent->handler(s, ent->dev, sndstat_verbose);
298 			else
299 				sbuf_printf(s, " [no handler]");
300 			sbuf_printf(s, "\n");
301 		}
302     	}
303 
304 	if (sndstat_verbose >= 3 && sndstat_files > 0) {
305 		sbuf_printf(s, "\nFile Versions:\n");
306 
307 		SLIST_FOREACH(ent, &sndstat_devlist, link) {
308 			if (ent->dev == NULL && ent->str != NULL)
309 				sbuf_printf(s, "%s\n", ent->str);
310 		}
311 	}
312 
313 	sbuf_finish(s);
314     	return sbuf_len(s);
315 }
316 
317 static int
318 sndstat_init(void)
319 {
320 	cdevsw_add(&sndstat_cdevsw, -1, SND_DEV_STATUS);
321 	make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
322 		UID_ROOT, GID_WHEEL, 0444, "sndstat");
323 	return (0);
324 }
325 
326 static int
327 sndstat_uninit(void)
328 {
329 	crit_enter();
330 	if (sndstat_isopen) {
331 		crit_exit();
332 		return EBUSY;
333 	}
334 	cdevsw_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS);
335 	crit_exit();
336 	return 0;
337 }
338 
339 int
340 sndstat_busy(void)
341 {
342 	return (sndstat_isopen);
343 }
344 
345 static void
346 sndstat_sysinit(void *p)
347 {
348 	sndstat_init();
349 }
350 
351 static void
352 sndstat_sysuninit(void *p)
353 {
354 	sndstat_uninit();
355 }
356 
357 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
358 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
359 
360 
361