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