Merge from vendor branch LIBARCHIVE:
[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.12 2007/05/08 02:31:42 dillon 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.12 2007/05/08 02:31:42 dillon 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") == NULL)
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