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