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 el = ENG_QLEN(e) + (e->e_head - e->e_tail); 529 er = e->e_rate; 530 sl = sp->s_cnv_cnt; 531 sr = sp->s_user_parms->p_rate; 532 cnt = (unsigned)(sp->s_head - sp->s_tail); 533 mutex_exit(&sp->s_lock); 534 mutex_exit(&e->e_lock); 535 536 /* engine frames converted to stream rate, plus stream frames */ 537 *slen = cnt; 538 *flen = ((unsigned)(((el * sr) / er) + sl)); 539 } 540 541 int 542 auclnt_set_format(audio_stream_t *sp, int fmt) 543 { 544 audio_parms_t parms; 545 int rv = 0; 546 547 /* 548 * AC3: If we select an AC3 format, then we have to allocate 549 * another engine. Normally this will be an output only 550 * engine. However, for now we aren't supporting AC3 551 * passthru. 552 */ 553 554 switch (fmt) { 555 case AUDIO_FORMAT_U8: 556 case AUDIO_FORMAT_ULAW: 557 case AUDIO_FORMAT_ALAW: 558 case AUDIO_FORMAT_S8: 559 case AUDIO_FORMAT_S16_LE: 560 case AUDIO_FORMAT_S16_BE: 561 case AUDIO_FORMAT_U16_LE: 562 case AUDIO_FORMAT_U16_BE: 563 case AUDIO_FORMAT_S24_LE: 564 case AUDIO_FORMAT_S24_BE: 565 case AUDIO_FORMAT_S32_LE: 566 case AUDIO_FORMAT_S32_BE: 567 case AUDIO_FORMAT_S24_PACKED: 568 break; 569 570 case AUDIO_FORMAT_AC3: /* AC3: PASSTHRU */ 571 default: 572 return (ENOTSUP); 573 } 574 575 576 mutex_enter(&sp->s_lock); 577 parms = *sp->s_user_parms; 578 579 /* 580 * Optimization. Some personalities send us the same format 581 * over and over again. (Sun personality does this 582 * repeatedly.) setup_src is potentially expensive, so we 583 * avoid doing it unless we really need to. 584 */ 585 if (fmt != parms.p_format) { 586 /* 587 * Note that setting the format doesn't check that the 588 * audio streams have been paused. As a result, any 589 * data still playing or recording will probably get 590 * misinterpreted. It would be smart if the client 591 * application paused/stopped playback before changing 592 * formats. 593 */ 594 parms.p_format = fmt; 595 rv = auimpl_format_setup(sp, &parms); 596 } 597 mutex_exit(&sp->s_lock); 598 599 return (rv); 600 } 601 602 int 603 auclnt_get_format(audio_stream_t *sp) 604 { 605 return (sp->s_user_parms->p_format); 606 } 607 608 int 609 auclnt_get_output_format(audio_client_t *c) 610 { 611 return (c->c_ostream.s_user_parms->p_format); 612 } 613 614 int 615 auclnt_get_input_format(audio_client_t *c) 616 { 617 return (c->c_istream.s_user_parms->p_format); 618 } 619 620 int 621 auclnt_set_channels(audio_stream_t *sp, int nchan) 622 { 623 audio_parms_t parms; 624 int rv = 0; 625 626 /* Validate setting */ 627 if ((nchan > AUDIO_MAX_CHANNELS) || (nchan < 1)) { 628 return (EINVAL); 629 } 630 631 mutex_enter(&sp->s_lock); 632 parms = *sp->s_user_parms; 633 if (nchan != parms.p_nchan) { 634 parms.p_nchan = nchan; 635 rv = auimpl_format_setup(sp, &parms); 636 } 637 mutex_exit(&sp->s_lock); 638 639 return (rv); 640 } 641 642 int 643 auclnt_get_channels(audio_stream_t *sp) 644 { 645 return (sp->s_user_parms->p_nchan); 646 } 647 648 649 static void 650 auimpl_set_gain_master(audio_stream_t *sp, uint8_t gain) 651 { 652 uint32_t scaled; 653 654 if (gain > 100) { 655 gain = 0; 656 } 657 658 mutex_enter(&sp->s_lock); 659 if (sp->s_gain_master == gain) { 660 mutex_exit(&sp->s_lock); 661 return; 662 } 663 664 /* 665 * calculate the scaled values. Done now to avoid calculations 666 * later. 667 */ 668 scaled = (gain * sp->s_gain_pct * AUDIO_DB_SIZE) / (100 * 100); 669 670 sp->s_gain_master = gain; 671 sp->s_gain_scaled = auimpl_db_table[scaled]; 672 673 if (!sp->s_muted) { 674 sp->s_gain_eff = sp->s_gain_scaled; 675 } 676 mutex_exit(&sp->s_lock); 677 } 678 679 int 680 auimpl_set_pcmvol(void *arg, uint64_t val) 681 { 682 audio_dev_t *d = arg; 683 list_t *l = &d->d_clients; 684 audio_client_t *c; 685 686 if (val > 100) { 687 return (EINVAL); 688 } 689 rw_enter(&auimpl_client_lock, RW_WRITER); 690 d->d_pcmvol = val & 0xff; 691 rw_downgrade(&auimpl_client_lock); 692 693 for (c = list_head(l); c; c = list_next(l, c)) { 694 /* don't need to check is_active here, its safe */ 695 auimpl_set_gain_master(&c->c_ostream, (uint8_t)val); 696 } 697 rw_exit(&auimpl_client_lock); 698 699 return (0); 700 } 701 702 int 703 auimpl_get_pcmvol(void *arg, uint64_t *val) 704 { 705 audio_dev_t *d = arg; 706 707 *val = d->d_pcmvol; 708 return (0); 709 } 710 711 void 712 auclnt_set_gain(audio_stream_t *sp, uint8_t gain) 713 { 714 uint32_t scaled; 715 716 if (gain > 100) { 717 gain = 0; 718 } 719 720 mutex_enter(&sp->s_lock); 721 722 /* if no change, don't bother doing updates */ 723 if (sp->s_gain_pct == gain) { 724 mutex_exit(&sp->s_lock); 725 return; 726 } 727 728 /* 729 * calculate the scaled values. Done now to avoid calculations 730 * later. 731 */ 732 scaled = (gain * sp->s_gain_master * AUDIO_DB_SIZE) / (100 * 100); 733 734 sp->s_gain_pct = gain; 735 sp->s_gain_scaled = auimpl_db_table[scaled]; 736 737 if (!sp->s_muted) { 738 sp->s_gain_eff = sp->s_gain_scaled; 739 } 740 mutex_exit(&sp->s_lock); 741 742 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 743 } 744 745 uint8_t 746 auclnt_get_gain(audio_stream_t *sp) 747 { 748 return (sp->s_gain_pct); 749 } 750 751 void 752 auclnt_set_muted(audio_stream_t *sp, boolean_t muted) 753 { 754 mutex_enter(&sp->s_lock); 755 756 /* if no work change, don't bother doing updates */ 757 if (sp->s_muted == muted) { 758 mutex_exit(&sp->s_lock); 759 return; 760 } 761 762 sp->s_muted = muted; 763 if (muted) { 764 sp->s_gain_eff = 0; 765 } else { 766 sp->s_gain_eff = sp->s_gain_scaled; 767 } 768 mutex_exit(&sp->s_lock); 769 770 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 771 } 772 773 boolean_t 774 auclnt_get_muted(audio_stream_t *sp) 775 { 776 return (sp->s_muted); 777 } 778 779 boolean_t 780 auclnt_is_running(audio_stream_t *sp) 781 { 782 return (sp->s_running); 783 } 784 785 void 786 auclnt_start(audio_stream_t *sp) 787 { 788 mutex_enter(&sp->s_lock); 789 sp->s_running = B_TRUE; 790 mutex_exit(&sp->s_lock); 791 } 792 793 void 794 auclnt_stop(audio_stream_t *sp) 795 { 796 mutex_enter(&sp->s_lock); 797 /* if running, then stop it */ 798 if (sp->s_running) { 799 sp->s_running = B_FALSE; 800 /* 801 * if we stopped the engine, we might need to wake up 802 * a thread that is waiting for drain to complete. 803 */ 804 cv_broadcast(&sp->s_cv); 805 } 806 mutex_exit(&sp->s_lock); 807 } 808 809 /* 810 * When pausing, no new data will be played after the most recently 811 * mixed samples have played. However, the audio engine will continue 812 * to play (possibly just silence). 813 * 814 * Note that we don't reference count the device, or release/close the 815 * engine here. Once fired up, the engine continues running unil it 816 * is closed. 817 */ 818 void 819 auclnt_set_paused(audio_stream_t *sp) 820 { 821 mutex_enter(&sp->s_lock); 822 if (sp->s_paused) { 823 mutex_exit(&sp->s_lock); 824 return; 825 } 826 sp->s_paused = B_TRUE; 827 mutex_exit(&sp->s_lock); 828 829 auclnt_stop(sp); 830 831 atomic_inc_uint(&sp->s_client->c_dev->d_serial); 832 } 833 834 void 835 auclnt_clear_paused(audio_stream_t *sp) 836 { 837 mutex_enter(&sp->s_lock); 838 if (!sp->s_paused) { 839 mutex_exit(&sp->s_lock); 840 return; 841 } 842 sp->s_paused = B_FALSE; 843 mutex_exit(&sp->s_lock); 844 } 845 846 boolean_t 847 auclnt_is_paused(audio_stream_t *sp) 848 { 849 return (sp->s_paused); 850 } 851 852 void 853 auclnt_flush(audio_stream_t *sp) 854 { 855 mutex_enter(&sp->s_lock); 856 if (sp == &sp->s_client->c_ostream) { 857 sp->s_tail = sp->s_head; 858 sp->s_tidx = sp->s_hidx; 859 } else { 860 sp->s_head = sp->s_tail; 861 sp->s_hidx = sp->s_tidx; 862 } 863 sp->s_cnv_cnt = 0; 864 mutex_exit(&sp->s_lock); 865 } 866 867 int 868 auclnt_get_oflag(audio_client_t *c) 869 { 870 return (c->c_omode); 871 } 872 873 /* 874 * These routines should not be accessed by client "personality" 875 * implementations, but are for private framework use only. 876 */ 877 878 void 879 auimpl_client_init(void) 880 { 881 rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL); 882 list_create(&auimpl_clients, sizeof (struct audio_client), 883 offsetof(struct audio_client, c_global_linkage)); 884 } 885 886 void 887 auimpl_client_fini(void) 888 { 889 rw_destroy(&auimpl_client_lock); 890 list_destroy(&auimpl_clients); 891 } 892 893 static int 894 auimpl_stream_init(audio_stream_t *sp, audio_client_t *c) 895 { 896 mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL); 897 cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL); 898 sp->s_client = c; 899 900 if (sp == &c->c_ostream) { 901 sp->s_user_parms = &sp->s_cnv_src_parms; 902 sp->s_phys_parms = &sp->s_cnv_dst_parms; 903 sp->s_engcap = ENGINE_OUTPUT_CAP; 904 } else { 905 ASSERT(sp == &c->c_istream); 906 sp->s_user_parms = &sp->s_cnv_dst_parms; 907 sp->s_phys_parms = &sp->s_cnv_src_parms; 908 sp->s_engcap = ENGINE_INPUT_CAP; 909 } 910 911 /* for now initialize conversion parameters */ 912 sp->s_src_quality = 3; /* reasonable compromise for now */ 913 sp->s_cnv_dst_nchan = 2; 914 sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE; 915 sp->s_cnv_dst_rate = 48000; 916 sp->s_cnv_src_nchan = 2; 917 sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE; 918 sp->s_cnv_src_rate = 48000; 919 920 /* set volume/gain all the way up */ 921 sp->s_muted = B_FALSE; 922 sp->s_gain_pct = 0; 923 sp->s_gain_scaled = AUDIO_VOL_SCALE; 924 sp->s_gain_eff = AUDIO_VOL_SCALE; 925 926 /* 927 * We have to start off with a reasonable buffer and 928 * interrupt configuration. 929 */ 930 sp->s_allocsz = 65536; 931 sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP, 932 &sp->s_cookie); 933 if (sp->s_data == NULL) { 934 sp->s_allocsz = 0; 935 audio_dev_warn(c->c_dev, "ddi_umem_alloc failed"); 936 return (ENOMEM); 937 } 938 /* make sure no stale data left in stream */ 939 bzero(sp->s_data, sp->s_allocsz); 940 941 /* 942 * Allocate SRC and data conversion state. 943 */ 944 mutex_enter(&sp->s_lock); 945 if (auimpl_format_alloc(sp) != 0) { 946 mutex_exit(&sp->s_lock); 947 return (ENOMEM); 948 } 949 950 mutex_exit(&sp->s_lock); 951 952 return (0); 953 } 954 955 956 static void 957 audio_stream_fini(audio_stream_t *sp) 958 { 959 auimpl_format_free(sp); 960 if (sp->s_cnv_buf0) 961 kmem_free(sp->s_cnv_buf0, sp->s_cnv_max); 962 if (sp->s_cnv_buf1) 963 kmem_free(sp->s_cnv_buf1, sp->s_cnv_max); 964 mutex_destroy(&sp->s_lock); 965 cv_destroy(&sp->s_cv); 966 if (sp->s_data != NULL) { 967 ddi_umem_free(sp->s_cookie); 968 sp->s_data = NULL; 969 } 970 } 971 972 int 973 auclnt_start_drain(audio_client_t *c) 974 { 975 audio_stream_t *sp; 976 int rv; 977 978 sp = &c->c_ostream; 979 980 /* start an asynchronous drain operation. */ 981 mutex_enter(&sp->s_lock); 982 if (sp->s_paused || !sp->s_running) { 983 rv = EALREADY; 984 } else { 985 sp->s_draining = B_TRUE; 986 rv = 0; 987 } 988 mutex_exit(&sp->s_lock); 989 return (rv); 990 } 991 992 int 993 auclnt_drain(audio_client_t *c) 994 { 995 audio_stream_t *sp; 996 997 sp = &c->c_ostream; 998 999 /* 1000 * Note: Drain logic will automatically "stop" the stream when 1001 * the drain threshold has been reached. So all we have to do 1002 * is wait for the stream to stop. 1003 */ 1004 mutex_enter(&sp->s_lock); 1005 sp->s_draining = B_TRUE; 1006 while (sp->s_draining && sp->s_running && !sp->s_paused) { 1007 if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) { 1008 mutex_exit(&sp->s_lock); 1009 return (EINTR); 1010 } 1011 } 1012 mutex_exit(&sp->s_lock); 1013 return (0); 1014 } 1015 1016 audio_client_t * 1017 auimpl_client_create(dev_t dev) 1018 { 1019 audio_client_ops_t *ops; 1020 audio_client_t *c; 1021 audio_client_t *next; 1022 list_t *list = &auimpl_clients; 1023 minor_t minor; 1024 audio_dev_t *d; 1025 1026 /* validate minor number */ 1027 minor = getminor(dev) & AUDIO_MN_TYPE_MASK; 1028 if ((ops = audio_client_ops[minor]) == NULL) { 1029 return (NULL); 1030 } 1031 1032 /* lookup device instance */ 1033 if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) { 1034 audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d", 1035 getmajor(dev), getminor(dev)); 1036 return (NULL); 1037 } 1038 1039 if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) { 1040 audio_dev_warn(d, "unable to allocate client structure"); 1041 auimpl_dev_release(d); 1042 return (NULL); 1043 } 1044 c->c_dev = d; 1045 1046 mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL); 1047 cv_init(&c->c_cv, NULL, CV_DRIVER, NULL); 1048 1049 if ((auimpl_stream_init(&c->c_ostream, c) != 0) || 1050 (auimpl_stream_init(&c->c_istream, c) != 0)) { 1051 goto failed; 1052 } 1053 1054 c->c_major = getmajor(dev); 1055 c->c_origminor = getminor(dev); 1056 c->c_ops = *ops; 1057 1058 /* 1059 * We hold the client lock here. 1060 */ 1061 rw_enter(&auimpl_client_lock, RW_WRITER); 1062 1063 minor = AUDIO_MN_CLONE_MASK; 1064 for (next = list_head(list); next; next = list_next(list, next)) { 1065 if (next->c_minor > minor) { 1066 break; 1067 } 1068 minor++; 1069 } 1070 if (minor >= MAXMIN32) { 1071 rw_exit(&auimpl_client_lock); 1072 goto failed; 1073 } 1074 c->c_minor = minor; 1075 list_insert_before(list, next, c); 1076 1077 rw_exit(&auimpl_client_lock); 1078 1079 1080 return (c); 1081 1082 failed: 1083 auimpl_dev_release(d); 1084 audio_stream_fini(&c->c_ostream); 1085 audio_stream_fini(&c->c_istream); 1086 mutex_destroy(&c->c_lock); 1087 cv_destroy(&c->c_cv); 1088 kmem_free(c, sizeof (*c)); 1089 return (NULL); 1090 } 1091 1092 void 1093 auimpl_client_destroy(audio_client_t *c) 1094 { 1095 /* remove us from the global list */ 1096 rw_enter(&auimpl_client_lock, RW_WRITER); 1097 list_remove(&auimpl_clients, c); 1098 rw_exit(&auimpl_client_lock); 1099 1100 ASSERT(!c->c_istream.s_running); 1101 ASSERT(!c->c_istream.s_running); 1102 1103 /* release the device reference count */ 1104 auimpl_dev_release(c->c_dev); 1105 c->c_dev = NULL; 1106 1107 mutex_destroy(&c->c_lock); 1108 cv_destroy(&c->c_cv); 1109 1110 audio_stream_fini(&c->c_istream); 1111 audio_stream_fini(&c->c_ostream); 1112 kmem_free(c, sizeof (*c)); 1113 } 1114 1115 void 1116 auimpl_client_activate(audio_client_t *c) 1117 { 1118 rw_enter(&auimpl_client_lock, RW_WRITER); 1119 c->c_is_active = B_TRUE; 1120 rw_exit(&auimpl_client_lock); 1121 } 1122 1123 void 1124 auimpl_client_deactivate(audio_client_t *c) 1125 { 1126 rw_enter(&auimpl_client_lock, RW_WRITER); 1127 c->c_is_active = B_FALSE; 1128 rw_exit(&auimpl_client_lock); 1129 } 1130 1131 void 1132 auclnt_close(audio_client_t *c) 1133 { 1134 audio_dev_t *d = c->c_dev; 1135 1136 /* stop the engines if they are running */ 1137 auclnt_stop(&c->c_istream); 1138 auclnt_stop(&c->c_ostream); 1139 1140 rw_enter(&auimpl_client_lock, RW_WRITER); 1141 list_remove(&d->d_clients, c); 1142 rw_exit(&auimpl_client_lock); 1143 1144 mutex_enter(&c->c_lock); 1145 /* if in transition need to wait for other thread to release */ 1146 while (c->c_refcnt) { 1147 cv_wait(&c->c_cv, &c->c_lock); 1148 } 1149 mutex_exit(&c->c_lock); 1150 1151 /* release any engines that we were holding */ 1152 auimpl_engine_close(&c->c_ostream); 1153 auimpl_engine_close(&c->c_istream); 1154 } 1155 1156 audio_dev_t * 1157 auclnt_hold_dev_by_index(int index) 1158 { 1159 return (auimpl_dev_hold_by_index(index)); 1160 } 1161 1162 void 1163 auclnt_release_dev(audio_dev_t *dev) 1164 { 1165 auimpl_dev_release(dev); 1166 } 1167 1168 audio_client_t * 1169 auclnt_hold_by_devt(dev_t dev) 1170 { 1171 minor_t mn = getminor(dev); 1172 major_t mj = getmajor(dev); 1173 list_t *list; 1174 audio_client_t *c; 1175 1176 list = &auimpl_clients; 1177 /* linked list search is kind of inefficient, but it works */ 1178 rw_enter(&auimpl_client_lock, RW_READER); 1179 for (c = list_head(list); c != NULL; c = list_next(list, c)) { 1180 if ((c->c_major == mj) && (c->c_minor == mn)) { 1181 mutex_enter(&c->c_lock); 1182 if (c->c_is_active) { 1183 c->c_refcnt++; 1184 mutex_exit(&c->c_lock); 1185 } else { 1186 mutex_exit(&c->c_lock); 1187 c = NULL; 1188 } 1189 break; 1190 } 1191 } 1192 rw_exit(&auimpl_client_lock); 1193 return (c); 1194 } 1195 1196 void 1197 auclnt_release(audio_client_t *c) 1198 { 1199 mutex_enter(&c->c_lock); 1200 ASSERT(c->c_refcnt > 0); 1201 c->c_refcnt--; 1202 if (c->c_refcnt == 0) 1203 cv_broadcast(&c->c_cv); 1204 mutex_exit(&c->c_lock); 1205 } 1206 1207 unsigned 1208 auclnt_dev_get_serial(audio_dev_t *d) 1209 { 1210 return (d->d_serial); 1211 } 1212 1213 void 1214 auclnt_dev_walk_clients(audio_dev_t *d, 1215 int (*walker)(audio_client_t *, void *), 1216 void *arg) 1217 { 1218 list_t *l = &d->d_clients; 1219 audio_client_t *c; 1220 int rv; 1221 1222 rw_enter(&auimpl_client_lock, RW_READER); 1223 restart: 1224 for (c = list_head(l); c != NULL; c = list_next(l, c)) { 1225 if (!c->c_is_active) 1226 continue; 1227 rv = (walker(c, arg)); 1228 if (rv == AUDIO_WALK_STOP) { 1229 break; 1230 } else if (rv == AUDIO_WALK_RESTART) { 1231 goto restart; 1232 } 1233 } 1234 rw_exit(&auimpl_client_lock); 1235 } 1236 1237 1238 int 1239 auclnt_open(audio_client_t *c, unsigned fmts, int oflag) 1240 { 1241 audio_stream_t *sp; 1242 audio_dev_t *d = c->c_dev; 1243 int rv = 0; 1244 int flags; 1245 audio_parms_t parms; 1246 1247 flags = 0; 1248 if (oflag & FNDELAY) 1249 flags |= ENGINE_NDELAY; 1250 1251 if (oflag & FWRITE) { 1252 sp = &c->c_ostream; 1253 rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp); 1254 1255 if (rv != 0) { 1256 goto done; 1257 } 1258 mutex_enter(&sp->s_lock); 1259 parms = *sp->s_user_parms; 1260 rv = auimpl_format_setup(sp, &parms); 1261 mutex_exit(&sp->s_lock); 1262 if (rv != 0) { 1263 goto done; 1264 } 1265 } 1266 1267 if (oflag & FREAD) { 1268 sp = &c->c_istream; 1269 rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp); 1270 1271 if (rv != 0) { 1272 goto done; 1273 } 1274 mutex_enter(&sp->s_lock); 1275 parms = *sp->s_user_parms; 1276 rv = auimpl_format_setup(sp, &parms); 1277 mutex_exit(&sp->s_lock); 1278 if (rv != 0) { 1279 goto done; 1280 } 1281 } 1282 1283 done: 1284 if (rv != 0) { 1285 /* close any engines that we opened */ 1286 auimpl_engine_close(&c->c_ostream); 1287 auimpl_engine_close(&c->c_istream); 1288 } else { 1289 rw_enter(&auimpl_client_lock, RW_WRITER); 1290 list_insert_tail(&d->d_clients, c); 1291 c->c_ostream.s_gain_master = d->d_pcmvol; 1292 c->c_istream.s_gain_master = 100; 1293 rw_exit(&auimpl_client_lock); 1294 auclnt_set_gain(&c->c_ostream, 100); 1295 auclnt_set_gain(&c->c_istream, 100); 1296 } 1297 1298 return (rv); 1299 } 1300 1301 minor_t 1302 auclnt_get_minor(audio_client_t *c) 1303 { 1304 return (c->c_minor); 1305 } 1306 1307 minor_t 1308 auclnt_get_original_minor(audio_client_t *c) 1309 { 1310 return (c->c_origminor); 1311 } 1312 1313 minor_t 1314 auclnt_get_minor_type(audio_client_t *c) 1315 { 1316 return (c->c_origminor & AUDIO_MN_TYPE_MASK); 1317 } 1318 1319 queue_t * 1320 auclnt_get_rq(audio_client_t *c) 1321 { 1322 return (c->c_rq); 1323 } 1324 1325 queue_t * 1326 auclnt_get_wq(audio_client_t *c) 1327 { 1328 return (c->c_wq); 1329 } 1330 1331 pid_t 1332 auclnt_get_pid(audio_client_t *c) 1333 { 1334 return (c->c_pid); 1335 } 1336 1337 cred_t * 1338 auclnt_get_cred(audio_client_t *c) 1339 { 1340 return (c->c_cred); 1341 } 1342 1343 audio_dev_t * 1344 auclnt_get_dev(audio_client_t *c) 1345 { 1346 return (c->c_dev); 1347 } 1348 1349 int 1350 auclnt_get_dev_number(audio_dev_t *dev) 1351 { 1352 return (dev->d_number); 1353 } 1354 1355 int 1356 auclnt_get_dev_index(audio_dev_t *dev) 1357 { 1358 return (dev->d_index); 1359 } 1360 1361 const char * 1362 auclnt_get_dev_name(audio_dev_t *dev) 1363 { 1364 return (dev->d_name); 1365 } 1366 1367 const char * 1368 auclnt_get_dev_driver(audio_dev_t *dev) 1369 { 1370 return (ddi_driver_name(dev->d_dip)); 1371 } 1372 1373 dev_info_t * 1374 auclnt_get_dev_devinfo(audio_dev_t *dev) 1375 { 1376 return (dev->d_dip); 1377 } 1378 1379 const char * 1380 auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter) 1381 { 1382 struct audio_infostr *isp = *iter; 1383 if (isp == NULL) { 1384 isp = list_head(&dev->d_hwinfo); 1385 } else { 1386 isp = list_next(&dev->d_hwinfo, isp); 1387 } 1388 1389 *iter = isp; 1390 return (isp ? isp->i_line : NULL); 1391 } 1392 1393 int 1394 auclnt_get_dev_instance(audio_dev_t *dev) 1395 { 1396 return (dev->d_instance); 1397 } 1398 1399 const char * 1400 auclnt_get_dev_description(audio_dev_t *dev) 1401 { 1402 return (dev->d_desc); 1403 } 1404 1405 const char * 1406 auclnt_get_dev_version(audio_dev_t *dev) 1407 { 1408 return (dev->d_vers); 1409 } 1410 1411 unsigned 1412 auclnt_get_dev_capab(audio_dev_t *dev) 1413 { 1414 uint32_t flags; 1415 unsigned caps = 0; 1416 1417 flags = dev->d_flags; 1418 1419 if (flags & DEV_OUTPUT_CAP) 1420 caps |= AUDIO_CLIENT_CAP_PLAY; 1421 if (flags & DEV_INPUT_CAP) 1422 caps |= AUDIO_CLIENT_CAP_RECORD; 1423 if (flags & DEV_DUPLEX_CAP) 1424 caps |= AUDIO_CLIENT_CAP_DUPLEX; 1425 1426 /* AC3: deal with formats that don't support mixing */ 1427 1428 return (caps); 1429 } 1430 1431 uint64_t 1432 auclnt_get_samples(audio_stream_t *sp) 1433 { 1434 uint64_t n; 1435 1436 mutex_enter(&sp->s_lock); 1437 n = sp->s_samples; 1438 mutex_exit(&sp->s_lock); 1439 return (n); 1440 } 1441 1442 void 1443 auclnt_set_samples(audio_stream_t *sp, uint64_t n) 1444 { 1445 mutex_enter(&sp->s_lock); 1446 sp->s_samples = n; 1447 mutex_exit(&sp->s_lock); 1448 } 1449 1450 uint64_t 1451 auclnt_get_errors(audio_stream_t *sp) 1452 { 1453 uint64_t n; 1454 mutex_enter(&sp->s_lock); 1455 n = sp->s_errors; 1456 mutex_exit(&sp->s_lock); 1457 return (n); 1458 } 1459 1460 void 1461 auclnt_set_errors(audio_stream_t *sp, uint64_t n) 1462 { 1463 mutex_enter(&sp->s_lock); 1464 sp->s_errors = n; 1465 mutex_exit(&sp->s_lock); 1466 } 1467 1468 void 1469 auclnt_register_ops(minor_t minor, audio_client_ops_t *ops) 1470 { 1471 /* we control minor number allocations, no need for runtime checks */ 1472 ASSERT(minor <= AUDIO_MN_TYPE_MASK); 1473 1474 audio_client_ops[minor] = ops; 1475 } 1476 1477 int 1478 auimpl_create_minors(audio_dev_t *d) 1479 { 1480 char path[MAXPATHLEN]; 1481 int rv = 0; 1482 minor_t minor; 1483 audio_client_ops_t *ops; 1484 char *nt; 1485 1486 for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) { 1487 1488 if ((ops = audio_client_ops[i]) == NULL) 1489 continue; 1490 1491 if (ops->aco_dev_init != NULL) 1492 d->d_minor_data[i] = ops->aco_dev_init(d); 1493 1494 switch (i) { 1495 case AUDIO_MINOR_SNDSTAT: 1496 if (!(d->d_flags & DEV_SNDSTAT_CAP)) { 1497 continue; 1498 } 1499 nt = DDI_PSEUDO; 1500 break; 1501 1502 default: 1503 if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) { 1504 continue; 1505 } 1506 nt = DDI_NT_AUDIO; 1507 break; 1508 } 1509 1510 if (ops->aco_minor_prefix != NULL) { 1511 1512 minor = AUDIO_MKMN(d->d_instance, i); 1513 (void) snprintf(path, sizeof (path), 1514 "%s%d", ops->aco_minor_prefix, d->d_instance); 1515 1516 rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR, 1517 minor, nt, 0); 1518 1519 if (rv != 0) 1520 break; 1521 } 1522 } 1523 return (rv); 1524 } 1525 1526 void 1527 auimpl_remove_minors(audio_dev_t *d) 1528 { 1529 char path[MAXPATHLEN]; 1530 audio_client_ops_t *ops; 1531 1532 for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) { 1533 if ((ops = audio_client_ops[i]) == NULL) 1534 continue; 1535 if (ops->aco_minor_prefix != NULL) { 1536 (void) snprintf(path, sizeof (path), "%s%d", 1537 ops->aco_minor_prefix, d->d_instance); 1538 (void) ddi_remove_minor_node(d->d_dip, path); 1539 } 1540 1541 if (ops->aco_dev_fini != NULL) 1542 ops->aco_dev_fini(d->d_minor_data[i]); 1543 } 1544 } 1545 1546 void * 1547 auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn) 1548 { 1549 ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS)); 1550 return (d->d_minor_data[mn]); 1551 } 1552 1553 void * 1554 auclnt_get_minor_data(audio_client_t *c, minor_t mn) 1555 { 1556 ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS)); 1557 return (c->c_dev->d_minor_data[mn]); 1558 } 1559 1560 /* 1561 * This will walk all controls registered to a clients device and callback 1562 * to walker for each one with its audio_ctrl. Note this data 1563 * must be considered read only by walker. 1564 * 1565 * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE) 1566 * or stop walk (AUDIO_WALK_STOP). 1567 * 1568 */ 1569 void 1570 auclnt_walk_controls(audio_dev_t *d, 1571 int (*walker)(audio_ctrl_t *, void *), 1572 void *arg) 1573 { 1574 audio_ctrl_t *ctrl; 1575 1576 rw_enter(&d->d_ctrl_lock, RW_READER); 1577 for (ctrl = list_head(&d->d_controls); ctrl; 1578 ctrl = list_next(&d->d_controls, ctrl)) { 1579 if (walker(ctrl, arg) == AUDIO_WALK_STOP) 1580 break; 1581 } 1582 rw_exit(&d->d_ctrl_lock); 1583 } 1584 1585 /* 1586 * This will search all controls attached to an 1587 * audio device for a control with the desired name. 1588 * 1589 * d - the audio device to look on 1590 * name - name of the control being looked for. 1591 * 1592 * On successful return a ctrl handle will be returned. On 1593 * failure NULL is returned. 1594 */ 1595 audio_ctrl_t * 1596 auclnt_find_control(audio_dev_t *d, const char *name) 1597 { 1598 audio_ctrl_t *ctrl; 1599 1600 /* Verify argument */ 1601 ASSERT(d); 1602 1603 rw_enter(&d->d_ctrl_lock, RW_READER); 1604 for (ctrl = list_head(&d->d_controls); ctrl; 1605 ctrl = list_next(&d->d_controls, ctrl)) { 1606 if (strcmp(ctrl->ctrl_name, name) == 0) { 1607 rw_exit(&d->d_ctrl_lock); 1608 return (ctrl); 1609 } 1610 } 1611 rw_exit(&d->d_ctrl_lock); 1612 return (NULL); 1613 } 1614 1615 /* 1616 * Given a known control, get its attributes. 1617 * 1618 * The caller must supply a audio_ctrl_desc_t structure. Also the 1619 * values in the structure are ignored when making the call and filled 1620 * in by this function. All data pointed to by elements of desc should 1621 * be assumed read only. 1622 * 1623 * If an error occurs then a non-zero is returned. 1624 * 1625 */ 1626 int 1627 auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc) 1628 { 1629 ASSERT(ctrl); 1630 ASSERT(desc); 1631 1632 bcopy(&ctrl->ctrl_des, desc, sizeof (*desc)); 1633 return (0); 1634 } 1635 1636 int 1637 auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value) 1638 { 1639 return (audio_control_read(ctrl, value)); 1640 } 1641 1642 int 1643 auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value) 1644 { 1645 return (audio_control_write(ctrl, value)); 1646 } 1647 1648 void 1649 auclnt_warn(audio_client_t *c, const char *fmt, ...) 1650 { 1651 va_list va; 1652 1653 va_start(va, fmt); 1654 auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va); 1655 va_end(va); 1656 } 1657