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 /*
28 * Purpose: Virtual mixing audio input routines
29 *
30 * This file contains the actual mixing and resampling engine for input.
31 */
32
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/sysmacros.h>
36 #include <sys/sdt.h>
37 #include "audio_impl.h"
38
39 #define DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT) \
40 void \
41 auimpl_import_##NAME(audio_engine_t *e, uint_t nfr, audio_stream_t *sp) \
42 { \
43 int nch = e->e_nchan; \
44 int32_t *out = (void *)sp->s_cnv_src; \
45 TYPE *in = (void *)e->e_data; \
46 int ch = 0; \
47 int vol = sp->s_gain_eff; \
48 \
49 do { /* for each channel */ \
50 TYPE *ip; \
51 int32_t *op; \
52 int i; \
53 int incr = e->e_chincr[ch]; \
54 uint_t tidx = e->e_tidx; \
55 \
56 /* get value and adjust next channel offset */ \
57 op = out++; \
58 ip = in + e->e_choffs[ch] + (tidx * incr); \
59 \
60 i = nfr; \
61 \
62 do { /* for each frame */ \
63 int32_t sample = (TYPE)SWAP(*ip); \
64 int32_t scaled = sample SHIFT; \
65 \
66 scaled *= vol; \
67 scaled /= AUDIO_VOL_SCALE; \
68 \
69 *op = scaled; \
70 op += nch; \
71 \
72 ip += incr; \
73 if (++tidx == e->e_nframes) { \
74 tidx = 0; \
75 ip = in + e->e_choffs[ch]; \
76 } \
77 } while (--i); \
78 ch++; \
79 } while (ch < nch); \
80 }
81
82 DECL_AUDIO_IMPORT(16ne, int16_t, /* nop */, << 8)
83 DECL_AUDIO_IMPORT(16oe, int16_t, ddi_swap16, << 8)
84 DECL_AUDIO_IMPORT(32ne, int32_t, /* nop */, >> 8)
85 DECL_AUDIO_IMPORT(32oe, int32_t, ddi_swap32, >> 8)
86 DECL_AUDIO_IMPORT(24ne, int32_t, /* nop */, /* nop */)
87 DECL_AUDIO_IMPORT(24oe, int32_t, ddi_swap32, /* nop */)
88
89 /*
90 * Produce capture data. This takes data from the conversion buffer
91 * and copies it into the stream data buffer.
92 */
93 static void
auimpl_produce_data(audio_stream_t * sp,uint_t count)94 auimpl_produce_data(audio_stream_t *sp, uint_t count)
95 {
96 uint_t nframes;
97 uint_t framesz;
98 caddr_t cnvsrc;
99 caddr_t data;
100
101 nframes = sp->s_nframes;
102 framesz = sp->s_framesz;
103
104 ASSERT(sp->s_head >= sp->s_tail);
105 ASSERT(sp->s_hidx < nframes);
106 ASSERT(sp->s_tidx < nframes);
107
108 /*
109 * Copy data. We deal properly with wraps. Done as a
110 * do...while to minimize the number of tests.
111 */
112 cnvsrc = sp->s_cnv_src;
113 data = sp->s_data + (sp->s_hidx * framesz);
114 do {
115 unsigned nf;
116 unsigned nb;
117
118 nf = min(nframes - sp->s_hidx, count);
119 nb = nf * framesz;
120
121 bcopy(cnvsrc, data, nb);
122 data += nb;
123 cnvsrc += nb;
124 sp->s_hidx += nf;
125 sp->s_head += nf;
126 count -= nf;
127 sp->s_samples += nf;
128 if (sp->s_hidx == nframes) {
129 sp->s_hidx = 0;
130 data = sp->s_data;
131 }
132 } while (count);
133
134 ASSERT(sp->s_tail <= sp->s_head);
135 ASSERT(sp->s_hidx < nframes);
136 }
137
138 void
auimpl_input_callback(void * arg)139 auimpl_input_callback(void *arg)
140 {
141 audio_engine_t *e = arg;
142 uint_t fragfr = e->e_fragfr;
143 audio_stream_t *sp;
144 audio_client_t *c;
145 audio_client_t *clist = NULL;
146 list_t *l = &e->e_streams;
147 uint64_t h;
148
149 mutex_enter(&e->e_lock);
150
151 if (e->e_suspended || e->e_failed || !e->e_periodic) {
152 mutex_exit(&e->e_lock);
153 return;
154 }
155
156 if (e->e_need_start) {
157 int rv;
158 if ((rv = ENG_START(e)) != 0) {
159 e->e_failed = B_TRUE;
160 mutex_exit(&e->e_lock);
161 audio_dev_warn(e->e_dev,
162 "failed starting input, rv = %d", rv);
163 return;
164 }
165 e->e_need_start = B_FALSE;
166 }
167
168 h = ENG_COUNT(e);
169 ASSERT(h >= e->e_head);
170 if (h < e->e_head) {
171 /*
172 * This is a sign of a serious bug. We should
173 * probably offline the device via FMA, if we ever
174 * support FMA for audio devices.
175 */
176 e->e_failed = B_TRUE;
177 ENG_STOP(e);
178 mutex_exit(&e->e_lock);
179 audio_dev_warn(e->e_dev,
180 "device malfunction: broken capture sample counter");
181 return;
182 }
183 e->e_head = h;
184 ASSERT(e->e_head >= e->e_tail);
185
186 if ((e->e_head - e->e_tail) > e->e_nframes) {
187 /* no room for data, not much we can do */
188 e->e_errors++;
189 e->e_overruns++;
190 }
191
192 /* consume all fragments in the buffer */
193 while ((e->e_head - e->e_tail) > fragfr) {
194
195 /*
196 * Consider doing the SYNC outside of the lock.
197 */
198 ENG_SYNC(e, fragfr);
199
200 for (sp = list_head(l); sp != NULL; sp = list_next(l, sp)) {
201 int space;
202 int count;
203
204 mutex_enter(&sp->s_lock);
205 /* skip over streams paused or not running */
206 if (sp->s_paused || !sp->s_running) {
207 mutex_exit(&sp->s_lock);
208 continue;
209 }
210 sp->s_cnv_src = sp->s_cnv_buf0;
211 sp->s_cnv_dst = sp->s_cnv_buf1;
212
213 e->e_import(e, fragfr, sp);
214
215 /*
216 * Optionally convert fragment to requested sample
217 * format and rate.
218 */
219 if (sp->s_converter != NULL) {
220 count = sp->s_converter(sp, fragfr);
221 } else {
222 count = fragfr;
223 }
224
225 ASSERT(sp->s_head >= sp->s_tail);
226 space = sp->s_nframes - (sp->s_head - sp->s_tail);
227 if (count > space) {
228 e->e_stream_overruns++;
229 e->e_errors++;
230 sp->s_errors += count - space;
231 count = space;
232 }
233
234 auimpl_produce_data(sp, count);
235
236 /* wake blocked threads (blocking reads, etc.) */
237 cv_broadcast(&sp->s_cv);
238
239 mutex_exit(&sp->s_lock);
240
241 /*
242 * Add client to notification list. We'll
243 * process it after dropping the lock.
244 */
245 c = sp->s_client;
246
247 if ((c->c_input != NULL) &&
248 (c->c_next_input == NULL)) {
249 auclnt_hold(c);
250 c->c_next_input = clist;
251 clist = c;
252 }
253 }
254
255 /*
256 * Update the tail pointer, and the data pointer.
257 */
258 e->e_tail += fragfr;
259 e->e_tidx += fragfr;
260 if (e->e_tidx >= e->e_nframes) {
261 e->e_tidx -= e->e_nframes;
262 }
263 }
264
265 mutex_exit(&e->e_lock);
266
267 /*
268 * Notify client personalities.
269 */
270
271 while ((c = clist) != NULL) {
272 clist = c->c_next_input;
273 c->c_next_input = NULL;
274 c->c_input(c);
275 auclnt_release(c);
276 }
277 }
278