1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (C) 4Front Technologies 1996-2008. 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/sysmacros.h> 30 #include <sys/list.h> 31 #include <sys/file.h> 32 #include <sys/open.h> 33 #include <sys/stat.h> 34 #include <sys/errno.h> 35 #include <sys/atomic.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 39 #include "audio_impl.h" 40 41 /* 42 * Audio Client implementation. 43 */ 44 45 /* 46 * Attenuation table for dB->linear conversion. Indexed in steps of 47 * 0.5 dB. Table size is 25 dB (first entry is handled as mute). 48 * 49 * Notably, the last item in table is taken as 0 dB (i.e. maximum volume). 50 * 51 * Table contents can be calculated as follows (requires sunmath library): 52 * 53 * scale = AUDIO_VOL_SCALE; 54 * for (i = -50; i <= 0; i++) { 55 * x = exp10(0.05 * i); 56 * printf("%d: %f %.0f\n", i, x, trunc(x * scale)); 57 * } 58 * 59 */ 60 61 static const uint16_t auimpl_db_table[AUDIO_DB_SIZE + 1] = { 62 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 63 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 64 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 65 25, 28, 32, 36, 40, 45, 51, 57, 64, 72, 66 80, 90, 101, 114, 128, 143, 161, 181, 203, 228, 67 256 68 }; 69 70 static list_t auimpl_clients; 71 static krwlock_t auimpl_client_lock; 72 static audio_client_ops_t *audio_client_ops[AUDIO_MN_TYPE_MASK + 1]; 73 74 void * 75 auclnt_get_private(audio_client_t *c) 76 { 77 return (c->c_private); 78 } 79 80 void 81 auclnt_set_private(audio_client_t *c, void *private) 82 { 83 c->c_private = private; 84 } 85 86 int 87 auclnt_set_rate(audio_stream_t *sp, int rate) 88 { 89 audio_parms_t parms; 90 int rv = 0; 91 92 /* basic sanity checks! */ 93 if ((rate < 5000) || (rate > 192000)) { 94 return (EINVAL); 95 } 96 mutex_enter(&sp->s_lock); 97 parms = *sp->s_user_parms; 98 if (rate != parms.p_rate) { 99 parms.p_rate = rate; 100 rv = auimpl_format_setup(sp, &parms); 101 } 102 mutex_exit(&sp->s_lock); 103 return (rv); 104 } 105 106 int 107 auclnt_get_rate(audio_stream_t *sp) 108 { 109 return (sp->s_user_parms->p_rate); 110 } 111 112 unsigned 113 auclnt_get_fragsz(audio_stream_t *sp) 114 { 115 return (sp->s_fragbytes); 116 } 117 118 unsigned 119 auclnt_get_framesz(audio_stream_t *sp) 120 { 121 return (sp->s_framesz); 122 } 123 124 unsigned 125 auclnt_get_nfrags(audio_stream_t *sp) 126 { 127 return (sp->s_nfrags); 128 } 129 130 unsigned 131 auclnt_get_nframes(audio_stream_t *sp) 132 { 133 return (sp->s_nframes); 134 } 135 136 void 137 auclnt_set_latency(audio_stream_t *sp, unsigned frags, unsigned bytes) 138 { 139 mutex_enter(&sp->s_lock); 140 sp->s_hintfrags = (uint16_t)frags; 141 sp->s_hintsz = bytes; 142 mutex_exit(&sp->s_lock); 143 } 144 145 uint64_t 146 auclnt_get_head(audio_stream_t *sp) 147 { 148 return (sp->s_head); 149 } 150 151 uint64_t 152 auclnt_get_tail(audio_stream_t *sp) 153 { 154 return (sp->s_tail); 155 } 156 157 unsigned 158 auclnt_get_hidx(audio_stream_t *sp) 159 { 160 return (sp->s_hidx); 161 } 162 163 unsigned 164 auclnt_get_tidx(audio_stream_t *sp) 165 { 166 return (sp->s_tidx); 167 } 168 169 audio_stream_t * 170 auclnt_input_stream(audio_client_t *c) 171 { 172 return (&c->c_istream); 173 } 174 175 audio_stream_t * 176 auclnt_output_stream(audio_client_t *c) 177 { 178 return (&c->c_ostream); 179 } 180 181 unsigned 182 auclnt_get_count(audio_stream_t *sp) 183 { 184 unsigned count; 185 186 mutex_enter(&sp->s_lock); 187 ASSERT((sp->s_head - sp->s_tail) <= sp->s_nframes); 188 count = (unsigned)(sp->s_head - sp->s_tail); 189 mutex_exit(&sp->s_lock); 190 191 return (count); 192 } 193 194 unsigned 195 auclnt_consume(audio_stream_t *sp, unsigned n) 196 { 197 mutex_enter(&sp->s_lock); 198 199 ASSERT(sp == &sp->s_client->c_istream); 200 n = max(n, sp->s_head - sp->s_tail); 201 sp->s_tail += n; 202 sp->s_tidx += n; 203 if (sp->s_tidx >= sp->s_nframes) { 204 sp->s_tidx -= sp->s_nframes; 205 } 206 207 ASSERT(sp->s_tail <= sp->s_head); 208 ASSERT(sp->s_hidx < sp->s_nframes); 209 210 mutex_exit(&sp->s_lock); 211 212 return (n); 213 } 214 215 unsigned 216 auclnt_consume_data(audio_stream_t *sp, caddr_t dst, unsigned n) 217 { 218 unsigned nframes; 219 unsigned framesz; 220 unsigned cnt; 221 caddr_t data; 222 223 mutex_enter(&sp->s_lock); 224 225 nframes = sp->s_nframes; 226 framesz = sp->s_framesz; 227 228 ASSERT(sp == &sp->s_client->c_istream); 229 ASSERT(sp->s_head >= sp->s_tail); 230 ASSERT(sp->s_tidx < nframes); 231 ASSERT(sp->s_hidx < nframes); 232 233 cnt = n = min(n, sp->s_head - sp->s_tail); 234 data = sp->s_data + (sp->s_tidx * framesz); 235 do { 236 unsigned nf, nb; 237 238 nf = min(nframes - sp->s_tidx, n); 239 nb = nf * framesz; 240 241 bcopy(data, dst, nb); 242 dst += nb; 243 data += nb; 244 245 n -= nf; 246 sp->s_tail += nf; 247 sp->s_tidx += nf; 248 if (sp->s_tidx == nframes) { 249 sp->s_tidx = 0; 250 data = sp->s_data; 251 } 252 } while (n); 253 254 ASSERT(sp->s_tail <= sp->s_head); 255 ASSERT(sp->s_tidx < nframes); 256 257 mutex_exit(&sp->s_lock); 258 259 return (cnt); 260 } 261 262 unsigned 263 auclnt_produce(audio_stream_t *sp, unsigned n) 264 { 265 mutex_enter(&sp->s_lock); 266 267 ASSERT(sp == &sp->s_client->c_ostream); 268 n = max(n, sp->s_nframes - (sp->s_head - sp->s_tail)); 269 sp->s_head += n; 270 sp->s_hidx += n; 271 if (sp->s_hidx >= sp->s_nframes) { 272 sp->s_hidx -= sp->s_nframes; 273 } 274 275 ASSERT(sp->s_tail <= sp->s_head); 276 ASSERT(sp->s_hidx < sp->s_nframes); 277 278 mutex_exit(&sp->s_lock); 279 280 return (n); 281 } 282 283 unsigned 284 auclnt_produce_data(audio_stream_t *sp, caddr_t src, unsigned n) 285 { 286 unsigned nframes; 287 unsigned framesz; 288 unsigned cnt; 289 caddr_t data; 290 291 mutex_enter(&sp->s_lock); 292 293 nframes = sp->s_nframes; 294 framesz = sp->s_framesz; 295 296 ASSERT(sp == &sp->s_client->c_ostream); 297 ASSERT(sp->s_head >= sp->s_tail); 298 ASSERT(sp->s_tidx < nframes); 299 ASSERT(sp->s_hidx < nframes); 300 301 cnt = n = min(n, nframes - (sp->s_head - sp->s_tail)); 302 data = sp->s_data + (sp->s_hidx * framesz); 303 do { 304 unsigned nf, nb; 305 306 nf = min(nframes - sp->s_hidx, n); 307 nb = nf * framesz; 308 309 bcopy(src, data, nb); 310 311 src += nb; 312 data += nb; 313 314 n -= nf; 315 sp->s_head += nf; 316 sp->s_hidx += nf; 317 if (sp->s_hidx == nframes) { 318 sp->s_hidx = 0; 319 data = sp->s_data; 320 } 321 } while (n); 322 323 ASSERT(sp->s_tail <= sp->s_head); 324 ASSERT(sp->s_hidx < nframes); 325 326 mutex_exit(&sp->s_lock); 327 328 return (cnt); 329 } 330 331 int 332 auclnt_read(audio_client_t *c, struct uio *uio) 333 { 334 audio_stream_t *sp = &c->c_istream; 335 unsigned cnt; 336 int rv = 0; 337 offset_t loff; 338 int eagain; 339 340 loff = uio->uio_loffset; 341 eagain = EAGAIN; 342 343 mutex_enter(&sp->s_lock); 344 345 if ((!sp->s_paused) && (!sp->s_running)) { 346 mutex_exit(&sp->s_lock); 347 auclnt_start(sp); 348 mutex_enter(&sp->s_lock); 349 } 350 351 ASSERT(sp->s_head >= sp->s_tail); 352 ASSERT(sp->s_tidx < sp->s_nframes); 353 ASSERT(sp->s_hidx < sp->s_nframes); 354 355 while (uio->uio_resid >= sp->s_framesz) { 356 357 while ((cnt = (sp->s_head - sp->s_tail)) == 0) { 358 if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) { 359 mutex_exit(&sp->s_lock); 360 return (eagain); 361 } 362 if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) { 363 mutex_exit(&sp->s_lock); 364 return (EINTR); 365 } 366 } 367 368 cnt = min(cnt, sp->s_nframes - sp->s_tidx); 369 cnt = min(cnt, (uio->uio_resid / sp->s_framesz)); 370 371 rv = uiomove(sp->s_data + (sp->s_tidx * sp->s_framesz), 372 cnt * sp->s_framesz, UIO_READ, uio); 373 uio->uio_loffset = loff; 374 eagain = 0; 375 376 if (rv != 0) { 377 mutex_exit(&sp->s_lock); 378 return (rv); 379 } 380 381 sp->s_tail += cnt; 382 sp->s_tidx += cnt; 383 if (sp->s_tidx == sp->s_nframes) { 384 sp->s_tidx = 0; 385 } 386 } 387 388 ASSERT(sp->s_tail <= sp->s_head); 389 ASSERT(sp->s_tidx < sp->s_nframes); 390 391 /* round off any remaining partial bits */ 392 uio->uio_resid = 0; 393 394 mutex_exit(&sp->s_lock); 395 396 return (rv); 397 } 398 399 int 400 auclnt_write(audio_client_t *c, struct uio *uio) 401 { 402 audio_stream_t *sp = &c->c_ostream; 403 unsigned cnt; 404 int rv = 0; 405 offset_t loff; 406 int eagain; 407 408 loff = uio->uio_loffset; 409 eagain = EAGAIN; 410 411 mutex_enter(&sp->s_lock); 412 413 ASSERT(sp->s_head >= sp->s_tail); 414 ASSERT(sp->s_tidx < sp->s_nframes); 415 ASSERT(sp->s_hidx < sp->s_nframes); 416 417 while (uio->uio_resid >= sp->s_framesz) { 418 419 while ((cnt = sp->s_nframes - (sp->s_head - sp->s_tail)) == 0) { 420 if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) { 421 mutex_exit(&sp->s_lock); 422 return (eagain); 423 } 424 if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) { 425 mutex_exit(&sp->s_lock); 426 return (EINTR); 427 } 428 } 429 430 cnt = min(cnt, sp->s_nframes - sp->s_hidx); 431 cnt = min(cnt, (uio->uio_resid / sp->s_framesz)); 432 433 rv = uiomove(sp->s_data + (sp->s_hidx * sp->s_framesz), 434 cnt * sp->s_framesz, UIO_WRITE, uio); 435 uio->uio_loffset = loff; 436 eagain = 0; 437 438 if (rv != 0) { 439 mutex_exit(&sp->s_lock); 440 return (rv); 441 } 442 443 sp->s_head += cnt; 444 sp->s_hidx += cnt; 445 if (sp->s_hidx == sp->s_nframes) { 446 sp->s_hidx = 0; 447 } 448 449 if ((!sp->s_paused) && (!sp->s_running) && 450 ((sp->s_head - sp->s_tail) > sp->s_fragfr)) { 451 mutex_exit(&sp->s_lock); 452 auclnt_start(sp); 453 mutex_enter(&sp->s_lock); 454 } 455 } 456 457 ASSERT(sp->s_tail <= sp->s_head); 458 ASSERT(sp->s_hidx < sp->s_nframes); 459 460 /* round off any remaining partial bits */ 461 uio->uio_resid = 0; 462 463 mutex_exit(&sp->s_lock); 464 465 return (rv); 466 } 467 468 int 469 auclnt_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp, 470 struct pollhead **phpp) 471 { 472 audio_stream_t *sp; 473 short nev = 0; 474 475 if (events & (POLLIN | POLLRDNORM)) { 476 sp = &c->c_istream; 477 mutex_enter(&sp->s_lock); 478 if ((sp->s_head - sp->s_tail) > sp->s_fragfr) { 479 nev = POLLIN | POLLRDNORM; 480 } 481 mutex_exit(&sp->s_lock); 482 } 483 484 if (events & POLLOUT) { 485 sp = &c->c_ostream; 486 mutex_enter(&sp->s_lock); 487 if ((sp->s_nframes - (sp->s_head - sp->s_tail)) > 488 sp->s_fragfr) { 489 nev = POLLOUT; 490 } 491 mutex_exit(&sp->s_lock); 492 } 493 494 if (nev) { 495 *reventsp = nev & events; 496 } else { 497 *reventsp = 0; 498 if (!anyyet) { 499 *phpp = &c->c_pollhead; 500 } 501 } 502 return (0); 503 } 504 505 void 506 auclnt_pollwakeup(audio_client_t *c, short events) 507 { 508 pollwakeup(&c->c_pollhead, events); 509 } 510 511 void 512 auclnt_get_output_qlen(audio_client_t *c, unsigned *slen, unsigned *flen) 513 { 514 audio_stream_t *sp = &c->c_ostream; 515 audio_engine_t *e = sp->s_engine; 516 uint64_t el, sl; 517 unsigned cnt, er, sr; 518 519 if (e == NULL) { 520 /* if no output engine, can't do it! */ 521 *slen = 0; 522 *flen = 0; 523 return; 524 } 525 526 mutex_enter(&e->e_lock); 527 mutex_enter(&sp->s_lock); 528 if (e->e_ops.audio_engine_qlen != NULL) { 529 el = ENG_QLEN(e) + (e->e_head - e->e_tail); 530 } else { 531 el = (e->e_head - e->e_tail); 532 } 533 er = e->e_rate; 534 sl = sp->s_cnv_cnt; 535 sr = sp->s_user_parms->p_rate; 536 cnt = (unsigned)(sp->s_head - sp->s_tail); 537 mutex_exit(&sp->s_lock); 538 mutex_exit(&e->e_lock); 539 540 /* engine frames converted to stream rate, plus stream frames */ 541 *slen = cnt; 542 *flen = ((unsigned)(((el * sr) / er) + sl)); 543 } 544 545 int 546 auclnt_set_format(audio_stream_t *sp, int fmt) 547 { 548 audio_parms_t parms; 549 int rv = 0; 550 551 /* 552 * AC3: If we select an AC3 format, then we have to allocate 553 * another engine. Normally this will be an output only 554 * engine. However, for now we aren't supporting AC3 555 * passthru. 556 */ 557 558 switch (fmt) { 559 case AUDIO_FORMAT_U8: 560 case AUDIO_FORMAT_ULAW: 561 case AUDIO_FORMAT_ALAW: 562 case AUDIO_FORMAT_S8: 563 case AUDIO_FORMAT_S16_LE: 564 case AUDIO_FORMAT_S16_BE: 565 case AUDIO_FORMAT_U16_LE: 566 case AUDIO_FORMAT_U16_BE: 567 case AUDIO_FORMAT_S24_LE: 568 case AUDIO_FORMAT_S24_BE: 569 case AUDIO_FORMAT_S32_LE: 570 case AUDIO_FORMAT_S32_BE: 571 case AUDIO_FORMAT_S24_PACKED: 572 break; 573 574 case AUDIO_FORMAT_AC3: /* AC3: PASSTHRU */ 575 default: 576 return (ENOTSUP); 577 } 578 579 580 mutex_enter(&sp->s_lock); 581 parms = *sp->s_user_parms; 582 583 /* 584 * Optimization. Some personalities send us the same format 585 * over and over again. (Sun personality does this 586 * repeatedly.) setup_src is potentially expensive, so we 587 * avoid doing it unless we really need to. 588 */ 589 if (fmt != parms.p_format) { 590 /* 591 * Note that setting the format doesn't check that the 592 * audio streams have been paused. As a result, any 593 * data still playing or recording will probably get 594 * misinterpreted. It would be smart if the client 595 * application paused/stopped playback before changing 596 * formats. 597 */ 598 parms.p_format = fmt; 599 rv = auimpl_format_setup(sp, &parms); 600 } 601 mutex_exit(&sp->s_lock); 602 603 return (rv); 604 } 605 606 int 607 auclnt_get_format(audio_stream_t *sp) 608 { 609 return (sp->s_user_parms->p_format); 610 } 611 612 int 613 auclnt_get_output_format(audio_client_t *c) 614 { 615 return (c->c_ostream.s_user_parms->p_format); 616 } 617 618 int 619 auclnt_get_input_format(audio_client_t *c) 620 { 621 return (c->c_istream.s_user_parms->p_format); 622 } 623 624 int 625 auclnt_set_channels(audio_stream_t *sp, int nchan) 626 { 627 audio_parms_t parms; 628 int rv = 0; 629 630 /* Validate setting */ 631 if ((nchan > AUDIO_MAX_CHANNELS) || (nchan < 1)) { 632 return (EINVAL); 633 } 634 635 mutex_enter(&sp->s_lock); 636 parms = *sp->s_user_parms; 637 if (nchan != parms.p_nchan) { 638 parms.p_nchan = nchan; 639 rv = auimpl_format_setup(sp, &parms); 640 } 641 mutex_exit(&sp->s_lock); 642 643 return (rv); 644 } 645 646 int 647 auclnt_get_channels(audio_stream_t *sp) 648 { 649 return (sp->s_user_parms->p_nchan); 650 } 651 652 653 static void 654 auimpl_set_gain_master(audio_stream_t *sp, uint8_t gain) 655 { 656 uint32_t scaled; 657 658 if (gain > 100) { 659 gain = 0; 660 } 661 662 mutex_enter(&sp->s_lock); 663 if (sp->s_gain_master == gain) { 664 mutex_exit(&sp->s_lock); 665 return; 666 } 667 668 /* 669 * calculate the scaled values. Done now to avoid calculations 670 * later. 671 */ 672 scaled = (gain * sp->s_gain_pct * AUDIO_DB_SIZE) / (100 * 100); 673 674 sp->s_gain_master = gain; 675 sp->s_gain_scaled = auimpl_db_table[scaled]; 676 677 if (!sp->s_muted) { 678 sp->s_gain_eff = sp->s_gain_scaled; 679 } 680 mutex_exit(&sp->s_lock); 681 } 682 683 int 684 auimpl_set_pcmvol(void *arg, uint64_t val) 685 { 686 audio_dev_t *d = arg; 687 list_t *l = &d->d_clients; 688 audio_client_t *c; 689 690 if (val > 100) { 691 return (EINVAL); 692 } 693 rw_enter(&auimpl_client_lock, RW_WRITER); 694 d->d_pcmvol = val & 0xff; 695 rw_downgrade(&auimpl_client_lock); 696 697 for (c = list_head(l); c; c = list_next(l, c)) { 698 /* don't need to check is_active here, its safe */ 699 auimpl_set_gain_master(&c->c_ostream, (uint8_t)val); 700 } 701 rw_exit(&auimpl_client_lock); 702 703 return (0); 704 } 705 706 int 707 auimpl_get_pcmvol(void *arg, uint64_t *val) 708 { 709 audio_dev_t *d = arg; 710 711 *val = d->d_pcmvol; 712 return (0); 713 } 714 715 void 716 auclnt_set_gain(audio_stream_t *sp, uint8_t gain) 717 { 718 uint32_t scaled; 719 720 if (gain > 100) { 721 gain = 0; 722 } 723 724 mutex_enter(&sp->s_lock); 725 726 /* if no change, don't bother doing updates */ 727 if (sp->s_gain_pct == gain) { 728 mutex_exit(&sp->s_lock); 729 return; 730 } 731 732 /* 733 * calculate the scaled values. Done now to avoid calculations 734 * later. 735 */ 736 scaled = (gain * sp->s_gain_master * AUDIO_DB_SIZE) / (100 * 100); 737 738 sp->s_gain_pct = gain; 739 sp->s_gain_scaled = auimpl_db_table[scaled]; 740 741 if (!sp->s_muted) { 742 sp->s_gain_eff = sp->s_gain_scaled; 743 } 744 mutex_exit(&sp->s_lock); 745 746 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 747 } 748 749 uint8_t 750 auclnt_get_gain(audio_stream_t *sp) 751 { 752 return (sp->s_gain_pct); 753 } 754 755 void 756 auclnt_set_muted(audio_stream_t *sp, boolean_t muted) 757 { 758 mutex_enter(&sp->s_lock); 759 760 /* if no work change, don't bother doing updates */ 761 if (sp->s_muted == muted) { 762 mutex_exit(&sp->s_lock); 763 return; 764 } 765 766 sp->s_muted = muted; 767 if (muted) { 768 sp->s_gain_eff = 0; 769 } else { 770 sp->s_gain_eff = sp->s_gain_scaled; 771 } 772 mutex_exit(&sp->s_lock); 773 774 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 775 } 776 777 boolean_t 778 auclnt_get_muted(audio_stream_t *sp) 779 { 780 return (sp->s_muted); 781 } 782 783 boolean_t 784 auclnt_is_running(audio_stream_t *sp) 785 { 786 return (sp->s_running); 787 } 788 789 void 790 auclnt_start(audio_stream_t *sp) 791 { 792 mutex_enter(&sp->s_lock); 793 sp->s_running = B_TRUE; 794 mutex_exit(&sp->s_lock); 795 } 796 797 void 798 auclnt_stop(audio_stream_t *sp) 799 { 800 mutex_enter(&sp->s_lock); 801 /* if running, then stop it */ 802 if (sp->s_running) { 803 sp->s_running = B_FALSE; 804 /* 805 * if we stopped the engine, we might need to wake up 806 * a thread that is waiting for drain to complete. 807 */ 808 cv_broadcast(&sp->s_cv); 809 } 810 mutex_exit(&sp->s_lock); 811 } 812 813 /* 814 * When pausing, no new data will be played after the most recently 815 * mixed samples have played. However, the audio engine will continue 816 * to play (possibly just silence). 817 * 818 * Note that we don't reference count the device, or release/close the 819 * engine here. Once fired up, the engine continues running unil it 820 * is closed. 821 */ 822 void 823 auclnt_set_paused(audio_stream_t *sp) 824 { 825 mutex_enter(&sp->s_lock); 826 if (sp->s_paused) { 827 mutex_exit(&sp->s_lock); 828 return; 829 } 830 sp->s_paused = B_TRUE; 831 mutex_exit(&sp->s_lock); 832 833 auclnt_stop(sp); 834 835 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 836 } 837 838 void 839 auclnt_clear_paused(audio_stream_t *sp) 840 { 841 mutex_enter(&sp->s_lock); 842 if (!sp->s_paused) { 843 mutex_exit(&sp->s_lock); 844 return; 845 } 846 sp->s_paused = B_FALSE; 847 mutex_exit(&sp->s_lock); 848 } 849 850 boolean_t 851 auclnt_is_paused(audio_stream_t *sp) 852 { 853 return (sp->s_paused); 854 } 855 856 void 857 auclnt_flush(audio_stream_t *sp) 858 { 859 mutex_enter(&sp->s_lock); 860 if (sp == &sp->s_client->c_ostream) { 861 sp->s_tail = sp->s_head; 862 sp->s_tidx = sp->s_hidx; 863 } else { 864 sp->s_head = sp->s_tail; 865 sp->s_hidx = sp->s_tidx; 866 } 867 sp->s_cnv_cnt = 0; 868 mutex_exit(&sp->s_lock); 869 } 870 871 int 872 auclnt_get_oflag(audio_client_t *c) 873 { 874 return (c->c_omode); 875 } 876 877 /* 878 * These routines should not be accessed by client "personality" 879 * implementations, but are for private framework use only. 880 */ 881 882 void 883 auimpl_client_init(void) 884 { 885 rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL); 886 list_create(&auimpl_clients, sizeof (struct audio_client), 887 offsetof(struct audio_client, c_global_linkage)); 888 } 889 890 void 891 auimpl_client_fini(void) 892 { 893 rw_destroy(&auimpl_client_lock); 894 list_destroy(&auimpl_clients); 895 } 896 897 static int 898 auimpl_stream_init(audio_stream_t *sp, audio_client_t *c) 899 { 900 mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL); 901 cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL); 902 sp->s_client = c; 903 904 if (sp == &c->c_ostream) { 905 sp->s_user_parms = &sp->s_cnv_src_parms; 906 sp->s_phys_parms = &sp->s_cnv_dst_parms; 907 sp->s_engcap = ENGINE_OUTPUT_CAP; 908 } else { 909 ASSERT(sp == &c->c_istream); 910 sp->s_user_parms = &sp->s_cnv_dst_parms; 911 sp->s_phys_parms = &sp->s_cnv_src_parms; 912 sp->s_engcap = ENGINE_INPUT_CAP; 913 } 914 915 /* for now initialize conversion parameters */ 916 sp->s_src_quality = 3; /* reasonable compromise for now */ 917 sp->s_cnv_dst_nchan = 2; 918 sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE; 919 sp->s_cnv_dst_rate = 48000; 920 sp->s_cnv_src_nchan = 2; 921 sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE; 922 sp->s_cnv_src_rate = 48000; 923 924 /* set volume/gain all the way up */ 925 sp->s_muted = B_FALSE; 926 sp->s_gain_pct = 0; 927 sp->s_gain_scaled = AUDIO_VOL_SCALE; 928 sp->s_gain_eff = AUDIO_VOL_SCALE; 929 930 /* 931 * We have to start off with a reasonable buffer and 932 * interrupt configuration. 933 */ 934 sp->s_allocsz = 65536; 935 sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP, 936 &sp->s_cookie); 937 if (sp->s_data == NULL) { 938 sp->s_allocsz = 0; 939 audio_dev_warn(c->c_dev, "ddi_umem_alloc failed"); 940 return (ENOMEM); 941 } 942 /* make sure no stale data left in stream */ 943 bzero(sp->s_data, sp->s_allocsz); 944 945 /* 946 * Allocate SRC and data conversion state. 947 */ 948 mutex_enter(&sp->s_lock); 949 if (auimpl_format_alloc(sp) != 0) { 950 mutex_exit(&sp->s_lock); 951 return (ENOMEM); 952 } 953 954 mutex_exit(&sp->s_lock); 955 956 return (0); 957 } 958 959 960 static void 961 audio_stream_fini(audio_stream_t *sp) 962 { 963 auimpl_format_free(sp); 964 if (sp->s_cnv_buf0) 965 kmem_free(sp->s_cnv_buf0, sp->s_cnv_max); 966 if (sp->s_cnv_buf1) 967 kmem_free(sp->s_cnv_buf1, sp->s_cnv_max); 968 mutex_destroy(&sp->s_lock); 969 cv_destroy(&sp->s_cv); 970 if (sp->s_data != NULL) { 971 ddi_umem_free(sp->s_cookie); 972 sp->s_data = NULL; 973 } 974 } 975 976 int 977 auclnt_start_drain(audio_client_t *c) 978 { 979 audio_stream_t *sp; 980 int rv; 981 982 sp = &c->c_ostream; 983 984 /* start an asynchronous drain operation. */ 985 mutex_enter(&sp->s_lock); 986 if (sp->s_paused || !sp->s_running) { 987 rv = EALREADY; 988 } else { 989 sp->s_draining = B_TRUE; 990 rv = 0; 991 } 992 mutex_exit(&sp->s_lock); 993 return (rv); 994 } 995 996 int 997 auclnt_drain(audio_client_t *c) 998 { 999 audio_stream_t *sp; 1000 1001 sp = &c->c_ostream; 1002 1003 /* 1004 * Note: Drain logic will automatically "stop" the stream when 1005 * the drain threshold has been reached. So all we have to do 1006 * is wait for the stream to stop. 1007 */ 1008 mutex_enter(&sp->s_lock); 1009 sp->s_draining = B_TRUE; 1010 while (sp->s_draining && sp->s_running && !sp->s_paused) { 1011 if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) { 1012 mutex_exit(&sp->s_lock); 1013 return (EINTR); 1014 } 1015 } 1016 mutex_exit(&sp->s_lock); 1017 return (0); 1018 } 1019 1020 audio_client_t * 1021 auimpl_client_create(dev_t dev) 1022 { 1023 audio_client_ops_t *ops; 1024 audio_client_t *c; 1025 audio_client_t *next; 1026 list_t *list = &auimpl_clients; 1027 minor_t minor; 1028 audio_dev_t *d; 1029 1030 /* validate minor number */ 1031 minor = getminor(dev) & AUDIO_MN_TYPE_MASK; 1032 if ((ops = audio_client_ops[minor]) == NULL) { 1033 return (NULL); 1034 } 1035 1036 /* lookup device instance */ 1037 if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) { 1038 audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d", 1039 getmajor(dev), getminor(dev)); 1040 return (NULL); 1041 } 1042 1043 if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) { 1044 audio_dev_warn(d, "unable to allocate client structure"); 1045 auimpl_dev_release(d); 1046 return (NULL); 1047 } 1048 c->c_dev = d; 1049 1050 mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL); 1051 cv_init(&c->c_cv, NULL, CV_DRIVER, NULL); 1052 1053 if ((auimpl_stream_init(&c->c_ostream, c) != 0) || 1054 (auimpl_stream_init(&c->c_istream, c) != 0)) { 1055 goto failed; 1056 } 1057 1058 c->c_major = getmajor(dev); 1059 c->c_origminor = getminor(dev); 1060 c->c_ops = *ops; 1061 1062 /* 1063 * We hold the client lock here. 1064 */ 1065 rw_enter(&auimpl_client_lock, RW_WRITER); 1066 1067 minor = AUDIO_MN_CLONE_MASK; 1068 for (next = list_head(list); next; next = list_next(list, next)) { 1069 if (next->c_minor > minor) { 1070 break; 1071 } 1072 minor++; 1073 } 1074 if (minor >= MAXMIN32) { 1075 rw_exit(&auimpl_client_lock); 1076 goto failed; 1077 } 1078 c->c_minor = minor; 1079 list_insert_before(list, next, c); 1080 1081 rw_exit(&auimpl_client_lock); 1082 1083 1084 return (c); 1085 1086 failed: 1087 auimpl_dev_release(d); 1088 audio_stream_fini(&c->c_ostream); 1089 audio_stream_fini(&c->c_istream); 1090 mutex_destroy(&c->c_lock); 1091 cv_destroy(&c->c_cv); 1092 kmem_free(c, sizeof (*c)); 1093 return (NULL); 1094 } 1095 1096 void 1097 auimpl_client_destroy(audio_client_t *c) 1098 { 1099 /* remove us from the global list */ 1100 rw_enter(&auimpl_client_lock, RW_WRITER); 1101 list_remove(&auimpl_clients, c); 1102 rw_exit(&auimpl_client_lock); 1103 1104 ASSERT(!c->c_istream.s_running); 1105 ASSERT(!c->c_istream.s_running); 1106 1107 /* release the device reference count */ 1108 auimpl_dev_release(c->c_dev); 1109 c->c_dev = NULL; 1110 1111 mutex_destroy(&c->c_lock); 1112 cv_destroy(&c->c_cv); 1113 1114 audio_stream_fini(&c->c_istream); 1115 audio_stream_fini(&c->c_ostream); 1116 kmem_free(c, sizeof (*c)); 1117 } 1118 1119 void 1120 auimpl_client_activate(audio_client_t *c) 1121 { 1122 rw_enter(&auimpl_client_lock, RW_WRITER); 1123 c->c_is_active = B_TRUE; 1124 rw_exit(&auimpl_client_lock); 1125 } 1126 1127 void 1128 auimpl_client_deactivate(audio_client_t *c) 1129 { 1130 rw_enter(&auimpl_client_lock, RW_WRITER); 1131 c->c_is_active = B_FALSE; 1132 rw_exit(&auimpl_client_lock); 1133 } 1134 1135 void 1136 auclnt_close(audio_client_t *c) 1137 { 1138 audio_dev_t *d = c->c_dev; 1139 1140 /* stop the engines if they are running */ 1141 auclnt_stop(&c->c_istream); 1142 auclnt_stop(&c->c_ostream); 1143 1144 rw_enter(&auimpl_client_lock, RW_WRITER); 1145 list_remove(&d->d_clients, c); 1146 rw_exit(&auimpl_client_lock); 1147 1148 mutex_enter(&c->c_lock); 1149 /* if in transition need to wait for other thread to release */ 1150 while (c->c_refcnt) { 1151 cv_wait(&c->c_cv, &c->c_lock); 1152 } 1153 mutex_exit(&c->c_lock); 1154 1155 /* release any engines that we were holding */ 1156 auimpl_engine_close(&c->c_ostream); 1157 auimpl_engine_close(&c->c_istream); 1158 } 1159 1160 audio_dev_t * 1161 auclnt_hold_dev_by_index(int index) 1162 { 1163 return (auimpl_dev_hold_by_index(index)); 1164 } 1165 1166 void 1167 auclnt_release_dev(audio_dev_t *dev) 1168 { 1169 auimpl_dev_release(dev); 1170 } 1171 1172 audio_client_t * 1173 auclnt_hold_by_devt(dev_t dev) 1174 { 1175 minor_t mn = getminor(dev); 1176 major_t mj = getmajor(dev); 1177 list_t *list; 1178 audio_client_t *c; 1179 1180 list = &auimpl_clients; 1181 /* linked list search is kind of inefficient, but it works */ 1182 rw_enter(&auimpl_client_lock, RW_READER); 1183 for (c = list_head(list); c != NULL; c = list_next(list, c)) { 1184 if ((c->c_major == mj) && (c->c_minor == mn)) { 1185 mutex_enter(&c->c_lock); 1186 if (c->c_is_active) { 1187 c->c_refcnt++; 1188 mutex_exit(&c->c_lock); 1189 } else { 1190 mutex_exit(&c->c_lock); 1191 c = NULL; 1192 } 1193 break; 1194 } 1195 } 1196 rw_exit(&auimpl_client_lock); 1197 return (c); 1198 } 1199 1200 void 1201 auclnt_release(audio_client_t *c) 1202 { 1203 mutex_enter(&c->c_lock); 1204 ASSERT(c->c_refcnt > 0); 1205 c->c_refcnt--; 1206 if (c->c_refcnt == 0) 1207 cv_broadcast(&c->c_cv); 1208 mutex_exit(&c->c_lock); 1209 } 1210 1211 unsigned 1212 auclnt_dev_get_serial(audio_dev_t *d) 1213 { 1214 return (d->d_serial); 1215 } 1216 1217 void 1218 auclnt_dev_walk_clients(audio_dev_t *d, 1219 int (*walker)(audio_client_t *, void *), 1220 void *arg) 1221 { 1222 list_t *l = &d->d_clients; 1223 audio_client_t *c; 1224 int rv; 1225 1226 rw_enter(&auimpl_client_lock, RW_READER); 1227 restart: 1228 for (c = list_head(l); c != NULL; c = list_next(l, c)) { 1229 if (!c->c_is_active) 1230 continue; 1231 rv = (walker(c, arg)); 1232 if (rv == AUDIO_WALK_STOP) { 1233 break; 1234 } else if (rv == AUDIO_WALK_RESTART) { 1235 goto restart; 1236 } 1237 } 1238 rw_exit(&auimpl_client_lock); 1239 } 1240 1241 1242 int 1243 auclnt_open(audio_client_t *c, unsigned fmts, int oflag) 1244 { 1245 audio_stream_t *sp; 1246 audio_dev_t *d = c->c_dev; 1247 int rv = 0; 1248 int flags; 1249 audio_parms_t parms; 1250 1251 flags = 0; 1252 if (oflag & FNDELAY) 1253 flags |= ENGINE_NDELAY; 1254 1255 if (oflag & FWRITE) { 1256 sp = &c->c_ostream; 1257 rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp); 1258 1259 if (rv != 0) { 1260 goto done; 1261 } 1262 mutex_enter(&sp->s_lock); 1263 parms = *sp->s_user_parms; 1264 rv = auimpl_format_setup(sp, &parms); 1265 mutex_exit(&sp->s_lock); 1266 if (rv != 0) { 1267 goto done; 1268 } 1269 } 1270 1271 if (oflag & FREAD) { 1272 sp = &c->c_istream; 1273 rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp); 1274 1275 if (rv != 0) { 1276 goto done; 1277 } 1278 mutex_enter(&sp->s_lock); 1279 parms = *sp->s_user_parms; 1280 rv = auimpl_format_setup(sp, &parms); 1281 mutex_exit(&sp->s_lock); 1282 if (rv != 0) { 1283 goto done; 1284 } 1285 } 1286 1287 done: 1288 if (rv != 0) { 1289 /* close any engines that we opened */ 1290 auimpl_engine_close(&c->c_ostream); 1291 auimpl_engine_close(&c->c_istream); 1292 } else { 1293 rw_enter(&auimpl_client_lock, RW_WRITER); 1294 list_insert_tail(&d->d_clients, c); 1295 c->c_ostream.s_gain_master = d->d_pcmvol; 1296 c->c_istream.s_gain_master = 100; 1297 rw_exit(&auimpl_client_lock); 1298 auclnt_set_gain(&c->c_ostream, 100); 1299 auclnt_set_gain(&c->c_istream, 100); 1300 } 1301 1302 return (rv); 1303 } 1304 1305 minor_t 1306 auclnt_get_minor(audio_client_t *c) 1307 { 1308 return (c->c_minor); 1309 } 1310 1311 minor_t 1312 auclnt_get_original_minor(audio_client_t *c) 1313 { 1314 return (c->c_origminor); 1315 } 1316 1317 minor_t 1318 auclnt_get_minor_type(audio_client_t *c) 1319 { 1320 return (c->c_origminor & AUDIO_MN_TYPE_MASK); 1321 } 1322 1323 queue_t * 1324 auclnt_get_rq(audio_client_t *c) 1325 { 1326 return (c->c_rq); 1327 } 1328 1329 queue_t * 1330 auclnt_get_wq(audio_client_t *c) 1331 { 1332 return (c->c_wq); 1333 } 1334 1335 pid_t 1336 auclnt_get_pid(audio_client_t *c) 1337 { 1338 return (c->c_pid); 1339 } 1340 1341 cred_t * 1342 auclnt_get_cred(audio_client_t *c) 1343 { 1344 return (c->c_cred); 1345 } 1346 1347 audio_dev_t * 1348 auclnt_get_dev(audio_client_t *c) 1349 { 1350 return (c->c_dev); 1351 } 1352 1353 int 1354 auclnt_get_dev_number(audio_dev_t *dev) 1355 { 1356 return (dev->d_number); 1357 } 1358 1359 int 1360 auclnt_get_dev_index(audio_dev_t *dev) 1361 { 1362 return (dev->d_index); 1363 } 1364 1365 const char * 1366 auclnt_get_dev_name(audio_dev_t *dev) 1367 { 1368 return (dev->d_name); 1369 } 1370 1371 const char * 1372 auclnt_get_dev_driver(audio_dev_t *dev) 1373 { 1374 return (ddi_driver_name(dev->d_dip)); 1375 } 1376 1377 dev_info_t * 1378 auclnt_get_dev_devinfo(audio_dev_t *dev) 1379 { 1380 return (dev->d_dip); 1381 } 1382 1383 const char * 1384 auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter) 1385 { 1386 struct audio_infostr *isp = *iter; 1387 if (isp == NULL) { 1388 isp = list_head(&dev->d_hwinfo); 1389 } else { 1390 isp = list_next(&dev->d_hwinfo, isp); 1391 } 1392 1393 *iter = isp; 1394 return (isp ? isp->i_line : NULL); 1395 } 1396 1397 int 1398 auclnt_get_dev_instance(audio_dev_t *dev) 1399 { 1400 return (dev->d_instance); 1401 } 1402 1403 const char * 1404 auclnt_get_dev_description(audio_dev_t *dev) 1405 { 1406 return (dev->d_desc); 1407 } 1408 1409 const char * 1410 auclnt_get_dev_version(audio_dev_t *dev) 1411 { 1412 return (dev->d_vers); 1413 } 1414 1415 unsigned 1416 auclnt_get_dev_capab(audio_dev_t *dev) 1417 { 1418 uint32_t flags; 1419 unsigned caps = 0; 1420 1421 flags = dev->d_flags; 1422 1423 if (flags & DEV_OUTPUT_CAP) 1424 caps |= AUDIO_CLIENT_CAP_PLAY; 1425 if (flags & DEV_INPUT_CAP) 1426 caps |= AUDIO_CLIENT_CAP_RECORD; 1427 if (flags & DEV_DUPLEX_CAP) 1428 caps |= AUDIO_CLIENT_CAP_DUPLEX; 1429 1430 /* AC3: deal with formats that don't support mixing */ 1431 1432 return (caps); 1433 } 1434 1435 uint64_t 1436 auclnt_get_samples(audio_stream_t *sp) 1437 { 1438 uint64_t n; 1439 1440 mutex_enter(&sp->s_lock); 1441 n = sp->s_samples; 1442 mutex_exit(&sp->s_lock); 1443 return (n); 1444 } 1445 1446 void 1447 auclnt_set_samples(audio_stream_t *sp, uint64_t n) 1448 { 1449 mutex_enter(&sp->s_lock); 1450 sp->s_samples = n; 1451 mutex_exit(&sp->s_lock); 1452 } 1453 1454 uint64_t 1455 auclnt_get_errors(audio_stream_t *sp) 1456 { 1457 uint64_t n; 1458 mutex_enter(&sp->s_lock); 1459 n = sp->s_errors; 1460 mutex_exit(&sp->s_lock); 1461 return (n); 1462 } 1463 1464 void 1465 auclnt_set_errors(audio_stream_t *sp, uint64_t n) 1466 { 1467 mutex_enter(&sp->s_lock); 1468 sp->s_errors = n; 1469 mutex_exit(&sp->s_lock); 1470 } 1471 1472 void 1473 auclnt_register_ops(minor_t minor, audio_client_ops_t *ops) 1474 { 1475 /* we control minor number allocations, no need for runtime checks */ 1476 ASSERT(minor <= AUDIO_MN_TYPE_MASK); 1477 1478 audio_client_ops[minor] = ops; 1479 } 1480 1481 int 1482 auimpl_create_minors(audio_dev_t *d) 1483 { 1484 char path[MAXPATHLEN]; 1485 int rv = 0; 1486 minor_t minor; 1487 audio_client_ops_t *ops; 1488 char *nt; 1489 1490 for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) { 1491 1492 if ((ops = audio_client_ops[i]) == NULL) 1493 continue; 1494 1495 if (ops->aco_dev_init != NULL) 1496 d->d_minor_data[i] = ops->aco_dev_init(d); 1497 1498 switch (i) { 1499 case AUDIO_MINOR_SNDSTAT: 1500 if (!(d->d_flags & DEV_SNDSTAT_CAP)) { 1501 continue; 1502 } 1503 nt = DDI_PSEUDO; 1504 break; 1505 1506 default: 1507 if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) { 1508 continue; 1509 } 1510 nt = DDI_NT_AUDIO; 1511 break; 1512 } 1513 1514 if (ops->aco_minor_prefix != NULL) { 1515 1516 minor = AUDIO_MKMN(d->d_instance, i); 1517 (void) snprintf(path, sizeof (path), 1518 "%s%d", ops->aco_minor_prefix, d->d_instance); 1519 1520 rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR, 1521 minor, nt, 0); 1522 1523 if (rv != 0) 1524 break; 1525 } 1526 } 1527 return (rv); 1528 } 1529 1530 void 1531 auimpl_remove_minors(audio_dev_t *d) 1532 { 1533 char path[MAXPATHLEN]; 1534 audio_client_ops_t *ops; 1535 1536 for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) { 1537 if ((ops = audio_client_ops[i]) == NULL) 1538 continue; 1539 if (ops->aco_minor_prefix != NULL) { 1540 (void) snprintf(path, sizeof (path), "%s%d", 1541 ops->aco_minor_prefix, d->d_instance); 1542 (void) ddi_remove_minor_node(d->d_dip, path); 1543 } 1544 1545 if (ops->aco_dev_fini != NULL) 1546 ops->aco_dev_fini(d->d_minor_data[i]); 1547 } 1548 } 1549 1550 void * 1551 auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn) 1552 { 1553 ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS)); 1554 return (d->d_minor_data[mn]); 1555 } 1556 1557 void * 1558 auclnt_get_minor_data(audio_client_t *c, minor_t mn) 1559 { 1560 ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS)); 1561 return (c->c_dev->d_minor_data[mn]); 1562 } 1563 1564 /* 1565 * This will walk all controls registered to a clients device and callback 1566 * to walker for each one with its audio_ctrl. Note this data 1567 * must be considered read only by walker. 1568 * 1569 * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE) 1570 * or stop walk (AUDIO_WALK_STOP). 1571 * 1572 */ 1573 void 1574 auclnt_walk_controls(audio_dev_t *d, 1575 int (*walker)(audio_ctrl_t *, void *), 1576 void *arg) 1577 { 1578 audio_ctrl_t *ctrl; 1579 1580 rw_enter(&d->d_ctrl_lock, RW_READER); 1581 for (ctrl = list_head(&d->d_controls); ctrl; 1582 ctrl = list_next(&d->d_controls, ctrl)) { 1583 if (walker(ctrl, arg) == AUDIO_WALK_STOP) 1584 break; 1585 } 1586 rw_exit(&d->d_ctrl_lock); 1587 } 1588 1589 /* 1590 * This will search all controls attached to an 1591 * audio device for a control with the desired name. 1592 * 1593 * d - the audio device to look on 1594 * name - name of the control being looked for. 1595 * 1596 * On successful return a ctrl handle will be returned. On 1597 * failure NULL is returned. 1598 */ 1599 audio_ctrl_t * 1600 auclnt_find_control(audio_dev_t *d, const char *name) 1601 { 1602 audio_ctrl_t *ctrl; 1603 1604 /* Verify argument */ 1605 ASSERT(d); 1606 1607 rw_enter(&d->d_ctrl_lock, RW_READER); 1608 for (ctrl = list_head(&d->d_controls); ctrl; 1609 ctrl = list_next(&d->d_controls, ctrl)) { 1610 if (strcmp(ctrl->ctrl_name, name) == 0) { 1611 rw_exit(&d->d_ctrl_lock); 1612 return (ctrl); 1613 } 1614 } 1615 rw_exit(&d->d_ctrl_lock); 1616 return (NULL); 1617 } 1618 1619 /* 1620 * Given a known control, get its attributes. 1621 * 1622 * The caller must supply a audio_ctrl_desc_t structure. Also the 1623 * values in the structure are ignored when making the call and filled 1624 * in by this function. All data pointed to by elements of desc should 1625 * be assumed read only. 1626 * 1627 * If an error occurs then a non-zero is returned. 1628 * 1629 */ 1630 int 1631 auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc) 1632 { 1633 ASSERT(ctrl); 1634 ASSERT(desc); 1635 1636 bcopy(&ctrl->ctrl_des, desc, sizeof (*desc)); 1637 return (0); 1638 } 1639 1640 int 1641 auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value) 1642 { 1643 return (audio_control_read(ctrl, value)); 1644 } 1645 1646 int 1647 auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value) 1648 { 1649 return (audio_control_write(ctrl, value)); 1650 } 1651 1652 void 1653 auclnt_warn(audio_client_t *c, const char *fmt, ...) 1654 { 1655 va_list va; 1656 1657 va_start(va, fmt); 1658 auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va); 1659 va_end(va); 1660 } 1661