e665e01747e452ee4686f2ad832030b627c2e66e
[dragonfly.git] / sys / dev / sound / pcm / sndstat.c
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         }
349         return 0;
350 }
351
352 static int
353 sndstat_uninit(void)
354 {
355         lockmgr(&sndstat_lock, LK_EXCLUSIVE);
356         if (sndstat_isopen) {
357                 lockmgr(&sndstat_lock, LK_RELEASE);
358                 return EBUSY;
359         }
360
361         //dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS);
362         dev_ops_remove_all(&sndstat_cdevsw);
363         lockmgr(&sndstat_lock, LK_RELEASE);
364         return 0;
365 }
366
367 static void
368 sndstat_sysinit(void *p)
369 {
370         sndstat_init();
371 }
372
373 static void
374 sndstat_sysuninit(void *p)
375 {
376         int error;
377
378         error = sndstat_uninit();
379         KASSERT(error == 0, ("%s: error = %d", __func__, error));
380 }
381
382 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
383 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
384
385