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