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