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 = (int)szmin(buf->uio_resid, 159 sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 160 if (l > 0) { 161 err = uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, 162 l, buf); 163 } else { 164 err = 0; 165 } 166 sndstat_bufptr += l; 167 168 lockmgr(&sndstat_lock, LK_RELEASE); 169 return err; 170 } 171 172 /************************************************************************/ 173 174 static struct sndstat_entry * 175 sndstat_find(int type, int unit) 176 { 177 struct sndstat_entry *ent; 178 179 SLIST_FOREACH(ent, &sndstat_devlist, link) { 180 if (ent->type == type && ent->unit == unit) 181 return ent; 182 } 183 184 return NULL; 185 } 186 187 int 188 sndstat_acquire(void) 189 { 190 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 191 if (sndstat_isopen) { 192 lockmgr(&sndstat_lock, LK_RELEASE); 193 return EBUSY; 194 } 195 sndstat_isopen = 1; 196 lockmgr(&sndstat_lock, LK_RELEASE); 197 return 0; 198 } 199 200 int 201 sndstat_release(void) 202 { 203 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 204 if (!sndstat_isopen) { 205 lockmgr(&sndstat_lock, LK_RELEASE); 206 return EBADF; 207 } 208 sndstat_isopen = 0; 209 lockmgr(&sndstat_lock, LK_RELEASE); 210 return 0; 211 } 212 213 int 214 sndstat_register(device_t dev, char *str, sndstat_handler handler) 215 { 216 struct sndstat_entry *ent; 217 const char *devtype; 218 int type, unit; 219 220 if (dev) { 221 unit = device_get_unit(dev); 222 devtype = device_get_name(dev); 223 if (!strcmp(devtype, "pcm")) 224 type = SS_TYPE_PCM; 225 else if (!strcmp(devtype, "midi")) 226 type = SS_TYPE_MIDI; 227 else if (!strcmp(devtype, "sequencer")) 228 type = SS_TYPE_SEQUENCER; 229 else 230 return EINVAL; 231 } else { 232 type = SS_TYPE_MODULE; 233 unit = -1; 234 } 235 236 ent = kmalloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK); 237 238 ent->dev = dev; 239 ent->str = str; 240 ent->type = type; 241 ent->unit = unit; 242 ent->handler = handler; 243 244 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 245 SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 246 if (type == SS_TYPE_MODULE) 247 sndstat_files++; 248 sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 249 lockmgr(&sndstat_lock, LK_RELEASE); 250 251 return 0; 252 } 253 254 int 255 sndstat_registerfile(char *str) 256 { 257 return sndstat_register(NULL, str, NULL); 258 } 259 260 int 261 sndstat_unregister(device_t dev) 262 { 263 struct sndstat_entry *ent; 264 265 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 266 SLIST_FOREACH(ent, &sndstat_devlist, link) { 267 if (ent->dev == dev) { 268 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 269 lockmgr(&sndstat_lock, LK_RELEASE); 270 kfree(ent, M_DEVBUF); 271 272 return 0; 273 } 274 } 275 lockmgr(&sndstat_lock, LK_RELEASE); 276 277 return ENXIO; 278 } 279 280 int 281 sndstat_unregisterfile(char *str) 282 { 283 struct sndstat_entry *ent; 284 285 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 286 SLIST_FOREACH(ent, &sndstat_devlist, link) { 287 if (ent->dev == NULL && ent->str == str) { 288 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 289 sndstat_files--; 290 lockmgr(&sndstat_lock, LK_RELEASE); 291 kfree(ent, M_DEVBUF); 292 293 return 0; 294 } 295 } 296 lockmgr(&sndstat_lock, LK_RELEASE); 297 298 return ENXIO; 299 } 300 301 /************************************************************************/ 302 303 static int 304 sndstat_prepare(struct sbuf *s) 305 { 306 struct sndstat_entry *ent; 307 int i, j; 308 309 sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n"); 310 if (SLIST_EMPTY(&sndstat_devlist)) { 311 sbuf_printf(s, "No devices installed.\n"); 312 sbuf_finish(s); 313 return sbuf_len(s); 314 } 315 316 sbuf_printf(s, "Installed devices:\n"); 317 318 for (i = 0; i <= sndstat_maxunit; i++) { 319 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 320 ent = sndstat_find(j, i); 321 if (!ent) 322 continue; 323 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 324 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 325 sbuf_printf(s, " %s", ent->str); 326 if (ent->handler) 327 ent->handler(s, ent->dev, sndstat_verbose); 328 else 329 sbuf_printf(s, " [no handler]"); 330 sbuf_printf(s, "\n"); 331 } 332 } 333 334 if (sndstat_verbose >= 3 && sndstat_files > 0) { 335 sbuf_printf(s, "\nFile Versions:\n"); 336 337 SLIST_FOREACH(ent, &sndstat_devlist, link) { 338 if (ent->dev == NULL && ent->str != NULL) 339 sbuf_printf(s, "%s\n", ent->str); 340 } 341 } 342 343 sbuf_finish(s); 344 return sbuf_len(s); 345 } 346 347 static int 348 sndstat_init(void) 349 { 350 lockinit(&sndstat_lock, "sndstat", 0, 0); 351 if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 352 UID_ROOT, GID_WHEEL, 0444, "sndstat") == NULL) { 353 return ENXIO; 354 } 355 return 0; 356 } 357 358 static int 359 sndstat_uninit(void) 360 { 361 lockmgr(&sndstat_lock, LK_EXCLUSIVE); 362 if (sndstat_isopen) { 363 lockmgr(&sndstat_lock, LK_RELEASE); 364 return EBUSY; 365 } 366 367 //dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS); 368 dev_ops_remove_all(&sndstat_cdevsw); 369 lockmgr(&sndstat_lock, LK_RELEASE); 370 return 0; 371 } 372 373 static void 374 sndstat_sysinit(void *p) 375 { 376 sndstat_init(); 377 } 378 379 static void 380 sndstat_sysuninit(void *p) 381 { 382 int error; 383 384 error = sndstat_uninit(); 385 KASSERT(error == 0, ("%s: error = %d", __func__, error)); 386 } 387 388 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 389 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 390 391 392