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