| Commit | Line | Data |
|---|---|---|
| 558a398b SS |
1 | /*- |
| 2 | * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> | |
| 984263bc MD |
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. | |
| 1de703da | 25 | * |
| 558a398b | 26 | * $FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.20.2.2 2005/12/30 19:55:54 netchild Exp $ |
| 978400d3 | 27 | * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.13 2008/01/06 16:55:51 swildner Exp $ |
| 984263bc MD |
28 | */ |
| 29 | ||
| 30 | #include <dev/sound/pcm/sound.h> | |
| 31 | #include <dev/sound/pcm/vchan.h> | |
| 558a398b SS |
32 | #ifdef USING_MUTEX |
| 33 | #include <sys/lock.h> | |
| 34 | #endif | |
| 984263bc | 35 | |
| 978400d3 | 36 | SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.13 2008/01/06 16:55:51 swildner Exp $"); |
| 984263bc MD |
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 | ||
| 558a398b | 49 | static struct dev_ops sndstat_cdevsw = { |
| fef8985e | 50 | { "sndstat", SND_CDEV_MAJOR, 0 }, |
| 558a398b | 51 | /* .d_flags = D_NEEDGIANT, */ |
| fef8985e MD |
52 | .d_open = sndstat_open, |
| 53 | .d_close = sndstat_close, | |
| 54 | .d_read = sndstat_read, | |
| 984263bc MD |
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 | ||
| 558a398b SS |
65 | #ifdef USING_MUTEX |
| 66 | static struct lock sndstat_lock; | |
| 67 | #endif | |
| 984263bc | 68 | static struct sbuf sndstat_sbuf; |
| 984263bc MD |
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 | { | |
| 984263bc MD |
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) { | |
| 558a398b | 93 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc MD |
94 | if (verbose < 0 || verbose > 3) |
| 95 | error = EINVAL; | |
| 96 | else | |
| 97 | sndstat_verbose = verbose; | |
| 558a398b | 98 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
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 | |
| fef8985e | 106 | sndstat_open(struct dev_open_args *ap) |
| 984263bc | 107 | { |
| 558a398b | 108 | int error; |
| 984263bc | 109 | |
| 558a398b | 110 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc | 111 | if (sndstat_isopen) { |
| 558a398b | 112 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
113 | return EBUSY; |
| 114 | } | |
| 558a398b SS |
115 | sndstat_isopen = 1; |
| 116 | lockmgr(&sndstat_lock, LK_RELEASE); | |
| 984263bc | 117 | if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) { |
| 558a398b SS |
118 | error = ENXIO; |
| 119 | goto out; | |
| 984263bc MD |
120 | } |
| 121 | sndstat_bufptr = 0; | |
| 558a398b SS |
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); | |
| 984263bc MD |
130 | } |
| 131 | ||
| 132 | static int | |
| fef8985e | 133 | sndstat_close(struct dev_close_args *ap) |
| 984263bc | 134 | { |
| 558a398b | 135 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc | 136 | if (!sndstat_isopen) { |
| 558a398b | 137 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
138 | return EBADF; |
| 139 | } | |
| 140 | sbuf_delete(&sndstat_sbuf); | |
| 141 | sndstat_isopen = 0; | |
| 142 | ||
| 558a398b | 143 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
144 | return 0; |
| 145 | } | |
| 146 | ||
| 147 | static int | |
| fef8985e | 148 | sndstat_read(struct dev_read_args *ap) |
| 984263bc | 149 | { |
| 558a398b | 150 | struct uio *buf = ap->a_uio; |
| 984263bc MD |
151 | int l, err; |
| 152 | ||
| 558a398b | 153 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc | 154 | if (!sndstat_isopen) { |
| 558a398b | 155 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
156 | return EBADF; |
| 157 | } | |
| 558a398b SS |
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; | |
| 984263bc MD |
160 | sndstat_bufptr += l; |
| 161 | ||
| 558a398b | 162 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
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 | |
| 558a398b SS |
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 | |
| 984263bc MD |
208 | sndstat_register(device_t dev, char *str, sndstat_handler handler) |
| 209 | { | |
| 984263bc MD |
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 | ||
| efda3bd0 | 230 | ent = kmalloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK); |
| 984263bc MD |
231 | |
| 232 | ent->dev = dev; | |
| 233 | ent->str = str; | |
| 234 | ent->type = type; | |
| 235 | ent->unit = unit; | |
| 236 | ent->handler = handler; | |
| 237 | ||
| 558a398b | 238 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc MD |
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; | |
| 558a398b | 243 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
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 | { | |
| 984263bc MD |
257 | struct sndstat_entry *ent; |
| 258 | ||
| 558a398b | 259 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc MD |
260 | SLIST_FOREACH(ent, &sndstat_devlist, link) { |
| 261 | if (ent->dev == dev) { | |
| 262 | SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); | |
| 558a398b | 263 | lockmgr(&sndstat_lock, LK_RELEASE); |
| efda3bd0 | 264 | kfree(ent, M_DEVBUF); |
| 984263bc MD |
265 | |
| 266 | return 0; | |
| 267 | } | |
| 268 | } | |
| 558a398b | 269 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
270 | |
| 271 | return ENXIO; | |
| 272 | } | |
| 273 | ||
| 274 | int | |
| 275 | sndstat_unregisterfile(char *str) | |
| 276 | { | |
| 984263bc MD |
277 | struct sndstat_entry *ent; |
| 278 | ||
| 558a398b | 279 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc MD |
280 | SLIST_FOREACH(ent, &sndstat_devlist, link) { |
| 281 | if (ent->dev == NULL && ent->str == str) { | |
| 282 | SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); | |
| 984263bc | 283 | sndstat_files--; |
| 558a398b SS |
284 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 285 | kfree(ent, M_DEVBUF); | |
| 984263bc MD |
286 | |
| 287 | return 0; | |
| 288 | } | |
| 289 | } | |
| 558a398b | 290 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
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 | { | |
| 558a398b SS |
344 | lockinit(&sndstat_lock, "sndstat", 0, 0); |
| 345 | if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS, | |
| 3e82b46c | 346 | UID_ROOT, GID_WHEEL, 0444, "sndstat") == NULL) { |
| 558a398b | 347 | return ENXIO; |
| 3e82b46c | 348 | } |
| 558a398b | 349 | return 0; |
| 984263bc MD |
350 | } |
| 351 | ||
| 352 | static int | |
| 353 | sndstat_uninit(void) | |
| 354 | { | |
| 558a398b | 355 | lockmgr(&sndstat_lock, LK_EXCLUSIVE); |
| 984263bc | 356 | if (sndstat_isopen) { |
| 558a398b | 357 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 984263bc MD |
358 | return EBUSY; |
| 359 | } | |
| 984263bc | 360 | |
| cd29885a MD |
361 | //dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS); |
| 362 | dev_ops_remove_all(&sndstat_cdevsw); | |
| 558a398b SS |
363 | lockmgr(&sndstat_lock, LK_RELEASE); |
| 364 | return 0; | |
| 984263bc MD |
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 | { | |
| 558a398b SS |
376 | int error; |
| 377 | ||
| 378 | error = sndstat_uninit(); | |
| 379 | KASSERT(error == 0, ("%s: error = %d", __func__, error)); | |
| 984263bc MD |
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 |