Merge from vendor branch LIBSTDC++:
[dragonfly.git] / sys / dev / sound / pcm / sndstat.c
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.4 2003/07/21 05:50:36 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.4 2003/07/21 05:50:36 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         /* autoq */     0,
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 dev_t sndstat_dev = 0;
75 static int sndstat_isopen = 0;
76 static int sndstat_bufptr;
77 static int sndstat_maxunit = -1;
78 static int sndstat_files = 0;
79
80 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
81
82 static int sndstat_verbose = 1;
83 #ifdef  USING_MUTEX
84 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
85 #else
86 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
87 #endif
88
89 static int sndstat_prepare(struct sbuf *s);
90
91 static int
92 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
93 {
94         intrmask_t s;
95         int error, verbose;
96
97         verbose = sndstat_verbose;
98         error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
99         if (error == 0 && req->newptr != NULL) {
100                 s = spltty();
101                 if (verbose < 0 || verbose > 3)
102                         error = EINVAL;
103                 else
104                         sndstat_verbose = verbose;
105                 splx(s);
106         }
107         return error;
108 }
109 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
110             0, sizeof(int), sysctl_hw_sndverbose, "I", "");
111
112 static int
113 sndstat_open(dev_t i_dev, int flags, int mode, struct thread *td)
114 {
115         intrmask_t s;
116         int err;
117
118         s = spltty();
119         if (sndstat_isopen) {
120                 splx(s);
121                 return EBUSY;
122         }
123         if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
124                 splx(s);
125                 return ENXIO;
126         }
127         sndstat_bufptr = 0;
128         err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM;
129         if (!err)
130                 sndstat_isopen = 1;
131
132         splx(s);
133         return err;
134 }
135
136 static int
137 sndstat_close(dev_t i_dev, int flags, int mode, struct thread *td)
138 {
139         intrmask_t s;
140
141         s = spltty();
142         if (!sndstat_isopen) {
143                 splx(s);
144                 return EBADF;
145         }
146         sbuf_delete(&sndstat_sbuf);
147         sndstat_isopen = 0;
148
149         splx(s);
150         return 0;
151 }
152
153 static int
154 sndstat_read(dev_t i_dev, struct uio *buf, int flag)
155 {
156         intrmask_t s;
157         int l, err;
158
159         s = spltty();
160         if (!sndstat_isopen) {
161                 splx(s);
162                 return EBADF;
163         }
164         l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
165         err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
166         sndstat_bufptr += l;
167
168         splx(s);
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_register(device_t dev, char *str, sndstat_handler handler)
189 {
190         intrmask_t s;
191         struct sndstat_entry *ent;
192         const char *devtype;
193         int type, unit;
194
195         if (dev) {
196                 unit = device_get_unit(dev);
197                 devtype = device_get_name(dev);
198                 if (!strcmp(devtype, "pcm"))
199                         type = SS_TYPE_PCM;
200                 else if (!strcmp(devtype, "midi"))
201                         type = SS_TYPE_MIDI;
202                 else if (!strcmp(devtype, "sequencer"))
203                         type = SS_TYPE_SEQUENCER;
204                 else
205                         return EINVAL;
206         } else {
207                 type = SS_TYPE_MODULE;
208                 unit = -1;
209         }
210
211         ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
212         if (!ent)
213                 return ENOSPC;
214
215         ent->dev = dev;
216         ent->str = str;
217         ent->type = type;
218         ent->unit = unit;
219         ent->handler = handler;
220
221         s = spltty();
222         SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
223         if (type == SS_TYPE_MODULE)
224                 sndstat_files++;
225         sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
226         splx(s);
227
228         return 0;
229 }
230
231 int
232 sndstat_registerfile(char *str)
233 {
234         return sndstat_register(NULL, str, NULL);
235 }
236
237 int
238 sndstat_unregister(device_t dev)
239 {
240         intrmask_t s;
241         struct sndstat_entry *ent;
242
243         s = spltty();
244         SLIST_FOREACH(ent, &sndstat_devlist, link) {
245                 if (ent->dev == dev) {
246                         SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
247                         free(ent, M_DEVBUF);
248                         splx(s);
249
250                         return 0;
251                 }
252         }
253         splx(s);
254
255         return ENXIO;
256 }
257
258 int
259 sndstat_unregisterfile(char *str)
260 {
261         intrmask_t s;
262         struct sndstat_entry *ent;
263
264         s = spltty();
265         SLIST_FOREACH(ent, &sndstat_devlist, link) {
266                 if (ent->dev == NULL && ent->str == str) {
267                         SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
268                         free(ent, M_DEVBUF);
269                         sndstat_files--;
270                         splx(s);
271
272                         return 0;
273                 }
274         }
275         splx(s);
276
277         return ENXIO;
278 }
279
280 /************************************************************************/
281
282 static int
283 sndstat_prepare(struct sbuf *s)
284 {
285         struct sndstat_entry *ent;
286         int i, j;
287
288         sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
289         if (SLIST_EMPTY(&sndstat_devlist)) {
290                 sbuf_printf(s, "No devices installed.\n");
291                 sbuf_finish(s);
292                 return sbuf_len(s);
293         }
294
295         sbuf_printf(s, "Installed devices:\n");
296
297         for (i = 0; i <= sndstat_maxunit; i++) {
298                 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
299                         ent = sndstat_find(j, i);
300                         if (!ent)
301                                 continue;
302                         sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
303                         sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
304                         sbuf_printf(s, " %s", ent->str);
305                         if (ent->handler)
306                                 ent->handler(s, ent->dev, sndstat_verbose);
307                         else
308                                 sbuf_printf(s, " [no handler]");
309                         sbuf_printf(s, "\n");
310                 }
311         }
312
313         if (sndstat_verbose >= 3 && sndstat_files > 0) {
314                 sbuf_printf(s, "\nFile Versions:\n");
315
316                 SLIST_FOREACH(ent, &sndstat_devlist, link) {
317                         if (ent->dev == NULL && ent->str != NULL)
318                                 sbuf_printf(s, "%s\n", ent->str);
319                 }
320         }
321
322         sbuf_finish(s);
323         return sbuf_len(s);
324 }
325
326 static int
327 sndstat_init(void)
328 {
329         sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
330
331         return (sndstat_dev != 0)? 0 : ENXIO;
332 }
333
334 static int
335 sndstat_uninit(void)
336 {
337         intrmask_t s;
338
339         s = spltty();
340         if (sndstat_isopen) {
341                 splx(s);
342                 return EBUSY;
343         }
344
345         if (sndstat_dev)
346                 destroy_dev(sndstat_dev);
347         sndstat_dev = 0;
348
349         splx(s);
350         return 0;
351 }
352
353 int
354 sndstat_busy(void)
355 {
356         return (sndstat_isopen);
357 }
358
359 static void
360 sndstat_sysinit(void *p)
361 {
362         sndstat_init();
363 }
364
365 static void
366 sndstat_sysuninit(void *p)
367 {
368         sndstat_uninit();
369 }
370
371 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
372 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
373
374