1 /*- 2 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #ifdef HAVE_KERNEL_OPTION_HEADERS 29 #include "opt_snd.h" 30 #endif 31 32 #include <dev/sound/pcm/sound.h> 33 #include <dev/sound/pcm/pcm.h> 34 #include <dev/sound/version.h> 35 #include <sys/sx.h> 36 37 SND_DECLARE_FILE("$FreeBSD$"); 38 39 #define SS_TYPE_MODULE 0 40 #define SS_TYPE_FIRST 1 41 #define SS_TYPE_PCM 1 42 #define SS_TYPE_MIDI 2 43 #define SS_TYPE_SEQUENCER 3 44 #define SS_TYPE_LAST 3 45 46 static d_open_t sndstat_open; 47 static d_close_t sndstat_close; 48 static d_read_t sndstat_read; 49 50 static struct cdevsw sndstat_cdevsw = { 51 .d_version = D_VERSION, 52 .d_open = sndstat_open, 53 .d_close = sndstat_close, 54 .d_read = sndstat_read, 55 .d_name = "sndstat", 56 }; 57 58 struct sndstat_entry { 59 SLIST_ENTRY(sndstat_entry) link; 60 device_t dev; 61 char *str; 62 sndstat_handler handler; 63 int type, unit; 64 }; 65 66 static struct mtx sndstat_lock; 67 static struct sbuf sndstat_sbuf; 68 static struct cdev *sndstat_dev = NULL; 69 static int sndstat_bufptr = -1; 70 static int sndstat_maxunit = -1; 71 static int sndstat_files = 0; 72 73 #define SNDSTAT_PID(x) ((pid_t)((intptr_t)((x)->si_drv1))) 74 #define SNDSTAT_PID_SET(x, y) (x)->si_drv1 = (void *)((intptr_t)(y)) 75 #define SNDSTAT_FLUSH() do { \ 76 if (sndstat_bufptr != -1) { \ 77 sbuf_delete(&sndstat_sbuf); \ 78 sndstat_bufptr = -1; \ 79 } \ 80 } while (0) 81 82 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(sndstat_devlist); 83 84 int snd_verbose = 1; 85 TUNABLE_INT("hw.snd.verbose", &snd_verbose); 86 87 #ifdef SND_DEBUG 88 static int 89 sysctl_hw_snd_sndstat_pid(SYSCTL_HANDLER_ARGS) 90 { 91 int err, val; 92 93 if (sndstat_dev == NULL) 94 return (EINVAL); 95 96 mtx_lock(&sndstat_lock); 97 val = (int)SNDSTAT_PID(sndstat_dev); 98 mtx_unlock(&sndstat_lock); 99 err = sysctl_handle_int(oidp, &val, 0, req); 100 if (err == 0 && req->newptr != NULL && val == 0) { 101 mtx_lock(&sndstat_lock); 102 SNDSTAT_FLUSH(); 103 SNDSTAT_PID_SET(sndstat_dev, 0); 104 mtx_unlock(&sndstat_lock); 105 } 106 return (err); 107 } 108 SYSCTL_PROC(_hw_snd, OID_AUTO, sndstat_pid, CTLTYPE_INT | CTLFLAG_RW, 109 0, sizeof(int), sysctl_hw_snd_sndstat_pid, "I", "sndstat busy pid"); 110 #endif 111 112 static int sndstat_prepare(struct sbuf *s); 113 114 static int 115 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 116 { 117 int error, verbose; 118 119 verbose = snd_verbose; 120 error = sysctl_handle_int(oidp, &verbose, 0, req); 121 if (error == 0 && req->newptr != NULL) { 122 mtx_lock(&sndstat_lock); 123 if (verbose < 0 || verbose > 4) 124 error = EINVAL; 125 else 126 snd_verbose = verbose; 127 mtx_unlock(&sndstat_lock); 128 } 129 return error; 130 } 131 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 132 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level"); 133 134 static int 135 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 136 { 137 if (sndstat_dev == NULL || i_dev != sndstat_dev) 138 return EBADF; 139 140 mtx_lock(&sndstat_lock); 141 if (SNDSTAT_PID(i_dev) != 0) { 142 mtx_unlock(&sndstat_lock); 143 return EBUSY; 144 } 145 SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid); 146 mtx_unlock(&sndstat_lock); 147 if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { 148 mtx_lock(&sndstat_lock); 149 SNDSTAT_PID_SET(i_dev, 0); 150 mtx_unlock(&sndstat_lock); 151 return ENXIO; 152 } 153 sndstat_bufptr = 0; 154 return 0; 155 } 156 157 static int 158 sndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 159 { 160 if (sndstat_dev == NULL || i_dev != sndstat_dev) 161 return EBADF; 162 163 mtx_lock(&sndstat_lock); 164 if (SNDSTAT_PID(i_dev) == 0) { 165 mtx_unlock(&sndstat_lock); 166 return EBADF; 167 } 168 169 SNDSTAT_FLUSH(); 170 SNDSTAT_PID_SET(i_dev, 0); 171 172 mtx_unlock(&sndstat_lock); 173 174 return 0; 175 } 176 177 static int 178 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag) 179 { 180 int l, err; 181 182 if (sndstat_dev == NULL || i_dev != sndstat_dev) 183 return EBADF; 184 185 mtx_lock(&sndstat_lock); 186 if (SNDSTAT_PID(i_dev) != buf->uio_td->td_proc->p_pid || 187 sndstat_bufptr == -1) { 188 mtx_unlock(&sndstat_lock); 189 return EBADF; 190 } 191 mtx_unlock(&sndstat_lock); 192 193 if (sndstat_bufptr == 0) { 194 err = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; 195 if (err) { 196 mtx_lock(&sndstat_lock); 197 SNDSTAT_FLUSH(); 198 mtx_unlock(&sndstat_lock); 199 return err; 200 } 201 } 202 203 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 204 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 205 sndstat_bufptr += l; 206 207 return err; 208 } 209 210 /************************************************************************/ 211 212 static struct sndstat_entry * 213 sndstat_find(int type, int unit) 214 { 215 struct sndstat_entry *ent; 216 217 SLIST_FOREACH(ent, &sndstat_devlist, link) { 218 if (ent->type == type && ent->unit == unit) 219 return ent; 220 } 221 222 return NULL; 223 } 224 225 int 226 sndstat_acquire(struct thread *td) 227 { 228 if (sndstat_dev == NULL) 229 return EBADF; 230 231 mtx_lock(&sndstat_lock); 232 if (SNDSTAT_PID(sndstat_dev) != 0) { 233 mtx_unlock(&sndstat_lock); 234 return EBUSY; 235 } 236 SNDSTAT_PID_SET(sndstat_dev, td->td_proc->p_pid); 237 mtx_unlock(&sndstat_lock); 238 return 0; 239 } 240 241 int 242 sndstat_release(struct thread *td) 243 { 244 if (sndstat_dev == NULL) 245 return EBADF; 246 247 mtx_lock(&sndstat_lock); 248 if (SNDSTAT_PID(sndstat_dev) != td->td_proc->p_pid) { 249 mtx_unlock(&sndstat_lock); 250 return EBADF; 251 } 252 SNDSTAT_PID_SET(sndstat_dev, 0); 253 mtx_unlock(&sndstat_lock); 254 return 0; 255 } 256 257 int 258 sndstat_register(device_t dev, char *str, sndstat_handler handler) 259 { 260 struct sndstat_entry *ent; 261 const char *devtype; 262 int type, unit; 263 264 if (dev) { 265 unit = device_get_unit(dev); 266 devtype = device_get_name(dev); 267 if (!strcmp(devtype, "pcm")) 268 type = SS_TYPE_PCM; 269 else if (!strcmp(devtype, "midi")) 270 type = SS_TYPE_MIDI; 271 else if (!strcmp(devtype, "sequencer")) 272 type = SS_TYPE_SEQUENCER; 273 else 274 return EINVAL; 275 } else { 276 type = SS_TYPE_MODULE; 277 unit = -1; 278 } 279 280 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 281 ent->dev = dev; 282 ent->str = str; 283 ent->type = type; 284 ent->unit = unit; 285 ent->handler = handler; 286 287 mtx_lock(&sndstat_lock); 288 SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 289 if (type == SS_TYPE_MODULE) 290 sndstat_files++; 291 sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 292 mtx_unlock(&sndstat_lock); 293 294 return 0; 295 } 296 297 int 298 sndstat_registerfile(char *str) 299 { 300 return sndstat_register(NULL, str, NULL); 301 } 302 303 int 304 sndstat_unregister(device_t dev) 305 { 306 struct sndstat_entry *ent; 307 308 mtx_lock(&sndstat_lock); 309 SLIST_FOREACH(ent, &sndstat_devlist, link) { 310 if (ent->dev == dev) { 311 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 312 mtx_unlock(&sndstat_lock); 313 free(ent, M_DEVBUF); 314 315 return 0; 316 } 317 } 318 mtx_unlock(&sndstat_lock); 319 320 return ENXIO; 321 } 322 323 int 324 sndstat_unregisterfile(char *str) 325 { 326 struct sndstat_entry *ent; 327 328 mtx_lock(&sndstat_lock); 329 SLIST_FOREACH(ent, &sndstat_devlist, link) { 330 if (ent->dev == NULL && ent->str == str) { 331 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 332 sndstat_files--; 333 mtx_unlock(&sndstat_lock); 334 free(ent, M_DEVBUF); 335 336 return 0; 337 } 338 } 339 mtx_unlock(&sndstat_lock); 340 341 return ENXIO; 342 } 343 344 /************************************************************************/ 345 346 static int 347 sndstat_prepare(struct sbuf *s) 348 { 349 struct sndstat_entry *ent; 350 struct snddev_info *d; 351 int i, j; 352 353 sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", 354 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); 355 if (SLIST_EMPTY(&sndstat_devlist)) { 356 sbuf_printf(s, "No devices installed.\n"); 357 sbuf_finish(s); 358 return sbuf_len(s); 359 } 360 361 sbuf_printf(s, "Installed devices:\n"); 362 363 for (i = 0; i <= sndstat_maxunit; i++) { 364 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 365 ent = sndstat_find(j, i); 366 if (!ent) 367 continue; 368 d = device_get_softc(ent->dev); 369 if (!PCM_REGISTERED(d)) 370 continue; 371 /* XXX Need Giant magic entry ??? */ 372 PCM_ACQUIRE_QUICK(d); 373 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 374 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 375 sbuf_printf(s, " %s [%s]", ent->str, 376 (d->flags & SD_F_MPSAFE) ? "MPSAFE" : "GIANT"); 377 if (ent->handler) 378 ent->handler(s, ent->dev, snd_verbose); 379 else 380 sbuf_printf(s, " [no handler]"); 381 sbuf_printf(s, "\n"); 382 PCM_RELEASE_QUICK(d); 383 } 384 } 385 386 if (snd_verbose >= 3 && sndstat_files > 0) { 387 sbuf_printf(s, "\nFile Versions:\n"); 388 389 SLIST_FOREACH(ent, &sndstat_devlist, link) { 390 if (ent->dev == NULL && ent->str != NULL) 391 sbuf_printf(s, "%s\n", ent->str); 392 } 393 } 394 395 sbuf_finish(s); 396 return sbuf_len(s); 397 } 398 399 static int 400 sndstat_init(void) 401 { 402 if (sndstat_dev != NULL) 403 return EINVAL; 404 mtx_init(&sndstat_lock, "sndstat", "sndstat lock", MTX_DEF); 405 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 406 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 407 return 0; 408 } 409 410 static int 411 sndstat_uninit(void) 412 { 413 if (sndstat_dev == NULL) 414 return EINVAL; 415 416 mtx_lock(&sndstat_lock); 417 if (SNDSTAT_PID(sndstat_dev) != curthread->td_proc->p_pid) { 418 mtx_unlock(&sndstat_lock); 419 return EBUSY; 420 } 421 422 SNDSTAT_FLUSH(); 423 424 mtx_unlock(&sndstat_lock); 425 426 destroy_dev(sndstat_dev); 427 sndstat_dev = NULL; 428 429 mtx_destroy(&sndstat_lock); 430 return 0; 431 } 432 433 static void 434 sndstat_sysinit(void *p) 435 { 436 sndstat_init(); 437 } 438 439 static void 440 sndstat_sysuninit(void *p) 441 { 442 int error; 443 444 error = sndstat_uninit(); 445 KASSERT(error == 0, ("%s: error = %d", __func__, error)); 446 } 447 448 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 449 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 450