1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 6 * Copyright (c) 2020 The FreeBSD Foundation 7 * All rights reserved. 8 * 9 * Portions of this software were developed by Ka Ho Ng 10 * under sponsorship from the FreeBSD Foundation. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifdef HAVE_KERNEL_OPTION_HEADERS 35 #include "opt_snd.h" 36 #endif 37 38 #include <sys/param.h> 39 #include <sys/lock.h> 40 #include <sys/malloc.h> 41 #include <sys/nv.h> 42 #include <sys/dnv.h> 43 #include <sys/sx.h> 44 #ifdef COMPAT_FREEBSD32 45 #include <sys/sysent.h> 46 #endif 47 48 #include <dev/sound/pcm/sound.h> 49 #include <dev/sound/pcm/pcm.h> 50 #include <dev/sound/version.h> 51 52 #define SS_TYPE_PCM 1 53 #define SS_TYPE_MIDI 2 54 #define SS_TYPE_SEQUENCER 3 55 56 static d_open_t sndstat_open; 57 static void sndstat_close(void *); 58 static d_read_t sndstat_read; 59 static d_write_t sndstat_write; 60 static d_ioctl_t sndstat_ioctl; 61 62 static struct cdevsw sndstat_cdevsw = { 63 .d_version = D_VERSION, 64 .d_open = sndstat_open, 65 .d_read = sndstat_read, 66 .d_write = sndstat_write, 67 .d_ioctl = sndstat_ioctl, 68 .d_name = "sndstat", 69 .d_flags = D_TRACKCLOSE, 70 }; 71 72 struct sndstat_entry { 73 TAILQ_ENTRY(sndstat_entry) link; 74 device_t dev; 75 char *str; 76 sndstat_handler handler; 77 int type, unit; 78 }; 79 80 struct sndstat_userdev { 81 TAILQ_ENTRY(sndstat_userdev) link; 82 char *provider; 83 char *nameunit; 84 char *devnode; 85 char *desc; 86 unsigned int pchan; 87 unsigned int rchan; 88 struct { 89 uint32_t min_rate; 90 uint32_t max_rate; 91 uint32_t formats; 92 uint32_t min_chn; 93 uint32_t max_chn; 94 } info_play, info_rec; 95 nvlist_t *provider_nvl; 96 }; 97 98 struct sndstat_file { 99 TAILQ_ENTRY(sndstat_file) entry; 100 struct sbuf sbuf; 101 struct sx lock; 102 void *devs_nvlbuf; /* (l) */ 103 size_t devs_nbytes; /* (l) */ 104 TAILQ_HEAD(, sndstat_userdev) userdev_list; /* (l) */ 105 int out_offset; 106 int in_offset; 107 int fflags; 108 }; 109 110 static struct sx sndstat_lock; 111 static struct cdev *sndstat_dev; 112 113 #define SNDSTAT_LOCK() sx_xlock(&sndstat_lock) 114 #define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock) 115 116 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist); 117 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist); 118 119 int snd_verbose = 0; 120 121 static int sndstat_prepare(struct sndstat_file *); 122 static struct sndstat_userdev * 123 sndstat_line2userdev(struct sndstat_file *, const char *, int); 124 125 static int 126 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 127 { 128 int error, verbose; 129 130 verbose = snd_verbose; 131 error = sysctl_handle_int(oidp, &verbose, 0, req); 132 if (error == 0 && req->newptr != NULL) { 133 if (verbose < 0 || verbose > 4) 134 error = EINVAL; 135 else 136 snd_verbose = verbose; 137 } 138 return (error); 139 } 140 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, 141 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int), 142 sysctl_hw_sndverbose, "I", 143 "verbosity level"); 144 145 static int 146 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 147 { 148 struct sndstat_file *pf; 149 150 pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO); 151 152 if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { 153 free(pf, M_DEVBUF); 154 return (ENOMEM); 155 } 156 157 pf->fflags = flags; 158 TAILQ_INIT(&pf->userdev_list); 159 sx_init(&pf->lock, "sndstat_file"); 160 161 SNDSTAT_LOCK(); 162 TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry); 163 SNDSTAT_UNLOCK(); 164 165 devfs_set_cdevpriv(pf, &sndstat_close); 166 167 return (0); 168 } 169 170 /* 171 * Should only be called either when: 172 * * Closing 173 * * pf->lock held 174 */ 175 static void 176 sndstat_remove_all_userdevs(struct sndstat_file *pf) 177 { 178 struct sndstat_userdev *ud; 179 180 KASSERT( 181 sx_xlocked(&pf->lock), ("%s: Called without pf->lock", __func__)); 182 while ((ud = TAILQ_FIRST(&pf->userdev_list)) != NULL) { 183 TAILQ_REMOVE(&pf->userdev_list, ud, link); 184 free(ud->provider, M_DEVBUF); 185 free(ud->desc, M_DEVBUF); 186 free(ud->devnode, M_DEVBUF); 187 free(ud->nameunit, M_DEVBUF); 188 nvlist_destroy(ud->provider_nvl); 189 free(ud, M_DEVBUF); 190 } 191 } 192 193 static void 194 sndstat_close(void *sndstat_file) 195 { 196 struct sndstat_file *pf = (struct sndstat_file *)sndstat_file; 197 198 SNDSTAT_LOCK(); 199 sbuf_delete(&pf->sbuf); 200 TAILQ_REMOVE(&sndstat_filelist, pf, entry); 201 SNDSTAT_UNLOCK(); 202 203 free(pf->devs_nvlbuf, M_NVLIST); 204 sx_xlock(&pf->lock); 205 sndstat_remove_all_userdevs(pf); 206 sx_xunlock(&pf->lock); 207 sx_destroy(&pf->lock); 208 209 free(pf, M_DEVBUF); 210 } 211 212 static int 213 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag) 214 { 215 struct sndstat_file *pf; 216 int err; 217 int len; 218 219 err = devfs_get_cdevpriv((void **)&pf); 220 if (err != 0) 221 return (err); 222 223 /* skip zero-length reads */ 224 if (buf->uio_resid == 0) 225 return (0); 226 227 SNDSTAT_LOCK(); 228 if (pf->out_offset != 0) { 229 /* don't allow both reading and writing */ 230 err = EINVAL; 231 goto done; 232 } else if (pf->in_offset == 0) { 233 err = sndstat_prepare(pf); 234 if (err <= 0) { 235 err = ENOMEM; 236 goto done; 237 } 238 } 239 len = sbuf_len(&pf->sbuf) - pf->in_offset; 240 if (len > buf->uio_resid) 241 len = buf->uio_resid; 242 if (len > 0) 243 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf); 244 pf->in_offset += len; 245 done: 246 SNDSTAT_UNLOCK(); 247 return (err); 248 } 249 250 static int 251 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag) 252 { 253 struct sndstat_file *pf; 254 uint8_t temp[64]; 255 int err; 256 int len; 257 258 err = devfs_get_cdevpriv((void **)&pf); 259 if (err != 0) 260 return (err); 261 262 /* skip zero-length writes */ 263 if (buf->uio_resid == 0) 264 return (0); 265 266 /* don't allow writing more than 64Kbytes */ 267 if (buf->uio_resid > 65536) 268 return (ENOMEM); 269 270 SNDSTAT_LOCK(); 271 if (pf->in_offset != 0) { 272 /* don't allow both reading and writing */ 273 err = EINVAL; 274 } else { 275 /* only remember the last write - allows for updates */ 276 sx_xlock(&pf->lock); 277 sndstat_remove_all_userdevs(pf); 278 sx_xunlock(&pf->lock); 279 280 while (1) { 281 len = sizeof(temp); 282 if (len > buf->uio_resid) 283 len = buf->uio_resid; 284 if (len > 0) { 285 err = uiomove(temp, len, buf); 286 if (err) 287 break; 288 } else { 289 break; 290 } 291 if (sbuf_bcat(&pf->sbuf, temp, len) < 0) { 292 err = ENOMEM; 293 break; 294 } 295 } 296 sbuf_finish(&pf->sbuf); 297 298 if (err == 0) { 299 char *line, *str; 300 301 str = sbuf_data(&pf->sbuf); 302 while ((line = strsep(&str, "\n")) != NULL) { 303 struct sndstat_userdev *ud; 304 305 ud = sndstat_line2userdev(pf, line, strlen(line)); 306 if (ud == NULL) 307 continue; 308 309 sx_xlock(&pf->lock); 310 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link); 311 sx_xunlock(&pf->lock); 312 } 313 314 pf->out_offset = sbuf_len(&pf->sbuf); 315 } else 316 pf->out_offset = 0; 317 318 sbuf_clear(&pf->sbuf); 319 } 320 SNDSTAT_UNLOCK(); 321 return (err); 322 } 323 324 static void 325 sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate, 326 uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn) 327 { 328 struct pcm_channel *c; 329 unsigned int encoding; 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 *min_rate = UINT32_MAX; 347 *max_rate = 0; 348 *minchn = UINT32_MAX; 349 *maxchn = 0; 350 encoding = 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 encoding |= AFMT_ENCODING(caps->fmtlist[i]); 364 *minchn = min(AFMT_CHANNEL(encoding), *minchn); 365 *maxchn = max(AFMT_CHANNEL(encoding), *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, caddr_t data) 623 { 624 int err; 625 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data; 626 627 SNDSTAT_LOCK(); 628 sx_xlock(&pf->lock); 629 630 if (pf->devs_nvlbuf == NULL) { 631 nvlist_t *nvl; 632 void *nvlbuf; 633 size_t nbytes; 634 int err; 635 636 sx_xunlock(&pf->lock); 637 638 err = sndstat_create_devs_nvlist(&nvl); 639 if (err) { 640 SNDSTAT_UNLOCK(); 641 return (err); 642 } 643 644 sx_xlock(&pf->lock); 645 646 nvlbuf = nvlist_pack(nvl, &nbytes); 647 err = nvlist_error(nvl); 648 nvlist_destroy(nvl); 649 if (nvlbuf == NULL || err != 0) { 650 SNDSTAT_UNLOCK(); 651 sx_xunlock(&pf->lock); 652 if (err == 0) 653 return (ENOMEM); 654 return (err); 655 } 656 657 free(pf->devs_nvlbuf, M_NVLIST); 658 pf->devs_nvlbuf = nvlbuf; 659 pf->devs_nbytes = nbytes; 660 } 661 662 SNDSTAT_UNLOCK(); 663 664 if (!arg->nbytes) { 665 arg->nbytes = pf->devs_nbytes; 666 err = 0; 667 goto done; 668 } 669 if (arg->nbytes < pf->devs_nbytes) { 670 arg->nbytes = 0; 671 err = 0; 672 goto done; 673 } 674 675 err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes); 676 if (err) 677 goto done; 678 679 arg->nbytes = pf->devs_nbytes; 680 681 free(pf->devs_nvlbuf, M_NVLIST); 682 pf->devs_nvlbuf = NULL; 683 pf->devs_nbytes = 0; 684 685 done: 686 sx_unlock(&pf->lock); 687 return (err); 688 } 689 690 static int 691 sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl) 692 { 693 void *nvlbuf; 694 int err; 695 696 nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK); 697 err = copyin(unvlbuf, nvlbuf, nbytes); 698 if (err != 0) { 699 free(nvlbuf, M_DEVBUF); 700 return (err); 701 } 702 *nvl = nvlist_unpack(nvlbuf, nbytes, 0); 703 free(nvlbuf, M_DEVBUF); 704 if (nvl == NULL) { 705 return (EINVAL); 706 } 707 708 return (0); 709 } 710 711 static bool 712 sndstat_diinfo_is_sane(const nvlist_t *diinfo) 713 { 714 if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) && 715 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) && 716 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) && 717 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) && 718 nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN))) 719 return (false); 720 return (true); 721 } 722 723 static bool 724 sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist) 725 { 726 if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) && 727 nvlist_exists_string(nvlist, SNDST_DSPS_DESC) && 728 nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) && 729 nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN))) 730 return (false); 731 732 if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) { 733 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) { 734 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist, 735 SNDST_DSPS_INFO_PLAY))) 736 return (false); 737 } else if (!(nvlist_exists_number(nvlist, "pminrate") && 738 nvlist_exists_number(nvlist, "pmaxrate") && 739 nvlist_exists_number(nvlist, "pfmts"))) 740 return (false); 741 } 742 743 if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) { 744 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) { 745 if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist, 746 SNDST_DSPS_INFO_REC))) 747 return (false); 748 } else if (!(nvlist_exists_number(nvlist, "rminrate") && 749 nvlist_exists_number(nvlist, "rmaxrate") && 750 nvlist_exists_number(nvlist, "rfmts"))) 751 return (false); 752 } 753 754 return (true); 755 756 } 757 758 static void 759 sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate, 760 uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn, 761 uint32_t *max_chn) 762 { 763 *min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE); 764 *max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE); 765 *formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS); 766 *min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN); 767 *max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN); 768 } 769 770 static int 771 sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud) 772 { 773 const char *nameunit, *devnode, *desc; 774 unsigned int pchan, rchan; 775 uint32_t pminrate = 0, pmaxrate = 0; 776 uint32_t rminrate = 0, rmaxrate = 0; 777 uint32_t pfmts = 0, rfmts = 0; 778 uint32_t pminchn = 0, pmaxchn = 0; 779 uint32_t rminchn = 0, rmaxchn = 0; 780 nvlist_t *provider_nvl = NULL; 781 const nvlist_t *diinfo; 782 const char *provider; 783 784 devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE); 785 if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT)) 786 nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT); 787 else 788 nameunit = devnode; 789 desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC); 790 pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN); 791 rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN); 792 if (pchan != 0) { 793 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) { 794 diinfo = nvlist_get_nvlist(nvlist, 795 SNDST_DSPS_INFO_PLAY); 796 sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate, 797 &pfmts, &pminchn, &pmaxchn); 798 } else { 799 pminrate = nvlist_get_number(nvlist, "pminrate"); 800 pmaxrate = nvlist_get_number(nvlist, "pmaxrate"); 801 pfmts = nvlist_get_number(nvlist, "pfmts"); 802 } 803 } 804 if (rchan != 0) { 805 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) { 806 diinfo = nvlist_get_nvlist(nvlist, 807 SNDST_DSPS_INFO_REC); 808 sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate, 809 &rfmts, &rminchn, &rmaxchn); 810 } else { 811 rminrate = nvlist_get_number(nvlist, "rminrate"); 812 rmaxrate = nvlist_get_number(nvlist, "rmaxrate"); 813 rfmts = nvlist_get_number(nvlist, "rfmts"); 814 } 815 } 816 817 provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, ""); 818 if (provider[0] == '\0') 819 provider = NULL; 820 821 if (provider != NULL && 822 nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) { 823 provider_nvl = nvlist_clone( 824 nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)); 825 if (provider_nvl == NULL) 826 return (ENOMEM); 827 } 828 829 ud->provider = (provider != NULL) ? strdup(provider, M_DEVBUF) : NULL; 830 ud->devnode = strdup(devnode, M_DEVBUF); 831 ud->nameunit = strdup(nameunit, M_DEVBUF); 832 ud->desc = strdup(desc, M_DEVBUF); 833 ud->pchan = pchan; 834 ud->rchan = rchan; 835 ud->info_play.min_rate = pminrate; 836 ud->info_play.max_rate = pmaxrate; 837 ud->info_play.formats = pfmts; 838 ud->info_play.min_chn = pminchn; 839 ud->info_play.max_chn = pmaxchn; 840 ud->info_rec.min_rate = rminrate; 841 ud->info_rec.max_rate = rmaxrate; 842 ud->info_rec.formats = rfmts; 843 ud->info_rec.min_chn = rminchn; 844 ud->info_rec.max_chn = rmaxchn; 845 ud->provider_nvl = provider_nvl; 846 return (0); 847 } 848 849 static int 850 sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data) 851 { 852 int err; 853 nvlist_t *nvl = NULL; 854 const nvlist_t * const *dsps; 855 size_t i, ndsps; 856 struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data; 857 858 if ((pf->fflags & FWRITE) == 0) { 859 err = EPERM; 860 goto done; 861 } 862 863 err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl); 864 if (err != 0) 865 goto done; 866 867 if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) { 868 err = EINVAL; 869 goto done; 870 } 871 dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps); 872 for (i = 0; i < ndsps; i++) { 873 if (!sndstat_dsp_nvlist_is_sane(dsps[i])) { 874 err = EINVAL; 875 goto done; 876 } 877 } 878 sx_xlock(&pf->lock); 879 for (i = 0; i < ndsps; i++) { 880 struct sndstat_userdev *ud = 881 malloc(sizeof(*ud), M_DEVBUF, M_WAITOK); 882 err = sndstat_dsp_unpack_nvlist(dsps[i], ud); 883 if (err) { 884 sx_unlock(&pf->lock); 885 goto done; 886 } 887 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link); 888 } 889 sx_unlock(&pf->lock); 890 891 done: 892 nvlist_destroy(nvl); 893 return (err); 894 } 895 896 static int 897 sndstat_flush_user_devs(struct sndstat_file *pf) 898 { 899 if ((pf->fflags & FWRITE) == 0) 900 return (EPERM); 901 902 sx_xlock(&pf->lock); 903 sndstat_remove_all_userdevs(pf); 904 sx_xunlock(&pf->lock); 905 906 return (0); 907 } 908 909 #ifdef COMPAT_FREEBSD32 910 static int 911 compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data) 912 { 913 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 914 struct sndstioc_nv_arg arg; 915 int err; 916 917 arg.buf = (void *)(uintptr_t)arg32->buf; 918 arg.nbytes = arg32->nbytes; 919 920 err = sndstat_get_devs(pf, (caddr_t)&arg); 921 if (err == 0) { 922 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 923 arg32->nbytes = arg.nbytes; 924 } 925 926 return (err); 927 } 928 929 static int 930 compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data) 931 { 932 struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data; 933 struct sndstioc_nv_arg arg; 934 int err; 935 936 arg.buf = (void *)(uintptr_t)arg32->buf; 937 arg.nbytes = arg32->nbytes; 938 939 err = sndstat_add_user_devs(pf, (caddr_t)&arg); 940 if (err == 0) { 941 arg32->buf = (uint32_t)(uintptr_t)arg.buf; 942 arg32->nbytes = arg.nbytes; 943 } 944 945 return (err); 946 } 947 #endif 948 949 static int 950 sndstat_ioctl( 951 struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 952 { 953 int err; 954 struct sndstat_file *pf; 955 956 err = devfs_get_cdevpriv((void **)&pf); 957 if (err != 0) 958 return (err); 959 960 switch (cmd) { 961 case SNDSTIOC_GET_DEVS: 962 err = sndstat_get_devs(pf, data); 963 break; 964 #ifdef COMPAT_FREEBSD32 965 case SNDSTIOC_GET_DEVS32: 966 if (!SV_CURPROC_FLAG(SV_ILP32)) { 967 err = ENODEV; 968 break; 969 } 970 err = compat_sndstat_get_devs32(pf, data); 971 break; 972 #endif 973 case SNDSTIOC_ADD_USER_DEVS: 974 err = sndstat_add_user_devs(pf, data); 975 break; 976 #ifdef COMPAT_FREEBSD32 977 case SNDSTIOC_ADD_USER_DEVS32: 978 if (!SV_CURPROC_FLAG(SV_ILP32)) { 979 err = ENODEV; 980 break; 981 } 982 err = compat_sndstat_add_user_devs32(pf, data); 983 break; 984 #endif 985 case SNDSTIOC_REFRESH_DEVS: 986 err = sndstat_refresh_devs(pf); 987 break; 988 case SNDSTIOC_FLUSH_USER_DEVS: 989 err = sndstat_flush_user_devs(pf); 990 break; 991 default: 992 err = ENODEV; 993 } 994 995 return (err); 996 } 997 998 static struct sndstat_userdev * 999 sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n) 1000 { 1001 struct sndstat_userdev *ud; 1002 const char *e, *m; 1003 1004 ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO); 1005 1006 ud->provider = NULL; 1007 ud->provider_nvl = NULL; 1008 e = strchr(line, ':'); 1009 if (e == NULL) 1010 goto fail; 1011 ud->nameunit = strndup(line, e - line, M_DEVBUF); 1012 ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO); 1013 strlcat(ud->devnode, ud->nameunit, e - line + 1); 1014 line = e + 1; 1015 1016 e = strchr(line, '<'); 1017 if (e == NULL) 1018 goto fail; 1019 line = e + 1; 1020 e = strrchr(line, '>'); 1021 if (e == NULL) 1022 goto fail; 1023 ud->desc = strndup(line, e - line, M_DEVBUF); 1024 line = e + 1; 1025 1026 e = strchr(line, '('); 1027 if (e == NULL) 1028 goto fail; 1029 line = e + 1; 1030 e = strrchr(line, ')'); 1031 if (e == NULL) 1032 goto fail; 1033 m = strstr(line, "play"); 1034 if (m != NULL && m < e) 1035 ud->pchan = 1; 1036 m = strstr(line, "rec"); 1037 if (m != NULL && m < e) 1038 ud->rchan = 1; 1039 1040 return (ud); 1041 1042 fail: 1043 free(ud->nameunit, M_DEVBUF); 1044 free(ud->devnode, M_DEVBUF); 1045 free(ud->desc, M_DEVBUF); 1046 free(ud, M_DEVBUF); 1047 return (NULL); 1048 } 1049 1050 /************************************************************************/ 1051 1052 int 1053 sndstat_register(device_t dev, char *str, sndstat_handler handler) 1054 { 1055 struct sndstat_entry *ent; 1056 struct sndstat_entry *pre; 1057 const char *devtype; 1058 int type, unit; 1059 1060 unit = device_get_unit(dev); 1061 devtype = device_get_name(dev); 1062 if (!strcmp(devtype, "pcm")) 1063 type = SS_TYPE_PCM; 1064 else if (!strcmp(devtype, "midi")) 1065 type = SS_TYPE_MIDI; 1066 else if (!strcmp(devtype, "sequencer")) 1067 type = SS_TYPE_SEQUENCER; 1068 else 1069 return (EINVAL); 1070 1071 ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 1072 ent->dev = dev; 1073 ent->str = str; 1074 ent->type = type; 1075 ent->unit = unit; 1076 ent->handler = handler; 1077 1078 SNDSTAT_LOCK(); 1079 /* sorted list insertion */ 1080 TAILQ_FOREACH(pre, &sndstat_devlist, link) { 1081 if (pre->unit > unit) 1082 break; 1083 else if (pre->unit < unit) 1084 continue; 1085 if (pre->type > type) 1086 break; 1087 else if (pre->type < unit) 1088 continue; 1089 } 1090 if (pre == NULL) { 1091 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link); 1092 } else { 1093 TAILQ_INSERT_BEFORE(pre, ent, link); 1094 } 1095 SNDSTAT_UNLOCK(); 1096 1097 return (0); 1098 } 1099 1100 int 1101 sndstat_unregister(device_t dev) 1102 { 1103 struct sndstat_entry *ent; 1104 int error = ENXIO; 1105 1106 SNDSTAT_LOCK(); 1107 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1108 if (ent->dev == dev) { 1109 TAILQ_REMOVE(&sndstat_devlist, ent, link); 1110 free(ent, M_DEVBUF); 1111 error = 0; 1112 break; 1113 } 1114 } 1115 SNDSTAT_UNLOCK(); 1116 1117 return (error); 1118 } 1119 1120 /************************************************************************/ 1121 1122 static int 1123 sndstat_prepare(struct sndstat_file *pf_self) 1124 { 1125 struct sbuf *s = &pf_self->sbuf; 1126 struct sndstat_entry *ent; 1127 struct snddev_info *d; 1128 struct sndstat_file *pf; 1129 int k; 1130 1131 /* make sure buffer is reset */ 1132 sbuf_clear(s); 1133 1134 if (snd_verbose > 0) { 1135 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n", 1136 (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, 1137 MACHINE_ARCH); 1138 } 1139 1140 /* generate list of installed devices */ 1141 k = 0; 1142 TAILQ_FOREACH(ent, &sndstat_devlist, link) { 1143 d = device_get_softc(ent->dev); 1144 if (!PCM_REGISTERED(d)) 1145 continue; 1146 if (!k++) 1147 sbuf_printf(s, "Installed devices:\n"); 1148 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 1149 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 1150 if (snd_verbose > 0) 1151 sbuf_printf(s, " %s", ent->str); 1152 if (ent->handler) { 1153 /* XXX Need Giant magic entry ??? */ 1154 PCM_ACQUIRE_QUICK(d); 1155 ent->handler(s, ent->dev, snd_verbose); 1156 PCM_RELEASE_QUICK(d); 1157 } 1158 sbuf_printf(s, "\n"); 1159 } 1160 if (k == 0) 1161 sbuf_printf(s, "No devices installed.\n"); 1162 1163 /* append any input from userspace */ 1164 k = 0; 1165 TAILQ_FOREACH(pf, &sndstat_filelist, entry) { 1166 struct sndstat_userdev *ud; 1167 1168 if (pf == pf_self) 1169 continue; 1170 sx_xlock(&pf->lock); 1171 if (TAILQ_EMPTY(&pf->userdev_list)) { 1172 sx_unlock(&pf->lock); 1173 continue; 1174 } 1175 if (!k++) 1176 sbuf_printf(s, "Installed devices from userspace:\n"); 1177 TAILQ_FOREACH(ud, &pf->userdev_list, link) { 1178 const char *caps = (ud->pchan && ud->rchan) ? 1179 "play/rec" : 1180 (ud->pchan ? "play" : (ud->rchan ? "rec" : "")); 1181 sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc); 1182 sbuf_printf(s, " (%s)", caps); 1183 sbuf_printf(s, "\n"); 1184 } 1185 sx_unlock(&pf->lock); 1186 } 1187 if (k == 0) 1188 sbuf_printf(s, "No devices installed from userspace.\n"); 1189 1190 sbuf_finish(s); 1191 return (sbuf_len(s)); 1192 } 1193 1194 static void 1195 sndstat_sysinit(void *p) 1196 { 1197 sx_init(&sndstat_lock, "sndstat lock"); 1198 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 1199 UID_ROOT, GID_WHEEL, 0644, "sndstat"); 1200 } 1201 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 1202 1203 static void 1204 sndstat_sysuninit(void *p) 1205 { 1206 if (sndstat_dev != NULL) { 1207 /* destroy_dev() will wait for all references to go away */ 1208 destroy_dev(sndstat_dev); 1209 } 1210 sx_destroy(&sndstat_lock); 1211 } 1212 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 1213