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