19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM * CDDL HEADER START
39484Sgarrett.damore@Sun.COM *
49484Sgarrett.damore@Sun.COM * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM *
89484Sgarrett.damore@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM * and limitations under the License.
129484Sgarrett.damore@Sun.COM *
139484Sgarrett.damore@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM *
199484Sgarrett.damore@Sun.COM * CDDL HEADER END
209484Sgarrett.damore@Sun.COM */
219484Sgarrett.damore@Sun.COM /*
229484Sgarrett.damore@Sun.COM * Copyright (C) 4Front Technologies 1996-2008.
239484Sgarrett.damore@Sun.COM *
24*12165Sgdamore@opensolaris.org * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
259484Sgarrett.damore@Sun.COM */
269484Sgarrett.damore@Sun.COM
279484Sgarrett.damore@Sun.COM /*
289484Sgarrett.damore@Sun.COM * Purpose: Virtual mixing audio input routines
299484Sgarrett.damore@Sun.COM *
309484Sgarrett.damore@Sun.COM * This file contains the actual mixing and resampling engine for input.
319484Sgarrett.damore@Sun.COM */
329484Sgarrett.damore@Sun.COM
339484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
349484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
359484Sgarrett.damore@Sun.COM #include <sys/sysmacros.h>
3611936Sgdamore@opensolaris.org #include <sys/sdt.h>
379484Sgarrett.damore@Sun.COM #include "audio_impl.h"
389484Sgarrett.damore@Sun.COM
399484Sgarrett.damore@Sun.COM #define DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT) \
409484Sgarrett.damore@Sun.COM void \
4111936Sgdamore@opensolaris.org auimpl_import_##NAME(audio_engine_t *e, uint_t nfr, audio_stream_t *sp) \
429484Sgarrett.damore@Sun.COM { \
4311936Sgdamore@opensolaris.org int nch = e->e_nchan; \
4411936Sgdamore@opensolaris.org int32_t *out = (void *)sp->s_cnv_src; \
4511936Sgdamore@opensolaris.org TYPE *in = (void *)e->e_data; \
4610550Sgdamore@opensolaris.org int ch = 0; \
4710550Sgdamore@opensolaris.org int vol = sp->s_gain_eff; \
489484Sgarrett.damore@Sun.COM \
499484Sgarrett.damore@Sun.COM do { /* for each channel */ \
5010550Sgdamore@opensolaris.org TYPE *ip; \
519484Sgarrett.damore@Sun.COM int32_t *op; \
5210550Sgdamore@opensolaris.org int i; \
5311936Sgdamore@opensolaris.org int incr = e->e_chincr[ch]; \
5411936Sgdamore@opensolaris.org uint_t tidx = e->e_tidx; \
559484Sgarrett.damore@Sun.COM \
569484Sgarrett.damore@Sun.COM /* get value and adjust next channel offset */ \
579484Sgarrett.damore@Sun.COM op = out++; \
5811936Sgdamore@opensolaris.org ip = in + e->e_choffs[ch] + (tidx * incr); \
599484Sgarrett.damore@Sun.COM \
6011936Sgdamore@opensolaris.org i = nfr; \
619484Sgarrett.damore@Sun.COM \
629484Sgarrett.damore@Sun.COM do { /* for each frame */ \
639484Sgarrett.damore@Sun.COM int32_t sample = (TYPE)SWAP(*ip); \
649484Sgarrett.damore@Sun.COM int32_t scaled = sample SHIFT; \
659484Sgarrett.damore@Sun.COM \
669484Sgarrett.damore@Sun.COM scaled *= vol; \
679484Sgarrett.damore@Sun.COM scaled /= AUDIO_VOL_SCALE; \
689484Sgarrett.damore@Sun.COM \
699484Sgarrett.damore@Sun.COM *op = scaled; \
709484Sgarrett.damore@Sun.COM op += nch; \
719484Sgarrett.damore@Sun.COM \
7211936Sgdamore@opensolaris.org ip += incr; \
7311936Sgdamore@opensolaris.org if (++tidx == e->e_nframes) { \
7411936Sgdamore@opensolaris.org tidx = 0; \
7511936Sgdamore@opensolaris.org ip = in + e->e_choffs[ch]; \
7611936Sgdamore@opensolaris.org } \
779484Sgarrett.damore@Sun.COM } while (--i); \
789484Sgarrett.damore@Sun.COM ch++; \
799484Sgarrett.damore@Sun.COM } while (ch < nch); \
809484Sgarrett.damore@Sun.COM }
819484Sgarrett.damore@Sun.COM
829484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(16ne, int16_t, /* nop */, << 8)
839484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(16oe, int16_t, ddi_swap16, << 8)
849484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(32ne, int32_t, /* nop */, >> 8)
859484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(32oe, int32_t, ddi_swap32, >> 8)
869484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(24ne, int32_t, /* nop */, /* nop */)
879484Sgarrett.damore@Sun.COM DECL_AUDIO_IMPORT(24oe, int32_t, ddi_swap32, /* nop */)
889484Sgarrett.damore@Sun.COM
899484Sgarrett.damore@Sun.COM /*
9011936Sgdamore@opensolaris.org * Produce capture data. This takes data from the conversion buffer
9111936Sgdamore@opensolaris.org * and copies it into the stream data buffer.
929484Sgarrett.damore@Sun.COM */
939484Sgarrett.damore@Sun.COM static void
auimpl_produce_data(audio_stream_t * sp,uint_t count)9411936Sgdamore@opensolaris.org auimpl_produce_data(audio_stream_t *sp, uint_t count)
959484Sgarrett.damore@Sun.COM {
9611936Sgdamore@opensolaris.org uint_t nframes;
9711936Sgdamore@opensolaris.org uint_t framesz;
9811936Sgdamore@opensolaris.org caddr_t cnvsrc;
9911936Sgdamore@opensolaris.org caddr_t data;
1009484Sgarrett.damore@Sun.COM
1019484Sgarrett.damore@Sun.COM nframes = sp->s_nframes;
1029484Sgarrett.damore@Sun.COM framesz = sp->s_framesz;
1039484Sgarrett.damore@Sun.COM
1049484Sgarrett.damore@Sun.COM ASSERT(sp->s_head >= sp->s_tail);
1059484Sgarrett.damore@Sun.COM ASSERT(sp->s_hidx < nframes);
1069484Sgarrett.damore@Sun.COM ASSERT(sp->s_tidx < nframes);
1079484Sgarrett.damore@Sun.COM
1089484Sgarrett.damore@Sun.COM /*
1099484Sgarrett.damore@Sun.COM * Copy data. We deal properly with wraps. Done as a
1109484Sgarrett.damore@Sun.COM * do...while to minimize the number of tests.
1119484Sgarrett.damore@Sun.COM */
1129484Sgarrett.damore@Sun.COM cnvsrc = sp->s_cnv_src;
1139484Sgarrett.damore@Sun.COM data = sp->s_data + (sp->s_hidx * framesz);
1149484Sgarrett.damore@Sun.COM do {
1159484Sgarrett.damore@Sun.COM unsigned nf;
1169484Sgarrett.damore@Sun.COM unsigned nb;
1179484Sgarrett.damore@Sun.COM
1189484Sgarrett.damore@Sun.COM nf = min(nframes - sp->s_hidx, count);
1199484Sgarrett.damore@Sun.COM nb = nf * framesz;
1209484Sgarrett.damore@Sun.COM
1219484Sgarrett.damore@Sun.COM bcopy(cnvsrc, data, nb);
1229484Sgarrett.damore@Sun.COM data += nb;
1239484Sgarrett.damore@Sun.COM cnvsrc += nb;
1249484Sgarrett.damore@Sun.COM sp->s_hidx += nf;
1259484Sgarrett.damore@Sun.COM sp->s_head += nf;
1269484Sgarrett.damore@Sun.COM count -= nf;
1279484Sgarrett.damore@Sun.COM sp->s_samples += nf;
12811936Sgdamore@opensolaris.org if (sp->s_hidx == nframes) {
12911936Sgdamore@opensolaris.org sp->s_hidx = 0;
13011936Sgdamore@opensolaris.org data = sp->s_data;
1319484Sgarrett.damore@Sun.COM }
1329484Sgarrett.damore@Sun.COM } while (count);
1339484Sgarrett.damore@Sun.COM
1349484Sgarrett.damore@Sun.COM ASSERT(sp->s_tail <= sp->s_head);
1359484Sgarrett.damore@Sun.COM ASSERT(sp->s_hidx < nframes);
1369484Sgarrett.damore@Sun.COM }
1379484Sgarrett.damore@Sun.COM
1389484Sgarrett.damore@Sun.COM void
auimpl_input_callback(void * arg)13911936Sgdamore@opensolaris.org auimpl_input_callback(void *arg)
1409484Sgarrett.damore@Sun.COM {
14111936Sgdamore@opensolaris.org audio_engine_t *e = arg;
14211936Sgdamore@opensolaris.org uint_t fragfr = e->e_fragfr;
14311936Sgdamore@opensolaris.org audio_stream_t *sp;
1449484Sgarrett.damore@Sun.COM audio_client_t *c;
14511936Sgdamore@opensolaris.org audio_client_t *clist = NULL;
14611936Sgdamore@opensolaris.org list_t *l = &e->e_streams;
14711936Sgdamore@opensolaris.org uint64_t h;
14811936Sgdamore@opensolaris.org
14911936Sgdamore@opensolaris.org mutex_enter(&e->e_lock);
15011936Sgdamore@opensolaris.org
151*12165Sgdamore@opensolaris.org if (e->e_suspended || e->e_failed || !e->e_periodic) {
15211936Sgdamore@opensolaris.org mutex_exit(&e->e_lock);
15311936Sgdamore@opensolaris.org return;
15411936Sgdamore@opensolaris.org }
15511936Sgdamore@opensolaris.org
15611936Sgdamore@opensolaris.org if (e->e_need_start) {
15711936Sgdamore@opensolaris.org int rv;
15811936Sgdamore@opensolaris.org if ((rv = ENG_START(e)) != 0) {
15911936Sgdamore@opensolaris.org e->e_failed = B_TRUE;
16011936Sgdamore@opensolaris.org mutex_exit(&e->e_lock);
16111936Sgdamore@opensolaris.org audio_dev_warn(e->e_dev,
16211936Sgdamore@opensolaris.org "failed starting input, rv = %d", rv);
16311936Sgdamore@opensolaris.org return;
16411936Sgdamore@opensolaris.org }
16511936Sgdamore@opensolaris.org e->e_need_start = B_FALSE;
16611936Sgdamore@opensolaris.org }
16711936Sgdamore@opensolaris.org
16811936Sgdamore@opensolaris.org h = ENG_COUNT(e);
16911936Sgdamore@opensolaris.org ASSERT(h >= e->e_head);
17011936Sgdamore@opensolaris.org if (h < e->e_head) {
17111936Sgdamore@opensolaris.org /*
17211936Sgdamore@opensolaris.org * This is a sign of a serious bug. We should
17311936Sgdamore@opensolaris.org * probably offline the device via FMA, if we ever
17411936Sgdamore@opensolaris.org * support FMA for audio devices.
17511936Sgdamore@opensolaris.org */
17611936Sgdamore@opensolaris.org e->e_failed = B_TRUE;
17711936Sgdamore@opensolaris.org ENG_STOP(e);
17811936Sgdamore@opensolaris.org mutex_exit(&e->e_lock);
17911936Sgdamore@opensolaris.org audio_dev_warn(e->e_dev,
18011936Sgdamore@opensolaris.org "device malfunction: broken capture sample counter");
18111936Sgdamore@opensolaris.org return;
18211936Sgdamore@opensolaris.org }
18311936Sgdamore@opensolaris.org e->e_head = h;
18411936Sgdamore@opensolaris.org ASSERT(e->e_head >= e->e_tail);
18511936Sgdamore@opensolaris.org
18611936Sgdamore@opensolaris.org if ((e->e_head - e->e_tail) > e->e_nframes) {
18711936Sgdamore@opensolaris.org /* no room for data, not much we can do */
18811936Sgdamore@opensolaris.org e->e_errors++;
18911936Sgdamore@opensolaris.org e->e_overruns++;
19011936Sgdamore@opensolaris.org }
1919484Sgarrett.damore@Sun.COM
1929484Sgarrett.damore@Sun.COM /* consume all fragments in the buffer */
19311936Sgdamore@opensolaris.org while ((e->e_head - e->e_tail) > fragfr) {
1949484Sgarrett.damore@Sun.COM
1959484Sgarrett.damore@Sun.COM /*
1969484Sgarrett.damore@Sun.COM * Consider doing the SYNC outside of the lock.
1979484Sgarrett.damore@Sun.COM */
19811936Sgdamore@opensolaris.org ENG_SYNC(e, fragfr);
1999484Sgarrett.damore@Sun.COM
20011936Sgdamore@opensolaris.org for (sp = list_head(l); sp != NULL; sp = list_next(l, sp)) {
2019484Sgarrett.damore@Sun.COM int space;
2029484Sgarrett.damore@Sun.COM int count;
2039484Sgarrett.damore@Sun.COM
2049484Sgarrett.damore@Sun.COM mutex_enter(&sp->s_lock);
2059484Sgarrett.damore@Sun.COM /* skip over streams paused or not running */
20611936Sgdamore@opensolaris.org if (sp->s_paused || !sp->s_running) {
2079484Sgarrett.damore@Sun.COM mutex_exit(&sp->s_lock);
2089484Sgarrett.damore@Sun.COM continue;
2099484Sgarrett.damore@Sun.COM }
2109484Sgarrett.damore@Sun.COM sp->s_cnv_src = sp->s_cnv_buf0;
2119484Sgarrett.damore@Sun.COM sp->s_cnv_dst = sp->s_cnv_buf1;
21211936Sgdamore@opensolaris.org
21311936Sgdamore@opensolaris.org e->e_import(e, fragfr, sp);
2149484Sgarrett.damore@Sun.COM
2159484Sgarrett.damore@Sun.COM /*
2169484Sgarrett.damore@Sun.COM * Optionally convert fragment to requested sample
2179484Sgarrett.damore@Sun.COM * format and rate.
2189484Sgarrett.damore@Sun.COM */
2199484Sgarrett.damore@Sun.COM if (sp->s_converter != NULL) {
2209484Sgarrett.damore@Sun.COM count = sp->s_converter(sp, fragfr);
2219484Sgarrett.damore@Sun.COM } else {
2229484Sgarrett.damore@Sun.COM count = fragfr;
2239484Sgarrett.damore@Sun.COM }
2249484Sgarrett.damore@Sun.COM
22511936Sgdamore@opensolaris.org ASSERT(sp->s_head >= sp->s_tail);
2269484Sgarrett.damore@Sun.COM space = sp->s_nframes - (sp->s_head - sp->s_tail);
2279484Sgarrett.damore@Sun.COM if (count > space) {
22811936Sgdamore@opensolaris.org e->e_stream_overruns++;
22911936Sgdamore@opensolaris.org e->e_errors++;
2309484Sgarrett.damore@Sun.COM sp->s_errors += count - space;
2319484Sgarrett.damore@Sun.COM count = space;
2329484Sgarrett.damore@Sun.COM }
2339484Sgarrett.damore@Sun.COM
23411936Sgdamore@opensolaris.org auimpl_produce_data(sp, count);
2359484Sgarrett.damore@Sun.COM
2369484Sgarrett.damore@Sun.COM /* wake blocked threads (blocking reads, etc.) */
2379484Sgarrett.damore@Sun.COM cv_broadcast(&sp->s_cv);
2389484Sgarrett.damore@Sun.COM
2399484Sgarrett.damore@Sun.COM mutex_exit(&sp->s_lock);
2409484Sgarrett.damore@Sun.COM
24111936Sgdamore@opensolaris.org /*
24211936Sgdamore@opensolaris.org * Add client to notification list. We'll
24311936Sgdamore@opensolaris.org * process it after dropping the lock.
24411936Sgdamore@opensolaris.org */
24511936Sgdamore@opensolaris.org c = sp->s_client;
24611936Sgdamore@opensolaris.org
24711936Sgdamore@opensolaris.org if ((c->c_input != NULL) &&
24811936Sgdamore@opensolaris.org (c->c_next_input == NULL)) {
24911936Sgdamore@opensolaris.org auclnt_hold(c);
25011936Sgdamore@opensolaris.org c->c_next_input = clist;
25111936Sgdamore@opensolaris.org clist = c;
2529484Sgarrett.damore@Sun.COM }
2539484Sgarrett.damore@Sun.COM }
2549484Sgarrett.damore@Sun.COM
2559484Sgarrett.damore@Sun.COM /*
2569484Sgarrett.damore@Sun.COM * Update the tail pointer, and the data pointer.
2579484Sgarrett.damore@Sun.COM */
25811936Sgdamore@opensolaris.org e->e_tail += fragfr;
25911936Sgdamore@opensolaris.org e->e_tidx += fragfr;
26011936Sgdamore@opensolaris.org if (e->e_tidx >= e->e_nframes) {
26111936Sgdamore@opensolaris.org e->e_tidx -= e->e_nframes;
2629484Sgarrett.damore@Sun.COM }
2639484Sgarrett.damore@Sun.COM }
26411936Sgdamore@opensolaris.org
26511936Sgdamore@opensolaris.org mutex_exit(&e->e_lock);
26611936Sgdamore@opensolaris.org
26711936Sgdamore@opensolaris.org /*
26811936Sgdamore@opensolaris.org * Notify client personalities.
26911936Sgdamore@opensolaris.org */
27011936Sgdamore@opensolaris.org
27111936Sgdamore@opensolaris.org while ((c = clist) != NULL) {
27211936Sgdamore@opensolaris.org clist = c->c_next_input;
27311936Sgdamore@opensolaris.org c->c_next_input = NULL;
27411936Sgdamore@opensolaris.org c->c_input(c);
27511936Sgdamore@opensolaris.org auclnt_release(c);
27611936Sgdamore@opensolaris.org }
2779484Sgarrett.damore@Sun.COM }
278