1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 6 * Copyright (c) 2020 The FreeBSD Foundation 7 * All rights reserved. 8 * 9 * Portions of this software were developed by Ka Ho Ng 10 * under sponsorship from the FreeBSD Foundation. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifdef HAVE_KERNEL_OPTION_HEADERS 35 #include "opt_snd.h" 36 #endif 37 38 #include <sys/param.h> 39 #include <sys/lock.h> 40 #include <sys/malloc.h> 41 #include <sys/nv.h> 42 #include <sys/dnv.h> 43 #include <sys/sx.h> 44 #ifdef COMPAT_FREEBSD32 45 #include <sys/sysent.h> 46 #endif 47 48 #include <dev/sound/pcm/sound.h> 49 #include <dev/sound/pcm/pcm.h> 50 #include <dev/sound/version.h> 51 52 #include "feeder_if.h" 53 54 #define SS_TYPE_PCM 1 55 #define SS_TYPE_MIDI 2 56 #define SS_TYPE_SEQUENCER 3 57 58 static d_open_t sndstat_open; 59 static void sndstat_close(void *); 60 static d_read_t sndstat_read; 61 static d_write_t sndstat_write; 62 static d_ioctl_t sndstat_ioctl; 63 64 static struct cdevsw sndstat_cdevsw = { 65 .d_version = D_VERSION, 66 .d_open = sndstat_open, 67 .d_read = sndstat_read, 68 .d_write = sndstat_write, 69 .d_ioctl = sndstat_ioctl, 70 .d_name = "sndstat", 71 .d_flags = D_TRACKCLOSE, 72 }; 73 74 struct sndstat_entry { 75 TAILQ_ENTRY(sndstat_entry) link; 76 device_t dev; 77 char *str; 78 int type, unit; 79 }; 80 81 struct sndstat_userdev { 82 TAILQ_ENTRY(sndstat_userdev) link; 83 char *provider; 84 char *nameunit; 85 char *devnode; 86 char *desc; 87 unsigned int pchan; 88 unsigned int rchan; 89 struct { 90 uint32_t min_rate; 91 uint32_t max_rate; 92 uint32_t formats; 93 uint32_t min_chn; 94 uint32_t max_chn; 95 } info_play, info_rec; 96 nvlist_t *provider_nvl; 97 }; 98 99 struct sndstat_file { 100 TAILQ_ENTRY(sndstat_file) entry; 101 struct sbuf sbuf; 102 struct sx lock; 103 void *devs_nvlbuf; /* (l) */ 104 size_t devs_nbytes; /* (l) */ 105 TAILQ_HEAD(, sndstat_userdev) userdev_list; /* (l) */ 106 int out_offset; 107 int in_offset; 108 int fflags; 109 }; 110 111 static struct sx sndstat_lock; 112 static struct cdev *sndstat_dev; 113 114 #define SNDSTAT_LOCK() sx_xlock(&sndstat_lock) 115 #define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock) 116 117 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist); 118 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist); 119 120 int snd_verbose = 0; 121 122 static int sndstat_prepare(struct sndstat_file *); 123 static struct sndstat_userdev * 124 sndstat_line2userdev(struct sndstat_file *, const char *, int); 125 126 static int 127 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 128 { 129 int error, verbose; 130 131 verbose = snd_verbose; 132 error = sysctl_handle_int(oidp, &verbose, 0, req); 133 if (error == 0 && req->newptr != NULL) { 134 if (verbose < 0 || verbose > 4) 135 error = EINVAL; 136 else 137 snd_verbose = verbose; 138 } 139 return (error); 140 } 141 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, 142 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int), 143 sysctl_hw_sndverbose, "I", 144 "verbosity level"); 145 146 static int 147 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 148 { 149 struct sndstat_file *pf; 150 151 pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO); 152 153 if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { 154 free(pf, M_DEVBUF); 155 return (ENOMEM); 156 } 157 158 pf->fflags = flags; 159 TAILQ_INIT(&pf->userdev_list); 160 sx_init(&pf->lock, "sndstat_file"); 161 162 SNDSTAT_LOCK(); 163 TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry); 164 SNDSTAT_UNLOCK(); 165 166 devfs_set_cdevpriv(pf, &sndstat_close); 167 168 return (0); 169 } 170 171 /* 172 * Should only be called either when: 173 * * Closing 174 * * pf->lock held 175 */ 176 static void 177 sndstat_remove_all_userdevs(struct sndstat_file *pf) 178 { 179 struct sndstat_userdev *ud; 180 181 KASSERT( 182 sx_xlocked(&pf->lock), ("%s: Called without pf->lock", __func__)); 183 while ((ud = TAILQ_FIRST(&pf->userdev_list)) != NULL) { 184 TAILQ_REMOVE(&pf->userdev_list, ud, link); 185 free(ud->provider, M_DEVBUF); 186 free(ud->desc, M_DEVBUF); 187 free(ud->devnode, M_DEVBUF); 188 free(ud->nameunit, M_DEVBUF); 189 nvlist_destroy(ud->provider_nvl); 190 free(ud, M_DEVBUF); 191 } 192 } 193 194 static void 195 sndstat_close(void *sndstat_file) 196 { 197 struct sndstat_file *pf = (struct sndstat_file *)sndstat_file; 198 199 SNDSTAT_LOCK(); 200 sbuf_delete(&pf->sbuf); 201 TAILQ_REMOVE(&sndstat_filelist, pf, entry); 202 SNDSTAT_UNLOCK(); 203 204 free(pf->devs_nvlbuf, M_NVLIST); 205 sx_xlock(&pf->lock); 206 sndstat_remove_all_userdevs(pf); 207 sx_xunlock(&pf->lock); 208 sx_destroy(&pf->lock); 209 210 free(pf, M_DEVBUF); 211 } 212 213 static int 214 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag) 215 { 216 struct sndstat_file *pf; 217 int err; 218 int len; 219 220 err = devfs_get_cdevpriv((void **)&pf); 221 if (err != 0) 222 return (err); 223 224 /* skip zero-length reads */ 225 if (buf->uio_resid == 0) 226 return (0); 227 228 SNDSTAT_LOCK(); 229 if (pf->out_offset != 0) { 230 /* don't allow both reading and writing */ 231 err = EINVAL; 232 goto done; 233 } else if (pf->in_offset == 0) { 234 err = sndstat_prepare(pf); 235 if (err <= 0) { 236 err = ENOMEM; 237 goto done; 238 } 239 } 240 len = sbuf_len(&pf->sbuf) - pf->in_offset; 241 if (len > buf->uio_resid) 242 len = buf->uio_resid; 243 if (len > 0) 244 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf); 245 pf->in_offset += len; 246 done: 247 SNDSTAT_UNLOCK(); 248 return (err); 249 } 250 251 static int 252 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag) 253 { 254 struct sndstat_file *pf; 255 uint8_t temp[64]; 256 int err; 257 int len; 258 259 err = devfs_get_cdevpriv((void **)&pf); 260 if (err != 0) 261 return (err); 262 263 /* skip zero-length writes */ 264 if (buf->uio_resid == 0) 265 return (0); 266 267 /* don't allow writing more than 64Kbytes */ 268 if (buf->uio_resid > 65536) 269 return (ENOMEM); 270 271 SNDSTAT_LOCK(); 272 if (pf->in_offset != 0) { 273 /* don't allow both reading and writing */ 274 err = EINVAL; 275 } else { 276 /* only remember the last write - allows for updates */ 277 sx_xlock(&pf->lock); 278 sndstat_remove_all_userdevs(pf); 279 sx_xunlock(&pf->lock); 280 281 while (1) { 282 len = sizeof(temp); 283 if (len > buf->uio_resid) 284 len = buf->uio_resid; 285 if (len > 0) { 286 err = uiomove(temp, len, buf); 287 if (err) 288 break; 289 } else { 290 break; 291 } 292 if (sbuf_bcat(&pf->sbuf, temp, len) < 0) { 293 err = ENOMEM; 294 break; 295 } 296 } 297 sbuf_finish(&pf->sbuf); 298 299 if (err == 0) { 300 char *line, *str; 301 302 str = sbuf_data(&pf->sbuf); 303 while ((line = strsep(&str, "\n")) != NULL) { 304 struct sndstat_userdev *ud; 305 306 ud = sndstat_line2userdev(pf, line, strlen(line)); 307 if (ud == NULL) 308 continue; 309 310 sx_xlock(&pf->lock); 311 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link); 312 sx_xunlock(&pf->lock); 313 } 314 315 pf->out_offset = sbuf_len(&pf->sbuf); 316 } else 317 pf->out_offset = 0; 318 319 sbuf_clear(&pf->sbuf); 320 } 321 SNDSTAT_UNLOCK(); 322 return (err); 323 } 324 325 static void 326 sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate, 327 uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn) 328 { 329 struct pcm_channel *c; 330 unsigned int encoding; 331 int dir; 332 333 dir = play ? PCMDIR_PLAY : PCMDIR_REC; 334 335 if (play && d->pvchancount > 0) { 336 *min_rate = *max_rate = d->pvchanrate; 337 *fmts = AFMT_ENCODING(d->pvchanformat); 338 *minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat); 339 return; 340 } else if (!play && d->rvchancount > 0) { 341 *min_rate = *max_rate = d->rvchanrate; 342 *fmts = AFMT_ENCODING(d->rvchanformat); 343 *minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat); 344 return; 345 } 346 347 *min_rate = UINT32_MAX; 348 *max_rate = 0; 349 *minchn = UINT32_MAX; 350 *maxchn = 0; 351 encoding = 0; 352 CHN_FOREACH(c, d, channels.pcm) { 353 struct pcmchan_caps *caps; 354 int i; 355 356 if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0) 357 continue; 358 359 CHN_LOCK(c); 360 caps = chn_getcaps(c); 361 *min_rate = min(caps->minspeed, *min_rate); 362 *max_rate = max(caps->maxspeed, *max_rate); 363 for (i = 0; caps->fmtlist[i]; i++) { 364 encoding |= AFMT_ENCODING(caps->fmtlist[i]); 365 *minchn = min(AFMT_CHANNEL(encoding), *minchn); 366 *maxchn = max(AFMT_CHANNEL(encoding), *maxchn); 367 } 368 CHN_UNLOCK(c); 369 } 370 if (*min_rate == UINT32_MAX) 371 *min_rate = 0; 372 if (*minchn == UINT32_MAX) 373 *minchn = 0; 374 } 375 376 static nvlist_t * 377 sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats, 378 uint32_t min_chn, uint32_t max_chn) 379 { 380 nvlist_t *nv; 381 382 nv = nvlist_create(0); 383 if (nv == NULL) 384 return (NULL); 385 nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_RATE, min_rate); 386 nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_RATE, max_rate); 387 nvlist_add_number(nv, SNDST_DSPS_INFO_FORMATS, formats); 388 nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_CHN, min_chn); 389 nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_CHN, max_chn); 390 return (nv); 391 } 392 393 static int 394 sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip) 395 { 396 uint32_t maxrate, minrate, fmts, minchn, maxchn; 397 nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL; 398 int err; 399 400 di = nvlist_create(0); 401 if (di == NULL) { 402 err = ENOMEM; 403 goto done; 404 } 405 sound4di = nvlist_create(0); 406 if (sound4di == NULL) { 407 err = ENOMEM; 408 goto done; 409 } 410 411 nvlist_add_bool(di, SNDST_DSPS_FROM_USER, false); 412 nvlist_add_stringf(di, SNDST_DSPS_NAMEUNIT, "%s", 413 device_get_nameunit(d->dev)); 414 nvlist_add_stringf(di, SNDST_DSPS_DEVNODE, "dsp%d", 415 device_get_unit(d->dev)); 416 nvlist_add_string( 417 di, SNDST_DSPS_DESC, device_get_desc(d->dev)); 418 419 PCM_ACQUIRE_QUICK(d); 420 nvlist_add_number(di, SNDST_DSPS_PCHAN, d->playcount); 421 nvlist_add_number(di, SNDST_DSPS_RCHAN, d->reccount); 422 if (d->playcount > 0) { 423 sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn, 424 &maxchn); 425 nvlist_add_number(di, "pminrate", minrate); 426 nvlist_add_number(di, "pmaxrate", maxrate); 427 nvlist_add_number(di, "pfmts", fmts); 428 diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts, 429 minchn, maxchn); 430 if (diinfo == NULL) 431 nvlist_set_error(di, ENOMEM); 432 else 433 nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo); 434 } 435 if (d->reccount > 0) { 436 sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn, 437 &maxchn); 438 nvlist_add_number(di, "rminrate", minrate); 439 nvlist_add_number(di, "rmaxrate", maxrate); 440 nvlist_add_number(di, "rfmts", fmts); 441 diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts, 442 minchn, maxchn); 443 if (diinfo == NULL) 444 nvlist_set_error(di, ENOMEM); 445 else 446 nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo); 447 } 448 449 nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_UNIT, 450 device_get_unit(d->dev)); // XXX: I want signed integer here 451 nvlist_add_bool( 452 sound4di, SNDST_DSPS_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT); 453 nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHAN, d->pvchancount); 454 nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHAN, d->rvchancount); 455 nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di); 456 sound4di = NULL; 457 PCM_RELEASE_QUICK(d); 458 nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER); 459 460 err = nvlist_error(di); 461 if (err) 462 goto done; 463 464 *dip = di; 465 466 done: 467 if (err) { 468 nvlist_destroy(sound4di); 469 nvlist_destroy(di); 470 } 471 return (err); 472 } 473 474 static int 475 sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip) 476 { 477 nvlist_t *di, *diinfo; 478 int err; 479 480 di = nvlist_create(0); 481 if (di == NULL) { 482 err = ENOMEM; 483 goto done; 484 } 485 486 nvlist_add_bool(di, SNDST_DSPS_FROM_USER, true); 487 nvlist_add_number(di, SNDST_DSPS_PCHAN, ud->pchan); 488 nvlist_add_number(di, SNDST_DSPS_RCHAN, ud->rchan); 489 nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, ud->nameunit); 490 nvlist_add_string( 491 di, SNDST_DSPS_DEVNODE, ud->devnode); 492 nvlist_add_string(di, SNDST_DSPS_DESC, ud->desc); 493 if (ud->pchan != 0) { 494 nvlist_add_number(di, "pminrate", 495 ud->info_play.min_rate); 496 nvlist_add_number(di, "pmaxrate", 497 ud->info_play.max_rate); 498 nvlist_add_number(di, "pfmts", 499 ud->info_play.formats); 500 diinfo = sndstat_create_diinfo_nv(ud->info_play.min_rate, 501 ud->info_play.max_rate, ud->info_play.formats, 502 ud->info_play.min_chn, ud->info_play.max_chn); 503 if (diinfo == NULL) 504 nvlist_set_error(di, ENOMEM); 505 else 506 nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo); 507 } 508 if (ud->rchan != 0) { 509 nvlist_add_number(di, "rminrate", 510 ud->info_rec.min_rate); 511 nvlist_add_number(di, "rmaxrate", 512 ud->info_rec.max_rate); 513 nvlist_add_number(di, "rfmts", 514 ud->info_rec.formats); 515 diinfo = sndstat_create_diinfo_nv(ud->info_rec.min_rate, 516 ud->info_rec.max_rate, ud->info_rec.formats, 517 ud->info_rec.min_chn, ud->info_rec.max_chn); 518 if (diinfo == NULL) 519 nvlist_set_error(di, ENOMEM); 520 else 521 nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo); 522 } 523 nvlist_add_string(di, SNDST_DSPS_PROVIDER, 524 (ud->provider != NULL) ? ud->provider : ""); 525 if (ud->provider_nvl != NULL) 526 nvlist_add_nvlist( 527 di, SNDST_DSPS_PROVIDER_INFO, ud->provider_nvl); 528 529 err = nvlist_error(di); 530 if (err) 531 goto done; 532 533 *dip = di; 534 535 done: 536 if (err) 537 nvlist_destroy(di); 538 return (err); 539 } 540 541 /* 542 * Should only be called with the following locks held: 543 * * sndstat_lock 544 */ 545 static int 546 sndstat_create_devs_nvlist(nvlist_t **nvlp) 547 { 548 int err; 549 nvlist_t *nvl; 550 struct sndstat_entry *ent; 551 struct sndstat_file *pf; 552 553 nvl = nvlist_create(0); 554 if (nvl == NULL) 555 return (ENOMEM); 556 557 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 558 struct snddev_info *d; 559 nvlist_t *di; 560 561 d = device_get_softc(ent->dev); 562 if (!PCM_REGISTERED(d)) 563 continue; 564 565 err = sndstat_build_sound4_nvlist(d, &di); 566 if (err) 567 goto done; 568 569 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di); 570 nvlist_destroy(di); 571 err = nvlist_error(nvl); 572 if (err) 573 goto done; 574 } 575 576 TAILQ_FOREACH(pf, &sndstat_filelist, entry) { 577 struct sndstat_userdev *ud; 578 579 sx_xlock(&pf->lock); 580 581 TAILQ_FOREACH(ud, &pf->userdev_list, link) { 582 nvlist_t *di; 583 584 err = sndstat_build_userland_nvlist(ud, &di); 585 if (err != 0) { 586 sx_xunlock(&pf->lock); 587 goto done; 588 } 589 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di); 590 nvlist_destroy(di); 591 592 err = nvlist_error(nvl); 593 if (err != 0) { 594 sx_xunlock(&pf->lock); 595 goto done; 596 } 597 } 598 599 sx_xunlock(&pf->lock); 600 } 601 602 *nvlp = nvl; 603 604 done: 605 if (err != 0) 606 nvlist_destroy(nvl); 607 return (err); 608 } 609 610 static int 611 sndstat_refresh_devs(struct sndstat_file *pf) 612 { 613 sx_xlock(&pf->lock); 614 free(pf->devs_nvlbuf, M_NVLIST); 615 pf->devs_nvlbuf = NULL; 616 pf->devs_nbytes = 0; 617 sx_unlock(&pf->lock); 618 619 return (0); 620 } 621 622 static int 623 sndstat_get_devs(struct sndstat_file *pf, caddr_t data) 624 { 625 int err; 626 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data; 627 628 SNDSTAT_LOCK(); 629 sx_xlock(&pf->lock); 630 631 if (pf->devs_nvlbuf == NULL) { 632 nvlist_t *nvl; 633 void *nvlbuf; 634 size_t nbytes; 635 int err; 636 637 sx_xunlock(&pf->lock); 638 639 err = sndstat_create_devs_nvlist(&nvl); 640 if (err) { 641 SNDSTAT_UNLOCK(); 642 return (err); 643 } 644 645 sx_xlock(&pf->lock); 646 647 nvlbuf = nvlist_pack(nvl, &nbytes); 648 err = nvlist_error(nvl); 649 nvlist_destroy(nvl); 650 if (nvlbuf == NULL || err != 0) { 651 SNDSTAT_UNLOCK(); 652 sx_xunlock(&pf->lock); 653 if (err == 0) 654 return (ENOMEM); 655 return (err); 656 } 657 658 free(pf->devs_nvlbuf, M_NVLIST); 659 pf->devs_nvlbuf = nvlbuf; 660 pf->devs_nbytes = nbytes; 661 } 662 663 SNDSTAT_UNLOCK(); 664 665 if (!arg->nbytes) { 666 arg->nbytes = pf->devs_nbytes; 667 err = 0; 668 goto done; 669 } 670 if (arg->nbytes < pf->devs_nbytes) { 671 arg->nbytes = 0; 672 err = 0; 673 goto done; 674 } 675 676 err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes); 677 if (err) 678 goto done; 679 680 arg->nbytes = pf->devs_nbytes; 681 682 free(pf->devs_nvlbuf, M_NVLIST); 683 pf->devs_nvlbuf = NULL; 684 pf->devs_nbytes = 0; 685 686 done: 687 sx_unlock(&pf->lock); 688 return (err); 689 } 690 691 static int 692 sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl) 693 { 694 void *nvlbuf; 695 int err; 696 697 nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK); 698 err = copyin(unvlbuf, nvlbuf, nbytes); 699 if (err != 0) { 700 free(nvlbuf, M_DEVBUF); 701 return (err); 702 } 703 *nvl = nvlist_unpack(nvlbuf, nbytes, 0); 704 free(nvlbuf, M_DEVBUF); 705 if (nvl == NULL) { 706 return (EINVAL); 707 } 708 709 return (0); 710 } 711 712 static bool 713 sndstat_diinfo_is_sane(const nvlist_t *diinfo) 714 { 715 if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) && 716 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) && 717 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) && 718 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) && 719 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN))) 720 return (false); 721 return (true); 722 } 723 724 static bool 725 sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist) 726 { 727 if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) && 728 nvlist_exists_string(nvlist, SNDST_DSPS_DESC) && 729 nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) && 730 nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN))) 731 return (false); 732 733 if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) { 734 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) { 735 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist, 736 SNDST_DSPS_INFO_PLAY))) 737 return (false); 738 } else if (!(nvlist_exists_number(nvlist, "pminrate") && 739 nvlist_exists_number(nvlist, "pmaxrate") && 740 nvlist_exists_number(nvlist, "pfmts"))) 741 return (false); 742 } 743 744 if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) { 745 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) { 746 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist, 747 SNDST_DSPS_INFO_REC))) 748 return (false); 749 } else if (!(nvlist_exists_number(nvlist, "rminrate") && 750 nvlist_exists_number(nvlist, "rmaxrate") && 751 nvlist_exists_number(nvlist, "rfmts"))) 752 return (false); 753 } 754 755 return (true); 756 757 } 758 759 static void 760 sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate, 761 uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn, 762 uint32_t *max_chn) 763 { 764 *min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE); 765 *max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE); 766 *formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS); 767 *min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN); 768 *max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN); 769 } 770 771 static int 772 sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud) 773 { 774 const char *nameunit, *devnode, *desc; 775 unsigned int pchan, rchan; 776 uint32_t pminrate = 0, pmaxrate = 0; 777 uint32_t rminrate = 0, rmaxrate = 0; 778 uint32_t pfmts = 0, rfmts = 0; 779 uint32_t pminchn = 0, pmaxchn = 0; 780 uint32_t rminchn = 0, rmaxchn = 0; 781 nvlist_t *provider_nvl = NULL; 782 const nvlist_t *diinfo; 783 const char *provider; 784 785 devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE); 786 if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT)) 787 nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT); 788 else 789 nameunit = devnode; 790 desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC); 791 pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN); 792 rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN); 793 if (pchan != 0) { 794 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) { 795 diinfo = nvlist_get_nvlist(nvlist, 796 SNDST_DSPS_INFO_PLAY); 797 sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate, 798 &pfmts, &pminchn, &pmaxchn); 799 } else { 800 pminrate = nvlist_get_number(nvlist, "pminrate"); 801 pmaxrate = nvlist_get_number(nvlist, "pmaxrate"); 802 pfmts = nvlist_get_number(nvlist, "pfmts"); 803 } 804 } 805 if (rchan != 0) { 806 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) { 807 diinfo = nvlist_get_nvlist(nvlist, 808 SNDST_DSPS_INFO_REC); 809 sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate, 810 &rfmts, &rminchn, &rmaxchn); 811 } else { 812 rminrate = nvlist_get_number(nvlist, "rminrate"); 813 rmaxrate = nvlist_get_number(nvlist, "rmaxrate"); 814 rfmts = nvlist_get_number(nvlist, "rfmts"); 815 } 816 } 817 818 provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, ""); 819 if (provider[0] == '\0') 820 provider = NULL; 821 822 if (provider != NULL && 823 nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) { 824 provider_nvl = nvlist_clone( 825 nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)); 826 if (provider_nvl == NULL) 827 return (ENOMEM); 828 } 829 830 ud->provider = (provider != NULL) ? strdup(provider, M_DEVBUF) : NULL; 831 ud->devnode = strdup(devnode, M_DEVBUF); 832 ud->nameunit = strdup(nameunit, M_DEVBUF); 833 ud->desc = strdup(desc, M_DEVBUF); 834 ud->pchan = pchan; 835 ud->rchan = rchan; 836 ud->info_play.min_rate = pminrate; 837 ud->info_play.max_rate = pmaxrate; 838 ud->info_play.formats = pfmts; 839 ud->info_play.min_chn = pminchn; 840 ud->info_play.max_chn = pmaxchn; 841 ud->info_rec.min_rate = rminrate; 842 ud->info_rec.max_rate = rmaxrate; 843 ud->info_rec.formats = rfmts; 844 ud->info_rec.min_chn = rminchn; 845 ud->info_rec.max_chn = rmaxchn; 846 ud->provider_nvl = provider_nvl; 847 return (0); 848 } 849 850 static int 851 sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data) 852 { 853 int err; 854 nvlist_t *nvl = NULL; 855 const nvlist_t * const *dsps; 856 size_t i, ndsps; 857 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data; 858 859 if ((pf->fflags & FWRITE) == 0) { 860 err = EPERM; 861 goto done; 862 } 863 864 err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl); 865 if (err != 0) 866 goto done; 867 868 if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) { 869 err = EINVAL; 870 goto done; 871 } 872 dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps); 873 for (i = 0; i < ndsps; i++) { 874 if (!sndstat_dsp_nvlist_is_sane(dsps[i])) { 875 err = EINVAL; 876 goto done; 877 } 878 } 879 sx_xlock(&pf->lock); 880 for (i = 0; i < ndsps; i++) { 881 struct sndstat_userdev *ud = 882 malloc(sizeof(*ud), M_DEVBUF, M_WAITOK); 883 err = sndstat_dsp_unpack_nvlist(dsps[i], ud); 884 if (err) { 885 sx_unlock(&pf->lock); 886 goto done; 887 } 888 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link); 889 } 890 sx_unlock(&pf->lock); 891 892 done: 893 nvlist_destroy(nvl); 894 return (err); 895 } 896 897 static int 898 sndstat_flush_user_devs(struct sndstat_file *pf) 899 { 900 if ((pf->fflags & FWRITE) == 0) 901 return (EPERM); 902 903 sx_xlock(&pf->lock); 904 sndstat_remove_all_userdevs(pf); 905 sx_xunlock(&pf->lock); 906 907 return (0); 908 } 909 910 #ifdef COMPAT_FREEBSD32 911 static int 912 compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data) 913 { 914 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 915 struct sndstioc_nv_arg arg; 916 int err; 917 918 arg.buf = (void *)(uintptr_t)arg32->buf; 919 arg.nbytes = arg32->nbytes; 920 921 err = sndstat_get_devs(pf, (caddr_t)&arg); 922 if (err == 0) { 923 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 924 arg32->nbytes = arg.nbytes; 925 } 926 927 return (err); 928 } 929 930 static int 931 compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data) 932 { 933 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 934 struct sndstioc_nv_arg arg; 935 int err; 936 937 arg.buf = (void *)(uintptr_t)arg32->buf; 938 arg.nbytes = arg32->nbytes; 939 940 err = sndstat_add_user_devs(pf, (caddr_t)&arg); 941 if (err == 0) { 942 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 943 arg32->nbytes = arg.nbytes; 944 } 945 946 return (err); 947 } 948 #endif 949 950 static int 951 sndstat_ioctl( 952 struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 953 { 954 int err; 955 struct sndstat_file *pf; 956 957 err = devfs_get_cdevpriv((void **)&pf); 958 if (err != 0) 959 return (err); 960 961 switch (cmd) { 962 case SNDSTIOC_GET_DEVS: 963 err = sndstat_get_devs(pf, data); 964 break; 965 #ifdef COMPAT_FREEBSD32 966 case SNDSTIOC_GET_DEVS32: 967 if (!SV_CURPROC_FLAG(SV_ILP32)) { 968 err = ENODEV; 969 break; 970 } 971 err = compat_sndstat_get_devs32(pf, data); 972 break; 973 #endif 974 case SNDSTIOC_ADD_USER_DEVS: 975 err = sndstat_add_user_devs(pf, data); 976 break; 977 #ifdef COMPAT_FREEBSD32 978 case SNDSTIOC_ADD_USER_DEVS32: 979 if (!SV_CURPROC_FLAG(SV_ILP32)) { 980 err = ENODEV; 981 break; 982 } 983 err = compat_sndstat_add_user_devs32(pf, data); 984 break; 985 #endif 986 case SNDSTIOC_REFRESH_DEVS: 987 err = sndstat_refresh_devs(pf); 988 break; 989 case SNDSTIOC_FLUSH_USER_DEVS: 990 err = sndstat_flush_user_devs(pf); 991 break; 992 default: 993 err = ENODEV; 994 } 995 996 return (err); 997 } 998 999 static struct sndstat_userdev * 1000 sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n) 1001 { 1002 struct sndstat_userdev *ud; 1003 const char *e, *m; 1004 1005 ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO); 1006 1007 ud->provider = NULL; 1008 ud->provider_nvl = NULL; 1009 e = strchr(line, ':'); 1010 if (e == NULL) 1011 goto fail; 1012 ud->nameunit = strndup(line, e - line, M_DEVBUF); 1013 ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO); 1014 strlcat(ud->devnode, ud->nameunit, e - line + 1); 1015 line = e + 1; 1016 1017 e = strchr(line, '<'); 1018 if (e == NULL) 1019 goto fail; 1020 line = e + 1; 1021 e = strrchr(line, '>'); 1022 if (e == NULL) 1023 goto fail; 1024 ud->desc = strndup(line, e - line, M_DEVBUF); 1025 line = e + 1; 1026 1027 e = strchr(line, '('); 1028 if (e == NULL) 1029 goto fail; 1030 line = e + 1; 1031 e = strrchr(line, ')'); 1032 if (e == NULL) 1033 goto fail; 1034 m = strstr(line, "play"); 1035 if (m != NULL && m < e) 1036 ud->pchan = 1; 1037 m = strstr(line, "rec"); 1038 if (m != NULL && m < e) 1039 ud->rchan = 1; 1040 1041 return (ud); 1042 1043 fail: 1044 free(ud->nameunit, M_DEVBUF); 1045 free(ud->devnode, M_DEVBUF); 1046 free(ud->desc, M_DEVBUF); 1047 free(ud, M_DEVBUF); 1048 return (NULL); 1049 } 1050 1051 /************************************************************************/ 1052 1053 int 1054 sndstat_register(device_t dev, char *str) 1055 { 1056 struct sndstat_entry *ent; 1057 struct sndstat_entry *pre; 1058 const char *devtype; 1059 int type, unit; 1060 1061 unit = device_get_unit(dev); 1062 devtype = device_get_name(dev); 1063 if (!strcmp(devtype, "pcm")) 1064 type = SS_TYPE_PCM; 1065 else if (!strcmp(devtype, "midi")) 1066 type = SS_TYPE_MIDI; 1067 else if (!strcmp(devtype, "sequencer")) 1068 type = SS_TYPE_SEQUENCER; 1069 else 1070 return (EINVAL); 1071 1072 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 1073 ent->dev = dev; 1074 ent->str = str; 1075 ent->type = type; 1076 ent->unit = unit; 1077 1078 SNDSTAT_LOCK(); 1079 /* sorted list insertion */ 1080 TAILQ_FOREACH(pre, &sndstat_devlist, link) { 1081 if (pre->unit > unit) 1082 break; 1083 else if (pre->unit < unit) 1084 continue; 1085 if (pre->type > type) 1086 break; 1087 else if (pre->type < unit) 1088 continue; 1089 } 1090 if (pre == NULL) { 1091 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link); 1092 } else { 1093 TAILQ_INSERT_BEFORE(pre, ent, link); 1094 } 1095 SNDSTAT_UNLOCK(); 1096 1097 return (0); 1098 } 1099 1100 int 1101 sndstat_unregister(device_t dev) 1102 { 1103 struct sndstat_entry *ent; 1104 int error = ENXIO; 1105 1106 SNDSTAT_LOCK(); 1107 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1108 if (ent->dev == dev) { 1109 TAILQ_REMOVE(&sndstat_devlist, ent, link); 1110 free(ent, M_DEVBUF); 1111 error = 0; 1112 break; 1113 } 1114 } 1115 SNDSTAT_UNLOCK(); 1116 1117 return (error); 1118 } 1119 1120 /************************************************************************/ 1121 1122 static int 1123 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 1124 { 1125 struct snddev_info *d; 1126 struct pcm_channel *c; 1127 struct pcm_feeder *f; 1128 1129 d = device_get_softc(dev); 1130 PCM_BUSYASSERT(d); 1131 1132 if (CHN_EMPTY(d, channels.pcm)) { 1133 sbuf_printf(s, " (mixer only)"); 1134 return (0); 1135 } 1136 1137 if (verbose < 1) { 1138 sbuf_printf(s, " (%s%s%s", 1139 d->playcount ? "play" : "", 1140 (d->playcount && d->reccount) ? "/" : "", 1141 d->reccount ? "rec" : ""); 1142 } else { 1143 sbuf_printf(s, " (%dp:%dv/%dr:%dv", 1144 d->playcount, d->pvchancount, 1145 d->reccount, d->rvchancount); 1146 } 1147 sbuf_printf(s, "%s)%s", 1148 ((d->playcount != 0 && d->reccount != 0) && 1149 (d->flags & SD_F_SIMPLEX)) ? " simplex" : "", 1150 (device_get_unit(dev) == snd_unit) ? " default" : ""); 1151 1152 if (verbose <= 1) 1153 return (0); 1154 1155 sbuf_printf(s, "\n\t"); 1156 sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS); 1157 1158 CHN_FOREACH(c, d, channels.pcm) { 1159 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1160 ("hosed pcm channel setup")); 1161 1162 sbuf_printf(s, "\n\t"); 1163 1164 sbuf_printf(s, "%s[%s]: ", 1165 (c->parentchannel != NULL) ? 1166 c->parentchannel->name : "", c->name); 1167 sbuf_printf(s, "spd %d", c->speed); 1168 if (c->speed != sndbuf_getspd(c->bufhard)) { 1169 sbuf_printf(s, "/%d", 1170 sndbuf_getspd(c->bufhard)); 1171 } 1172 sbuf_printf(s, ", fmt 0x%08x", c->format); 1173 if (c->format != sndbuf_getfmt(c->bufhard)) { 1174 sbuf_printf(s, "/0x%08x", 1175 sndbuf_getfmt(c->bufhard)); 1176 } 1177 sbuf_printf(s, ", flags 0x%08x, 0x%08x", 1178 c->flags, c->feederflags); 1179 if (c->pid != -1) { 1180 sbuf_printf(s, ", pid %d (%s)", 1181 c->pid, c->comm); 1182 } 1183 sbuf_printf(s, "\n\t"); 1184 1185 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1186 1187 if (c->direction == PCMDIR_REC) { 1188 sbuf_printf(s, 1189 "overruns %d, feed %u, hfree %d, " 1190 "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1191 c->xruns, c->feedcount, 1192 sndbuf_getfree(c->bufhard), 1193 sndbuf_getfree(c->bufsoft), 1194 sndbuf_getsize(c->bufhard), 1195 sndbuf_getblksz(c->bufhard), 1196 sndbuf_getblkcnt(c->bufhard), 1197 sndbuf_getsize(c->bufsoft), 1198 sndbuf_getblksz(c->bufsoft), 1199 sndbuf_getblkcnt(c->bufsoft)); 1200 } else { 1201 sbuf_printf(s, 1202 "underruns %d, feed %u, ready %d " 1203 "[b:%d/%d/%d|bs:%d/%d/%d]", 1204 c->xruns, c->feedcount, 1205 sndbuf_getready(c->bufsoft), 1206 sndbuf_getsize(c->bufhard), 1207 sndbuf_getblksz(c->bufhard), 1208 sndbuf_getblkcnt(c->bufhard), 1209 sndbuf_getsize(c->bufsoft), 1210 sndbuf_getblksz(c->bufsoft), 1211 sndbuf_getblkcnt(c->bufsoft)); 1212 } 1213 sbuf_printf(s, "\n\t"); 1214 1215 sbuf_printf(s, "channel flags=0x%b", c->flags, CHN_F_BITS); 1216 sbuf_printf(s, "\n\t"); 1217 1218 sbuf_printf(s, "{%s}", 1219 (c->direction == PCMDIR_REC) ? "hardware" : "userland"); 1220 sbuf_printf(s, " -> "); 1221 f = c->feeder; 1222 while (f->source != NULL) 1223 f = f->source; 1224 while (f != NULL) { 1225 sbuf_printf(s, "%s", f->class->name); 1226 if (f->desc->type == FEEDER_FORMAT) { 1227 sbuf_printf(s, "(0x%08x -> 0x%08x)", 1228 f->desc->in, f->desc->out); 1229 } else if (f->desc->type == FEEDER_MATRIX) { 1230 sbuf_printf(s, "(%d.%d -> %d.%d)", 1231 AFMT_CHANNEL(f->desc->in) - 1232 AFMT_EXTCHANNEL(f->desc->in), 1233 AFMT_EXTCHANNEL(f->desc->in), 1234 AFMT_CHANNEL(f->desc->out) - 1235 AFMT_EXTCHANNEL(f->desc->out), 1236 AFMT_EXTCHANNEL(f->desc->out)); 1237 } else if (f->desc->type == FEEDER_RATE) { 1238 sbuf_printf(s, 1239 "(0x%08x q:%d %d -> %d)", 1240 f->desc->out, 1241 FEEDER_GET(f, FEEDRATE_QUALITY), 1242 FEEDER_GET(f, FEEDRATE_SRC), 1243 FEEDER_GET(f, FEEDRATE_DST)); 1244 } else { 1245 sbuf_printf(s, "(0x%08x)", 1246 f->desc->out); 1247 } 1248 sbuf_printf(s, " -> "); 1249 f = f->parent; 1250 } 1251 sbuf_printf(s, "{%s}", 1252 (c->direction == PCMDIR_REC) ? "userland" : "hardware"); 1253 } 1254 1255 return (0); 1256 } 1257 1258 static int 1259 sndstat_prepare(struct sndstat_file *pf_self) 1260 { 1261 struct sbuf *s = &pf_self->sbuf; 1262 struct sndstat_entry *ent; 1263 struct snddev_info *d; 1264 struct sndstat_file *pf; 1265 int k; 1266 1267 /* make sure buffer is reset */ 1268 sbuf_clear(s); 1269 1270 if (snd_verbose > 0) { 1271 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n", 1272 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, 1273 MACHINE_ARCH); 1274 } 1275 1276 /* generate list of installed devices */ 1277 k = 0; 1278 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1279 d = device_get_softc(ent->dev); 1280 if (!PCM_REGISTERED(d)) 1281 continue; 1282 if (!k++) 1283 sbuf_printf(s, "Installed devices:\n"); 1284 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 1285 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 1286 if (snd_verbose > 0) 1287 sbuf_printf(s, " %s", ent->str); 1288 /* XXX Need Giant magic entry ??? */ 1289 PCM_ACQUIRE_QUICK(d); 1290 sndstat_prepare_pcm(s, ent->dev, snd_verbose); 1291 PCM_RELEASE_QUICK(d); 1292 sbuf_printf(s, "\n"); 1293 } 1294 if (k == 0) 1295 sbuf_printf(s, "No devices installed.\n"); 1296 1297 /* append any input from userspace */ 1298 k = 0; 1299 TAILQ_FOREACH(pf, &sndstat_filelist, entry) { 1300 struct sndstat_userdev *ud; 1301 1302 if (pf == pf_self) 1303 continue; 1304 sx_xlock(&pf->lock); 1305 if (TAILQ_EMPTY(&pf->userdev_list)) { 1306 sx_unlock(&pf->lock); 1307 continue; 1308 } 1309 if (!k++) 1310 sbuf_printf(s, "Installed devices from userspace:\n"); 1311 TAILQ_FOREACH(ud, &pf->userdev_list, link) { 1312 const char *caps = (ud->pchan && ud->rchan) ? 1313 "play/rec" : 1314 (ud->pchan ? "play" : (ud->rchan ? "rec" : "")); 1315 sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc); 1316 sbuf_printf(s, " (%s)", caps); 1317 sbuf_printf(s, "\n"); 1318 } 1319 sx_unlock(&pf->lock); 1320 } 1321 if (k == 0) 1322 sbuf_printf(s, "No devices installed from userspace.\n"); 1323 1324 sbuf_finish(s); 1325 return (sbuf_len(s)); 1326 } 1327 1328 static void 1329 sndstat_sysinit(void *p) 1330 { 1331 sx_init(&sndstat_lock, "sndstat lock"); 1332 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 1333 UID_ROOT, GID_WHEEL, 0644, "sndstat"); 1334 } 1335 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 1336 1337 static void 1338 sndstat_sysuninit(void *p) 1339 { 1340 if (sndstat_dev != NULL) { 1341 /* destroy_dev() will wait for all references to go away */ 1342 destroy_dev(sndstat_dev); 1343 } 1344 sx_destroy(&sndstat_lock); 1345 } 1346 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 1347