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