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 .d_flags = D_TRACKCLOSE, 57 }; 58 59 struct sndstat_entry { 60 SLIST_ENTRY(sndstat_entry) link; 61 device_t dev; 62 char *str; 63 sndstat_handler handler; 64 int type, unit; 65 }; 66 67 static struct sx sndstat_lock; 68 static struct sbuf sndstat_sbuf; 69 static struct cdev *sndstat_dev = NULL; 70 static int sndstat_bufptr = -1; 71 static int sndstat_maxunit = -1; 72 static int sndstat_files = 0; 73 74 #define SNDSTAT_PID(x) ((pid_t)((intptr_t)((x)->si_drv1))) 75 #define SNDSTAT_PID_SET(x, y) (x)->si_drv1 = (void *)((intptr_t)(y)) 76 #define SNDSTAT_FLUSH() do { \ 77 if (sndstat_bufptr != -1) { \ 78 sbuf_delete(&sndstat_sbuf); \ 79 sndstat_bufptr = -1; \ 80 } \ 81 } while (0) 82 83 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(sndstat_devlist); 84 85 int snd_verbose = 0; 86 TUNABLE_INT("hw.snd.verbose", &snd_verbose); 87 88 #ifdef SND_DEBUG 89 static int 90 sysctl_hw_snd_sndstat_pid(SYSCTL_HANDLER_ARGS) 91 { 92 int err, val; 93 94 if (sndstat_dev == NULL) 95 return (EINVAL); 96 97 sx_xlock(&sndstat_lock); 98 val = (int)SNDSTAT_PID(sndstat_dev); 99 err = sysctl_handle_int(oidp, &val, 0, req); 100 if (err == 0 && req->newptr != NULL && val == 0) { 101 SNDSTAT_FLUSH(); 102 SNDSTAT_PID_SET(sndstat_dev, 0); 103 } 104 sx_unlock(&sndstat_lock); 105 return (err); 106 } 107 SYSCTL_PROC(_hw_snd, OID_AUTO, sndstat_pid, CTLTYPE_INT | CTLFLAG_RW, 108 0, sizeof(int), sysctl_hw_snd_sndstat_pid, "I", "sndstat busy pid"); 109 #endif 110 111 static int sndstat_prepare(struct sbuf *s); 112 113 static int 114 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 115 { 116 int error, verbose; 117 118 verbose = snd_verbose; 119 error = sysctl_handle_int(oidp, &verbose, 0, req); 120 if (error == 0 && req->newptr != NULL) { 121 if (verbose < 0 || verbose > 4) 122 error = EINVAL; 123 else 124 snd_verbose = verbose; 125 } 126 return error; 127 } 128 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 129 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level"); 130 131 static int 132 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 133 { 134 if (sndstat_dev == NULL || i_dev != sndstat_dev) 135 return EBADF; 136 137 sx_xlock(&sndstat_lock); 138 if (SNDSTAT_PID(i_dev) != 0) { 139 sx_unlock(&sndstat_lock); 140 return EBUSY; 141 } 142 SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid); 143 if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { 144 SNDSTAT_PID_SET(i_dev, 0); 145 sx_unlock(&sndstat_lock); 146 return ENXIO; 147 } 148 sndstat_bufptr = 0; 149 sx_unlock(&sndstat_lock); 150 return 0; 151 } 152 153 static int 154 sndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 155 { 156 if (sndstat_dev == NULL || i_dev != sndstat_dev) 157 return EBADF; 158 159 sx_xlock(&sndstat_lock); 160 if (SNDSTAT_PID(i_dev) == 0) { 161 sx_unlock(&sndstat_lock); 162 return EBADF; 163 } 164 165 SNDSTAT_FLUSH(); 166 SNDSTAT_PID_SET(i_dev, 0); 167 168 sx_unlock(&sndstat_lock); 169 170 return 0; 171 } 172 173 static int 174 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag) 175 { 176 int l, err; 177 178 if (sndstat_dev == NULL || i_dev != sndstat_dev) 179 return EBADF; 180 181 sx_xlock(&sndstat_lock); 182 if (SNDSTAT_PID(i_dev) != buf->uio_td->td_proc->p_pid || 183 sndstat_bufptr == -1) { 184 sx_unlock(&sndstat_lock); 185 return EBADF; 186 } 187 188 if (sndstat_bufptr == 0) { 189 err = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; 190 if (err) { 191 SNDSTAT_FLUSH(); 192 sx_unlock(&sndstat_lock); 193 return err; 194 } 195 } 196 197 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 198 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 199 sndstat_bufptr += l; 200 sx_unlock(&sndstat_lock); 201 202 return err; 203 } 204 205 /************************************************************************/ 206 207 static struct sndstat_entry * 208 sndstat_find(int type, int unit) 209 { 210 struct sndstat_entry *ent; 211 212 SLIST_FOREACH(ent, &sndstat_devlist, link) { 213 if (ent->type == type && ent->unit == unit) 214 return ent; 215 } 216 217 return NULL; 218 } 219 220 int 221 sndstat_acquire(struct thread *td) 222 { 223 if (sndstat_dev == NULL) 224 return EBADF; 225 226 sx_xlock(&sndstat_lock); 227 if (SNDSTAT_PID(sndstat_dev) != 0) { 228 sx_unlock(&sndstat_lock); 229 return EBUSY; 230 } 231 SNDSTAT_PID_SET(sndstat_dev, td->td_proc->p_pid); 232 sx_unlock(&sndstat_lock); 233 return 0; 234 } 235 236 int 237 sndstat_release(struct thread *td) 238 { 239 if (sndstat_dev == NULL) 240 return EBADF; 241 242 sx_xlock(&sndstat_lock); 243 if (SNDSTAT_PID(sndstat_dev) != td->td_proc->p_pid) { 244 sx_unlock(&sndstat_lock); 245 return EBADF; 246 } 247 SNDSTAT_PID_SET(sndstat_dev, 0); 248 sx_unlock(&sndstat_lock); 249 return 0; 250 } 251 252 int 253 sndstat_register(device_t dev, char *str, sndstat_handler handler) 254 { 255 struct sndstat_entry *ent; 256 const char *devtype; 257 int type, unit; 258 259 if (dev) { 260 unit = device_get_unit(dev); 261 devtype = device_get_name(dev); 262 if (!strcmp(devtype, "pcm")) 263 type = SS_TYPE_PCM; 264 else if (!strcmp(devtype, "midi")) 265 type = SS_TYPE_MIDI; 266 else if (!strcmp(devtype, "sequencer")) 267 type = SS_TYPE_SEQUENCER; 268 else 269 return EINVAL; 270 } else { 271 type = SS_TYPE_MODULE; 272 unit = -1; 273 } 274 275 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 276 ent->dev = dev; 277 ent->str = str; 278 ent->type = type; 279 ent->unit = unit; 280 ent->handler = handler; 281 282 sx_xlock(&sndstat_lock); 283 SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 284 if (type == SS_TYPE_MODULE) 285 sndstat_files++; 286 sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 287 sx_unlock(&sndstat_lock); 288 289 return 0; 290 } 291 292 int 293 sndstat_registerfile(char *str) 294 { 295 return sndstat_register(NULL, str, NULL); 296 } 297 298 int 299 sndstat_unregister(device_t dev) 300 { 301 struct sndstat_entry *ent; 302 303 sx_xlock(&sndstat_lock); 304 SLIST_FOREACH(ent, &sndstat_devlist, link) { 305 if (ent->dev == dev) { 306 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 307 sx_unlock(&sndstat_lock); 308 free(ent, M_DEVBUF); 309 310 return 0; 311 } 312 } 313 sx_unlock(&sndstat_lock); 314 315 return ENXIO; 316 } 317 318 int 319 sndstat_unregisterfile(char *str) 320 { 321 struct sndstat_entry *ent; 322 323 sx_xlock(&sndstat_lock); 324 SLIST_FOREACH(ent, &sndstat_devlist, link) { 325 if (ent->dev == NULL && ent->str == str) { 326 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 327 sndstat_files--; 328 sx_unlock(&sndstat_lock); 329 free(ent, M_DEVBUF); 330 331 return 0; 332 } 333 } 334 sx_unlock(&sndstat_lock); 335 336 return ENXIO; 337 } 338 339 /************************************************************************/ 340 341 static int 342 sndstat_prepare(struct sbuf *s) 343 { 344 struct sndstat_entry *ent; 345 struct snddev_info *d; 346 int i, j; 347 348 if (snd_verbose > 0) { 349 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n", 350 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, 351 MACHINE_ARCH); 352 } 353 354 if (SLIST_EMPTY(&sndstat_devlist)) { 355 sbuf_printf(s, "No devices installed.\n"); 356 sbuf_finish(s); 357 return sbuf_len(s); 358 } 359 360 sbuf_printf(s, "Installed devices:\n"); 361 362 for (i = 0; i <= sndstat_maxunit; i++) { 363 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 364 ent = sndstat_find(j, i); 365 if (!ent) 366 continue; 367 d = device_get_softc(ent->dev); 368 if (!PCM_REGISTERED(d)) 369 continue; 370 /* XXX Need Giant magic entry ??? */ 371 PCM_ACQUIRE_QUICK(d); 372 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 373 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 374 if (snd_verbose > 0) 375 sbuf_printf(s, " %s", ent->str); 376 if (ent->handler) 377 ent->handler(s, ent->dev, snd_verbose); 378 sbuf_printf(s, "\n"); 379 PCM_RELEASE_QUICK(d); 380 } 381 } 382 383 if (snd_verbose >= 3 && sndstat_files > 0) { 384 sbuf_printf(s, "\nFile Versions:\n"); 385 386 SLIST_FOREACH(ent, &sndstat_devlist, link) { 387 if (ent->dev == NULL && ent->str != NULL) 388 sbuf_printf(s, "%s\n", ent->str); 389 } 390 } 391 392 sbuf_finish(s); 393 return sbuf_len(s); 394 } 395 396 static int 397 sndstat_init(void) 398 { 399 if (sndstat_dev != NULL) 400 return EINVAL; 401 sx_init(&sndstat_lock, "sndstat lock"); 402 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 403 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 404 return 0; 405 } 406 407 static int 408 sndstat_uninit(void) 409 { 410 if (sndstat_dev == NULL) 411 return EINVAL; 412 413 sx_xlock(&sndstat_lock); 414 if (SNDSTAT_PID(sndstat_dev) != curthread->td_proc->p_pid) { 415 sx_unlock(&sndstat_lock); 416 return EBUSY; 417 } 418 419 /* XXXPHO: use destroy_dev_sched() */ 420 destroy_dev(sndstat_dev); 421 sndstat_dev = NULL; 422 423 SNDSTAT_FLUSH(); 424 425 sx_unlock(&sndstat_lock); 426 sx_destroy(&sndstat_lock); 427 return 0; 428 } 429 430 static void 431 sndstat_sysinit(void *p) 432 { 433 sndstat_init(); 434 } 435 436 static void 437 sndstat_sysuninit(void *p) 438 { 439 int error; 440 441 error = sndstat_uninit(); 442 KASSERT(error == 0, ("%s: error = %d", __func__, error)); 443 } 444 445 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 446 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 447