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.11 2007/01/04 21:47:03 corecode 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.11 2007/01/04 21:47:03 corecode 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") == NOCDEV) 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 389