1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "alsa_device_proxy" 18 /*#define LOG_NDEBUG 0*/ 19 /*#define LOG_PCM_PARAMS 0*/ 20 21 #include <log/log.h> 22 23 #include <errno.h> 24 #include <stdio.h> 25 #include <string.h> 26 27 #include <audio_utils/clock.h> 28 29 #include "include/alsa_device_proxy.h" 30 31 #include "include/alsa_logging.h" 32 33 #define DEFAULT_PERIOD_SIZE 1024 34 #define DEFAULT_PERIOD_COUNT 2 35 36 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 37 38 static const unsigned format_byte_size_map[] = { 39 2, /* PCM_FORMAT_S16_LE */ 40 4, /* PCM_FORMAT_S32_LE */ 41 1, /* PCM_FORMAT_S8 */ 42 4, /* PCM_FORMAT_S24_LE */ 43 3, /* PCM_FORMAT_S24_3LE */ 44 }; 45 46 int proxy_prepare(alsa_device_proxy * proxy, const alsa_device_profile* profile, 47 struct pcm_config * config) 48 { 49 int ret = 0; 50 51 ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device); 52 53 proxy->profile = profile; 54 55 #ifdef LOG_PCM_PARAMS 56 log_pcm_config(config, "proxy_setup()"); 57 #endif 58 59 if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) { 60 proxy->alsa_config.format = config->format; 61 } else { 62 proxy->alsa_config.format = profile->default_config.format; 63 ALOGW("Invalid format %d - using default %d.", 64 config->format, profile->default_config.format); 65 // Indicate override when default format was not requested 66 if (config->format != PCM_FORMAT_INVALID) { 67 ret = -EINVAL; 68 } 69 } 70 71 if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) { 72 proxy->alsa_config.rate = config->rate; 73 } else { 74 proxy->alsa_config.rate = profile->default_config.rate; 75 ALOGW("Invalid sample rate %u - using default %u.", 76 config->rate, profile->default_config.rate); 77 // Indicate override when default rate was not requested 78 if (config->rate != 0) { 79 ret = -EINVAL; 80 } 81 } 82 83 if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) { 84 proxy->alsa_config.channels = config->channels; 85 } else { 86 proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels); 87 ALOGW("Invalid channel count %u - using closest %u.", 88 config->channels, proxy->alsa_config.channels); 89 // Indicate override when default channel count was not requested 90 if (config->channels != 0) { 91 ret = -EINVAL; 92 } 93 } 94 95 proxy->alsa_config.period_count = profile->default_config.period_count; 96 proxy->alsa_config.period_size = 97 profile_get_period_size(proxy->profile, proxy->alsa_config.rate); 98 99 // Hack for USB accessory audio. 100 // Here we set the correct value for period_count if tinyalsa fails to get it from the 101 // f_audio_source driver. 102 if (proxy->alsa_config.period_count == 0) { 103 proxy->alsa_config.period_count = 4; 104 } 105 106 proxy->pcm = NULL; 107 // config format should be checked earlier against profile. 108 if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) { 109 proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels; 110 } else { 111 proxy->frame_size = 1; 112 } 113 114 // let's check to make sure we can ACTUALLY use the maximum rate (with the channel count) 115 // Note that profile->sample_rates is sorted highest to lowest, so the scan will get 116 // us the highest working rate 117 int max_rate_index = proxy_scan_rates(proxy, profile->sample_rates); 118 if (max_rate_index >= 0) { 119 if (proxy->alsa_config.rate > profile->sample_rates[max_rate_index]) { 120 ALOGW("Limiting sampling rate from %u to %u.", 121 proxy->alsa_config.rate, profile->sample_rates[max_rate_index]); 122 proxy->alsa_config.rate = profile->sample_rates[max_rate_index]; 123 ret = -EINVAL; 124 } 125 } 126 return ret; 127 } 128 129 int proxy_open(alsa_device_proxy * proxy) 130 { 131 const alsa_device_profile* profile = proxy->profile; 132 ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device, 133 profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN"); 134 135 if (profile->card < 0 || profile->device < 0) { 136 return -EINVAL; 137 } 138 139 proxy->pcm = pcm_open(profile->card, profile->device, 140 profile->direction | PCM_MONOTONIC, &proxy->alsa_config); 141 if (proxy->pcm == NULL) { 142 return -ENOMEM; 143 } 144 145 if (!pcm_is_ready(proxy->pcm)) { 146 ALOGE(" proxy_open() pcm_is_ready() failed: %s", pcm_get_error(proxy->pcm)); 147 #if defined(LOG_PCM_PARAMS) 148 log_pcm_config(&proxy->alsa_config, "config"); 149 #endif 150 pcm_close(proxy->pcm); 151 proxy->pcm = NULL; 152 return -ENOMEM; 153 } 154 155 return 0; 156 } 157 158 void proxy_close(alsa_device_proxy * proxy) 159 { 160 ALOGV("proxy_close() [pcm:%p]", proxy->pcm); 161 162 if (proxy->pcm != NULL) { 163 pcm_close(proxy->pcm); 164 proxy->pcm = NULL; 165 } 166 } 167 168 /* 169 * Sample Rate 170 */ 171 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy) 172 { 173 return proxy->alsa_config.rate; 174 } 175 176 /* 177 * Format 178 */ 179 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy) 180 { 181 return proxy->alsa_config.format; 182 } 183 184 /* 185 * Channel Count 186 */ 187 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy) 188 { 189 return proxy->alsa_config.channels; 190 } 191 192 /* 193 * Other 194 */ 195 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy) 196 { 197 return proxy->alsa_config.period_size; 198 } 199 200 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy) 201 { 202 return proxy->alsa_config.period_count; 203 } 204 205 unsigned proxy_get_latency(const alsa_device_proxy * proxy) 206 { 207 return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000) 208 / proxy_get_sample_rate(proxy); 209 } 210 211 int proxy_get_presentation_position(const alsa_device_proxy * proxy, 212 uint64_t *frames, struct timespec *timestamp) 213 { 214 int ret = -EPERM; // -1 215 unsigned int avail; 216 if (proxy->pcm != NULL 217 && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) { 218 const size_t kernel_buffer_size = 219 proxy->alsa_config.period_size * proxy->alsa_config.period_count; 220 if (avail > kernel_buffer_size) { 221 ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size); 222 } else { 223 int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail; 224 // It is possible to compensate for additional driver and device delay 225 // by changing signed_frames. Example: 226 // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000; 227 if (signed_frames >= 0) { 228 *frames = signed_frames; 229 ret = 0; 230 } 231 } 232 } 233 return ret; 234 } 235 236 int proxy_get_capture_position(const alsa_device_proxy * proxy, 237 int64_t *frames, int64_t *time) 238 { 239 int ret = -ENOSYS; 240 unsigned int avail; 241 struct timespec timestamp; 242 // TODO: add logging for tinyalsa errors. 243 if (proxy->pcm != NULL 244 && pcm_get_htimestamp(proxy->pcm, &avail, ×tamp) == 0) { 245 const size_t kernel_buffer_size = 246 proxy->alsa_config.period_size * proxy->alsa_config.period_count; 247 if (avail > kernel_buffer_size) { 248 ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size); 249 } else { 250 *frames = proxy->transferred + avail; 251 *time = audio_utils_ns_from_timespec(×tamp); 252 ret = 0; 253 } 254 } 255 return ret; 256 } 257 258 /* 259 * I/O 260 */ 261 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count) 262 { 263 int ret = pcm_write(proxy->pcm, data, count); 264 if (ret == 0) { 265 proxy->transferred += count / proxy->frame_size; 266 } 267 return ret; 268 } 269 270 int proxy_read(alsa_device_proxy * proxy, void *data, unsigned int count) 271 { 272 int ret = pcm_read(proxy->pcm, data, count); 273 if (ret == 0) { 274 proxy->transferred += count / proxy->frame_size; 275 } 276 return ret; 277 } 278 279 /* 280 * Debugging 281 */ 282 void proxy_dump(const alsa_device_proxy* proxy, int fd) 283 { 284 if (proxy != NULL) { 285 dprintf(fd, " channels: %d\n", proxy->alsa_config.channels); 286 dprintf(fd, " rate: %d\n", proxy->alsa_config.rate); 287 dprintf(fd, " period_size: %d\n", proxy->alsa_config.period_size); 288 dprintf(fd, " period_count: %d\n", proxy->alsa_config.period_count); 289 dprintf(fd, " format: %d\n", proxy->alsa_config.format); 290 } 291 } 292 293 int proxy_scan_rates(alsa_device_proxy * proxy, const unsigned sample_rates[]) { 294 const alsa_device_profile* profile = proxy->profile; 295 if (profile->card < 0 || profile->device < 0) { 296 return -EINVAL; 297 } 298 299 struct pcm_config alsa_config; 300 memcpy(&alsa_config, &proxy->alsa_config, sizeof(alsa_config)); 301 302 struct pcm * alsa_pcm; 303 int rate_index = 0; 304 while (sample_rates[rate_index] != 0) { 305 alsa_config.rate = sample_rates[rate_index]; 306 alsa_pcm = pcm_open(profile->card, profile->device, 307 profile->direction | PCM_MONOTONIC, &alsa_config); 308 if (alsa_pcm != NULL) { 309 if (pcm_is_ready(alsa_pcm)) { 310 pcm_close(alsa_pcm); 311 return rate_index; 312 } 313 314 pcm_close(alsa_pcm); 315 } 316 317 rate_index++; 318 } 319 320 return -EINVAL; 321 } 322