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 25 #include "include/alsa_device_proxy.h" 26 27 #include "include/alsa_logging.h" 28 29 #define DEFAULT_PERIOD_SIZE 1024 30 #define DEFAULT_PERIOD_COUNT 2 31 32 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 33 34 static const unsigned format_byte_size_map[] = { 35 2, /* PCM_FORMAT_S16_LE */ 36 4, /* PCM_FORMAT_S32_LE */ 37 1, /* PCM_FORMAT_S8 */ 38 4, /* PCM_FORMAT_S24_LE */ 39 3, /* PCM_FORMAT_S24_3LE */ 40 }; 41 42 void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile, 43 struct pcm_config * config) 44 { 45 ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device); 46 47 proxy->profile = profile; 48 49 #ifdef LOG_PCM_PARAMS 50 log_pcm_config(config, "proxy_setup()"); 51 #endif 52 53 if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) { 54 proxy->alsa_config.format = config->format; 55 } else { 56 ALOGW("Invalid format %d - using default %d.", 57 config->format, profile->default_config.format); 58 proxy->alsa_config.format = profile->default_config.format; 59 } 60 61 if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) { 62 proxy->alsa_config.rate = config->rate; 63 } else { 64 ALOGW("Invalid sample rate %u - using default %u.", 65 config->rate, profile->default_config.rate); 66 proxy->alsa_config.rate = profile->default_config.rate; 67 } 68 69 if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) { 70 proxy->alsa_config.channels = config->channels; 71 } else { 72 proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels); 73 ALOGW("Invalid channel count %u - using closest %u.", 74 config->channels, proxy->alsa_config.channels); 75 } 76 77 proxy->alsa_config.period_count = profile->default_config.period_count; 78 proxy->alsa_config.period_size = 79 profile_get_period_size(proxy->profile, proxy->alsa_config.rate); 80 81 // Hack for USB accessory audio. 82 // Here we set the correct value for period_count if tinyalsa fails to get it from the 83 // f_audio_source driver. 84 if (proxy->alsa_config.period_count == 0) { 85 proxy->alsa_config.period_count = 4; 86 } 87 88 proxy->pcm = NULL; 89 // config format should be checked earlier against profile. 90 if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) { 91 proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels; 92 } else { 93 proxy->frame_size = 1; 94 } 95 } 96 97 int proxy_open(alsa_device_proxy * proxy) 98 { 99 alsa_device_profile* profile = proxy->profile; 100 ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device, 101 profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN"); 102 103 if (profile->card < 0 || profile->device < 0) { 104 return -EINVAL; 105 } 106 107 proxy->pcm = pcm_open(profile->card, profile->device, 108 profile->direction | PCM_MONOTONIC, &proxy->alsa_config); 109 if (proxy->pcm == NULL) { 110 return -ENOMEM; 111 } 112 113 if (!pcm_is_ready(proxy->pcm)) { 114 ALOGE(" proxy_open() pcm_open() failed: %s", pcm_get_error(proxy->pcm)); 115 #if defined(LOG_PCM_PARAMS) 116 log_pcm_config(&proxy->alsa_config, "config"); 117 #endif 118 pcm_close(proxy->pcm); 119 proxy->pcm = NULL; 120 return -ENOMEM; 121 } 122 123 return 0; 124 } 125 126 void proxy_close(alsa_device_proxy * proxy) 127 { 128 ALOGV("proxy_close() [pcm:%p]", proxy->pcm); 129 130 if (proxy->pcm != NULL) { 131 pcm_close(proxy->pcm); 132 proxy->pcm = NULL; 133 } 134 } 135 136 /* 137 * Sample Rate 138 */ 139 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy) 140 { 141 return proxy->alsa_config.rate; 142 } 143 144 /* 145 * Format 146 */ 147 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy) 148 { 149 return proxy->alsa_config.format; 150 } 151 152 /* 153 * Channel Count 154 */ 155 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy) 156 { 157 return proxy->alsa_config.channels; 158 } 159 160 /* 161 * Other 162 */ 163 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy) 164 { 165 return proxy->alsa_config.period_size; 166 } 167 168 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy) 169 { 170 return proxy->alsa_config.period_count; 171 } 172 173 unsigned proxy_get_latency(const alsa_device_proxy * proxy) 174 { 175 return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000) 176 / proxy_get_sample_rate(proxy); 177 } 178 179 int proxy_get_presentation_position(const alsa_device_proxy * proxy, 180 uint64_t *frames, struct timespec *timestamp) 181 { 182 int ret = -EPERM; // -1 183 unsigned int avail; 184 if (proxy->pcm != NULL 185 && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) { 186 const size_t kernel_buffer_size = 187 proxy->alsa_config.period_size * proxy->alsa_config.period_count; 188 if (avail > kernel_buffer_size) { 189 ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size); 190 } else { 191 int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail; 192 // It is possible to compensate for additional driver and device delay 193 // by changing signed_frames. Example: 194 // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000; 195 if (signed_frames >= 0) { 196 *frames = signed_frames; 197 ret = 0; 198 } 199 } 200 } 201 return ret; 202 } 203 204 /* 205 * I/O 206 */ 207 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count) 208 { 209 int ret = pcm_write(proxy->pcm, data, count); 210 if (ret == 0) { 211 proxy->transferred += count / proxy->frame_size; 212 } 213 return ret; 214 } 215 216 int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count) 217 { 218 return pcm_read(proxy->pcm, data, count); 219 } 220 221 /* 222 * Debugging 223 */ 224 void proxy_dump(const alsa_device_proxy* proxy, int fd) 225 { 226 if (proxy != NULL) { 227 dprintf(fd, " channels: %d\n", proxy->alsa_config.channels); 228 dprintf(fd, " rate: %d\n", proxy->alsa_config.rate); 229 dprintf(fd, " period_size: %d\n", proxy->alsa_config.period_size); 230 dprintf(fd, " period_count: %d\n", proxy->alsa_config.period_count); 231 dprintf(fd, " format: %d\n", proxy->alsa_config.format); 232 } 233 } 234