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