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