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 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 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