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 if (arg->nbytes > SNDST_UNVLBUF_MAX) { 868 err = ENOMEM; 869 goto done; 870 } 871 872 err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl); 873 if (err != 0) 874 goto done; 875 876 if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) { 877 err = EINVAL; 878 goto done; 879 } 880 dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps); 881 for (i = 0; i < ndsps; i++) { 882 if (!sndstat_dsp_nvlist_is_sane(dsps[i])) { 883 err = EINVAL; 884 goto done; 885 } 886 } 887 sx_xlock(&pf->lock); 888 for (i = 0; i < ndsps; i++) { 889 struct sndstat_userdev *ud = 890 malloc(sizeof(*ud), M_DEVBUF, M_WAITOK); 891 err = sndstat_dsp_unpack_nvlist(dsps[i], ud); 892 if (err) { 893 sx_unlock(&pf->lock); 894 goto done; 895 } 896 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link); 897 } 898 sx_unlock(&pf->lock); 899 900 done: 901 nvlist_destroy(nvl); 902 return (err); 903 } 904 905 static int 906 sndstat_flush_user_devs(struct sndstat_file *pf) 907 { 908 if ((pf->fflags & FWRITE) == 0) 909 return (EPERM); 910 911 sx_xlock(&pf->lock); 912 sndstat_remove_all_userdevs(pf); 913 sx_xunlock(&pf->lock); 914 915 return (0); 916 } 917 918 #ifdef COMPAT_FREEBSD32 919 static int 920 compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data) 921 { 922 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 923 struct sndstioc_nv_arg arg; 924 int err; 925 926 arg.buf = (void *)(uintptr_t)arg32->buf; 927 arg.nbytes = arg32->nbytes; 928 929 err = sndstat_get_devs(pf, (caddr_t)&arg); 930 if (err == 0) { 931 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 932 arg32->nbytes = arg.nbytes; 933 } 934 935 return (err); 936 } 937 938 static int 939 compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data) 940 { 941 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 942 struct sndstioc_nv_arg arg; 943 int err; 944 945 arg.buf = (void *)(uintptr_t)arg32->buf; 946 arg.nbytes = arg32->nbytes; 947 948 err = sndstat_add_user_devs(pf, (caddr_t)&arg); 949 if (err == 0) { 950 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 951 arg32->nbytes = arg.nbytes; 952 } 953 954 return (err); 955 } 956 #endif 957 958 static int 959 sndstat_ioctl( 960 struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 961 { 962 int err; 963 struct sndstat_file *pf; 964 965 err = devfs_get_cdevpriv((void **)&pf); 966 if (err != 0) 967 return (err); 968 969 switch (cmd) { 970 case SNDSTIOC_GET_DEVS: 971 err = sndstat_get_devs(pf, data); 972 break; 973 #ifdef COMPAT_FREEBSD32 974 case SNDSTIOC_GET_DEVS32: 975 if (!SV_CURPROC_FLAG(SV_ILP32)) { 976 err = ENODEV; 977 break; 978 } 979 err = compat_sndstat_get_devs32(pf, data); 980 break; 981 #endif 982 case SNDSTIOC_ADD_USER_DEVS: 983 err = sndstat_add_user_devs(pf, data); 984 break; 985 #ifdef COMPAT_FREEBSD32 986 case SNDSTIOC_ADD_USER_DEVS32: 987 if (!SV_CURPROC_FLAG(SV_ILP32)) { 988 err = ENODEV; 989 break; 990 } 991 err = compat_sndstat_add_user_devs32(pf, data); 992 break; 993 #endif 994 case SNDSTIOC_REFRESH_DEVS: 995 err = sndstat_refresh_devs(pf); 996 break; 997 case SNDSTIOC_FLUSH_USER_DEVS: 998 err = sndstat_flush_user_devs(pf); 999 break; 1000 default: 1001 err = ENODEV; 1002 } 1003 1004 return (err); 1005 } 1006 1007 static struct sndstat_userdev * 1008 sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n) 1009 { 1010 struct sndstat_userdev *ud; 1011 const char *e, *m; 1012 1013 ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO); 1014 1015 ud->provider = NULL; 1016 ud->provider_nvl = NULL; 1017 e = strchr(line, ':'); 1018 if (e == NULL) 1019 goto fail; 1020 ud->nameunit = strndup(line, e - line, M_DEVBUF); 1021 ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO); 1022 strlcat(ud->devnode, ud->nameunit, e - line + 1); 1023 line = e + 1; 1024 1025 e = strchr(line, '<'); 1026 if (e == NULL) 1027 goto fail; 1028 line = e + 1; 1029 e = strrchr(line, '>'); 1030 if (e == NULL) 1031 goto fail; 1032 ud->desc = strndup(line, e - line, M_DEVBUF); 1033 line = e + 1; 1034 1035 e = strchr(line, '('); 1036 if (e == NULL) 1037 goto fail; 1038 line = e + 1; 1039 e = strrchr(line, ')'); 1040 if (e == NULL) 1041 goto fail; 1042 m = strstr(line, "play"); 1043 if (m != NULL && m < e) 1044 ud->pchan = 1; 1045 m = strstr(line, "rec"); 1046 if (m != NULL && m < e) 1047 ud->rchan = 1; 1048 1049 return (ud); 1050 1051 fail: 1052 free(ud->nameunit, M_DEVBUF); 1053 free(ud->devnode, M_DEVBUF); 1054 free(ud->desc, M_DEVBUF); 1055 free(ud, M_DEVBUF); 1056 return (NULL); 1057 } 1058 1059 /************************************************************************/ 1060 1061 int 1062 sndstat_register(device_t dev, char *str) 1063 { 1064 struct sndstat_entry *ent; 1065 struct sndstat_entry *pre; 1066 const char *devtype; 1067 int type, unit; 1068 1069 unit = device_get_unit(dev); 1070 devtype = device_get_name(dev); 1071 if (!strcmp(devtype, "pcm")) 1072 type = SS_TYPE_PCM; 1073 else if (!strcmp(devtype, "midi")) 1074 type = SS_TYPE_MIDI; 1075 else if (!strcmp(devtype, "sequencer")) 1076 type = SS_TYPE_SEQUENCER; 1077 else 1078 return (EINVAL); 1079 1080 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 1081 ent->dev = dev; 1082 ent->str = str; 1083 ent->type = type; 1084 ent->unit = unit; 1085 1086 SNDSTAT_LOCK(); 1087 /* sorted list insertion */ 1088 TAILQ_FOREACH(pre, &sndstat_devlist, link) { 1089 if (pre->unit > unit) 1090 break; 1091 else if (pre->unit < unit) 1092 continue; 1093 if (pre->type > type) 1094 break; 1095 else if (pre->type < unit) 1096 continue; 1097 } 1098 if (pre == NULL) { 1099 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link); 1100 } else { 1101 TAILQ_INSERT_BEFORE(pre, ent, link); 1102 } 1103 SNDSTAT_UNLOCK(); 1104 1105 return (0); 1106 } 1107 1108 int 1109 sndstat_unregister(device_t dev) 1110 { 1111 struct sndstat_entry *ent; 1112 int error = ENXIO; 1113 1114 SNDSTAT_LOCK(); 1115 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1116 if (ent->dev == dev) { 1117 TAILQ_REMOVE(&sndstat_devlist, ent, link); 1118 free(ent, M_DEVBUF); 1119 error = 0; 1120 break; 1121 } 1122 } 1123 SNDSTAT_UNLOCK(); 1124 1125 return (error); 1126 } 1127 1128 /************************************************************************/ 1129 1130 static int 1131 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 1132 { 1133 struct snddev_info *d; 1134 struct pcm_channel *c; 1135 struct pcm_feeder *f; 1136 1137 d = device_get_softc(dev); 1138 PCM_BUSYASSERT(d); 1139 1140 if (CHN_EMPTY(d, channels.pcm)) { 1141 sbuf_printf(s, " (mixer only)"); 1142 return (0); 1143 } 1144 1145 if (verbose < 1) { 1146 sbuf_printf(s, " (%s%s%s", 1147 d->playcount ? "play" : "", 1148 (d->playcount && d->reccount) ? "/" : "", 1149 d->reccount ? "rec" : ""); 1150 } else { 1151 sbuf_printf(s, " (%dp:%dv/%dr:%dv", 1152 d->playcount, d->pvchancount, 1153 d->reccount, d->rvchancount); 1154 } 1155 sbuf_printf(s, "%s)%s", 1156 ((d->playcount != 0 && d->reccount != 0) && 1157 (d->flags & SD_F_SIMPLEX)) ? " simplex" : "", 1158 (device_get_unit(dev) == snd_unit) ? " default" : ""); 1159 1160 if (verbose <= 1) 1161 return (0); 1162 1163 sbuf_printf(s, "\n\t"); 1164 sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS); 1165 1166 CHN_FOREACH(c, d, channels.pcm) { 1167 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1168 ("hosed pcm channel setup")); 1169 1170 sbuf_printf(s, "\n\t"); 1171 1172 sbuf_printf(s, "%s[%s]: ", 1173 (c->parentchannel != NULL) ? 1174 c->parentchannel->name : "", c->name); 1175 sbuf_printf(s, "spd %d", c->speed); 1176 if (c->speed != sndbuf_getspd(c->bufhard)) { 1177 sbuf_printf(s, "/%d", 1178 sndbuf_getspd(c->bufhard)); 1179 } 1180 sbuf_printf(s, ", fmt 0x%08x", c->format); 1181 if (c->format != sndbuf_getfmt(c->bufhard)) { 1182 sbuf_printf(s, "/0x%08x", 1183 sndbuf_getfmt(c->bufhard)); 1184 } 1185 sbuf_printf(s, ", flags 0x%08x, 0x%08x", 1186 c->flags, c->feederflags); 1187 if (c->pid != -1) { 1188 sbuf_printf(s, ", pid %d (%s)", 1189 c->pid, c->comm); 1190 } 1191 sbuf_printf(s, "\n\t"); 1192 1193 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1194 1195 if (c->direction == PCMDIR_REC) { 1196 sbuf_printf(s, 1197 "overruns %d, feed %u, hfree %d, " 1198 "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1199 c->xruns, c->feedcount, 1200 sndbuf_getfree(c->bufhard), 1201 sndbuf_getfree(c->bufsoft), 1202 sndbuf_getsize(c->bufhard), 1203 sndbuf_getblksz(c->bufhard), 1204 sndbuf_getblkcnt(c->bufhard), 1205 sndbuf_getsize(c->bufsoft), 1206 sndbuf_getblksz(c->bufsoft), 1207 sndbuf_getblkcnt(c->bufsoft)); 1208 } else { 1209 sbuf_printf(s, 1210 "underruns %d, feed %u, ready %d " 1211 "[b:%d/%d/%d|bs:%d/%d/%d]", 1212 c->xruns, c->feedcount, 1213 sndbuf_getready(c->bufsoft), 1214 sndbuf_getsize(c->bufhard), 1215 sndbuf_getblksz(c->bufhard), 1216 sndbuf_getblkcnt(c->bufhard), 1217 sndbuf_getsize(c->bufsoft), 1218 sndbuf_getblksz(c->bufsoft), 1219 sndbuf_getblkcnt(c->bufsoft)); 1220 } 1221 sbuf_printf(s, "\n\t"); 1222 1223 sbuf_printf(s, "channel flags=0x%b", c->flags, CHN_F_BITS); 1224 sbuf_printf(s, "\n\t"); 1225 1226 sbuf_printf(s, "{%s}", 1227 (c->direction == PCMDIR_REC) ? "hardware" : "userland"); 1228 sbuf_printf(s, " -> "); 1229 f = c->feeder; 1230 while (f->source != NULL) 1231 f = f->source; 1232 while (f != NULL) { 1233 sbuf_printf(s, "%s", f->class->name); 1234 if (f->desc->type == FEEDER_FORMAT) { 1235 sbuf_printf(s, "(0x%08x -> 0x%08x)", 1236 f->desc->in, f->desc->out); 1237 } else if (f->desc->type == FEEDER_MATRIX) { 1238 sbuf_printf(s, "(%d.%d -> %d.%d)", 1239 AFMT_CHANNEL(f->desc->in) - 1240 AFMT_EXTCHANNEL(f->desc->in), 1241 AFMT_EXTCHANNEL(f->desc->in), 1242 AFMT_CHANNEL(f->desc->out) - 1243 AFMT_EXTCHANNEL(f->desc->out), 1244 AFMT_EXTCHANNEL(f->desc->out)); 1245 } else if (f->desc->type == FEEDER_RATE) { 1246 sbuf_printf(s, 1247 "(0x%08x q:%d %d -> %d)", 1248 f->desc->out, 1249 FEEDER_GET(f, FEEDRATE_QUALITY), 1250 FEEDER_GET(f, FEEDRATE_SRC), 1251 FEEDER_GET(f, FEEDRATE_DST)); 1252 } else { 1253 sbuf_printf(s, "(0x%08x)", 1254 f->desc->out); 1255 } 1256 sbuf_printf(s, " -> "); 1257 f = f->parent; 1258 } 1259 sbuf_printf(s, "{%s}", 1260 (c->direction == PCMDIR_REC) ? "userland" : "hardware"); 1261 } 1262 1263 return (0); 1264 } 1265 1266 static int 1267 sndstat_prepare(struct sndstat_file *pf_self) 1268 { 1269 struct sbuf *s = &pf_self->sbuf; 1270 struct sndstat_entry *ent; 1271 struct snddev_info *d; 1272 struct sndstat_file *pf; 1273 int k; 1274 1275 /* make sure buffer is reset */ 1276 sbuf_clear(s); 1277 1278 if (snd_verbose > 0) 1279 sbuf_printf(s, "FreeBSD Audio Driver\n"); 1280 1281 /* generate list of installed devices */ 1282 k = 0; 1283 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1284 d = device_get_softc(ent->dev); 1285 if (!PCM_REGISTERED(d)) 1286 continue; 1287 if (!k++) 1288 sbuf_printf(s, "Installed devices:\n"); 1289 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 1290 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 1291 if (snd_verbose > 0) 1292 sbuf_printf(s, " %s", ent->str); 1293 /* XXX Need Giant magic entry ??? */ 1294 PCM_ACQUIRE_QUICK(d); 1295 sndstat_prepare_pcm(s, ent->dev, snd_verbose); 1296 PCM_RELEASE_QUICK(d); 1297 sbuf_printf(s, "\n"); 1298 } 1299 if (k == 0) 1300 sbuf_printf(s, "No devices installed.\n"); 1301 1302 /* append any input from userspace */ 1303 k = 0; 1304 TAILQ_FOREACH(pf, &sndstat_filelist, entry) { 1305 struct sndstat_userdev *ud; 1306 1307 if (pf == pf_self) 1308 continue; 1309 sx_xlock(&pf->lock); 1310 if (TAILQ_EMPTY(&pf->userdev_list)) { 1311 sx_unlock(&pf->lock); 1312 continue; 1313 } 1314 if (!k++) 1315 sbuf_printf(s, "Installed devices from userspace:\n"); 1316 TAILQ_FOREACH(ud, &pf->userdev_list, link) { 1317 const char *caps = (ud->pchan && ud->rchan) ? 1318 "play/rec" : 1319 (ud->pchan ? "play" : (ud->rchan ? "rec" : "")); 1320 sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc); 1321 sbuf_printf(s, " (%s)", caps); 1322 sbuf_printf(s, "\n"); 1323 } 1324 sx_unlock(&pf->lock); 1325 } 1326 if (k == 0) 1327 sbuf_printf(s, "No devices installed from userspace.\n"); 1328 1329 sbuf_finish(s); 1330 return (sbuf_len(s)); 1331 } 1332 1333 static void 1334 sndstat_sysinit(void *p) 1335 { 1336 sx_init(&sndstat_lock, "sndstat lock"); 1337 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 1338 UID_ROOT, GID_WHEEL, 0644, "sndstat"); 1339 } 1340 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 1341 1342 static void 1343 sndstat_sysuninit(void *p) 1344 { 1345 if (sndstat_dev != NULL) { 1346 /* destroy_dev() will wait for all references to go away */ 1347 destroy_dev(sndstat_dev); 1348 } 1349 sx_destroy(&sndstat_lock); 1350 } 1351 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 1352