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
proxy_prepare(alsa_device_proxy * proxy,const alsa_device_profile * profile,struct pcm_config * config)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 samplnig 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
proxy_open(alsa_device_proxy * proxy)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
proxy_close(alsa_device_proxy * proxy)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 */
proxy_get_sample_rate(const alsa_device_proxy * proxy)171 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
172 {
173 return proxy->alsa_config.rate;
174 }
175
176 /*
177 * Format
178 */
proxy_get_format(const alsa_device_proxy * proxy)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 */
proxy_get_channel_count(const alsa_device_proxy * proxy)187 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
188 {
189 return proxy->alsa_config.channels;
190 }
191
192 /*
193 * Other
194 */
proxy_get_period_size(const alsa_device_proxy * proxy)195 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
196 {
197 return proxy->alsa_config.period_size;
198 }
199
proxy_get_period_count(const alsa_device_proxy * proxy)200 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
201 {
202 return proxy->alsa_config.period_count;
203 }
204
proxy_get_latency(const alsa_device_proxy * proxy)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
proxy_get_presentation_position(const alsa_device_proxy * proxy,uint64_t * frames,struct timespec * timestamp)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
proxy_get_capture_position(const alsa_device_proxy * proxy,int64_t * frames,int64_t * time)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 */
proxy_write(alsa_device_proxy * proxy,const void * data,unsigned int count)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
proxy_read(alsa_device_proxy * proxy,void * data,unsigned int count)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 */
proxy_dump(const alsa_device_proxy * proxy,int fd)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
proxy_scan_rates(alsa_device_proxy * proxy,const unsigned sample_rates[])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