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