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