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