xref: /onnv-gate/usr/src/uts/common/io/audio/impl/audio_input.c (revision 12165:e481916a5729)
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