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