blob: e64a42ee658f85c56060e7fec6476a7d7d6b796f [file] [log] [blame]
Paul McLean98ca8d42015-01-13 09:41:36 -08001/*
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"
Paul Mclean2a3925e2016-06-02 14:48:10 +000018/*#define LOG_NDEBUG 0*/
Paul McLean98ca8d42015-01-13 09:41:36 -080019/*#define LOG_PCM_PARAMS 0*/
20
21#include <log/log.h>
22
23#include <errno.h>
Tri Vo82d94502017-06-23 15:42:32 -070024#include <stdio.h>
Dan Albertd8ec6c22017-10-11 12:26:23 -070025#include <string.h>
Tri Vo82d94502017-06-23 15:42:32 -070026
Paul McLean98ca8d42015-01-13 09:41:36 -080027#include "include/alsa_device_proxy.h"
28
29#include "include/alsa_logging.h"
30
31#define DEFAULT_PERIOD_SIZE 1024
32#define DEFAULT_PERIOD_COUNT 2
33
Andy Hung8785fe12015-08-04 14:57:50 -070034#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
35
36static const unsigned format_byte_size_map[] = {
37 2, /* PCM_FORMAT_S16_LE */
38 4, /* PCM_FORMAT_S32_LE */
39 1, /* PCM_FORMAT_S8 */
40 4, /* PCM_FORMAT_S24_LE */
41 3, /* PCM_FORMAT_S24_3LE */
42};
43
Andy Hung2c959782017-10-27 20:20:27 -070044int proxy_prepare(alsa_device_proxy * proxy, const alsa_device_profile* profile,
Paul McLean98ca8d42015-01-13 09:41:36 -080045 struct pcm_config * config)
46{
Eric Laurent49bc03c2017-05-03 11:33:26 -070047 int ret = 0;
48
Paul McLean98ca8d42015-01-13 09:41:36 -080049 ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
Paul Mclean2a3925e2016-06-02 14:48:10 +000050
Paul McLean98ca8d42015-01-13 09:41:36 -080051 proxy->profile = profile;
52
53#ifdef LOG_PCM_PARAMS
54 log_pcm_config(config, "proxy_setup()");
55#endif
56
Paul McLeana70650a2015-11-03 12:25:24 -080057 if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
58 proxy->alsa_config.format = config->format;
59 } else {
Eric Laurent49bc03c2017-05-03 11:33:26 -070060 proxy->alsa_config.format = profile->default_config.format;
Paul McLeana70650a2015-11-03 12:25:24 -080061 ALOGW("Invalid format %d - using default %d.",
62 config->format, profile->default_config.format);
Eric Laurent49bc03c2017-05-03 11:33:26 -070063 // Indicate override when default format was not requested
64 if (config->format != PCM_FORMAT_INVALID) {
65 ret = -EINVAL;
66 }
Paul McLeana70650a2015-11-03 12:25:24 -080067 }
68
69 if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
70 proxy->alsa_config.rate = config->rate;
71 } else {
Eric Laurent49bc03c2017-05-03 11:33:26 -070072 proxy->alsa_config.rate = profile->default_config.rate;
Paul McLeana70650a2015-11-03 12:25:24 -080073 ALOGW("Invalid sample rate %u - using default %u.",
74 config->rate, profile->default_config.rate);
Eric Laurent49bc03c2017-05-03 11:33:26 -070075 // Indicate override when default rate was not requested
76 if (config->rate != 0) {
77 ret = -EINVAL;
78 }
Paul McLeana70650a2015-11-03 12:25:24 -080079 }
80
81 if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
82 proxy->alsa_config.channels = config->channels;
83 } else {
Paul McLeaneafa18a2016-06-10 11:47:43 -060084 proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels);
85 ALOGW("Invalid channel count %u - using closest %u.",
86 config->channels, proxy->alsa_config.channels);
Eric Laurent49bc03c2017-05-03 11:33:26 -070087 // Indicate override when default channel count was not requested
88 if (config->channels != 0) {
89 ret = -EINVAL;
90 }
Paul McLeana70650a2015-11-03 12:25:24 -080091 }
Paul McLean98ca8d42015-01-13 09:41:36 -080092
93 proxy->alsa_config.period_count = profile->default_config.period_count;
94 proxy->alsa_config.period_size =
95 profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
96
97 // Hack for USB accessory audio.
98 // Here we set the correct value for period_count if tinyalsa fails to get it from the
99 // f_audio_source driver.
100 if (proxy->alsa_config.period_count == 0) {
101 proxy->alsa_config.period_count = 4;
102 }
103
104 proxy->pcm = NULL;
Andy Hung8785fe12015-08-04 14:57:50 -0700105 // config format should be checked earlier against profile.
106 if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
107 proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
108 } else {
109 proxy->frame_size = 1;
110 }
Paul McLean948a6a42017-03-08 15:06:27 -0700111
112 // let's check to make sure we can ACTUALLY use the maximum rate (with the channel count)
113 // Note that profile->sample_rates is sorted highest to lowest, so the scan will get
114 // us the highest working rate
115 int max_rate_index = proxy_scan_rates(proxy, profile->sample_rates);
116 if (max_rate_index >= 0) {
Eric Laurent49bc03c2017-05-03 11:33:26 -0700117 if (proxy->alsa_config.rate > profile->sample_rates[max_rate_index]) {
118 ALOGW("Limiting samplnig rate from %u to %u.",
119 proxy->alsa_config.rate, profile->sample_rates[max_rate_index]);
120 proxy->alsa_config.rate = profile->sample_rates[max_rate_index];
121 ret = -EINVAL;
122 }
Paul McLean948a6a42017-03-08 15:06:27 -0700123 }
Eric Laurent49bc03c2017-05-03 11:33:26 -0700124 return ret;
Paul McLean98ca8d42015-01-13 09:41:36 -0800125}
126
127int proxy_open(alsa_device_proxy * proxy)
128{
Andy Hung2c959782017-10-27 20:20:27 -0700129 const alsa_device_profile* profile = proxy->profile;
Paul McLean98ca8d42015-01-13 09:41:36 -0800130 ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
131 profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
132
133 if (profile->card < 0 || profile->device < 0) {
134 return -EINVAL;
135 }
136
Andy Hung8785fe12015-08-04 14:57:50 -0700137 proxy->pcm = pcm_open(profile->card, profile->device,
138 profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
Paul McLean98ca8d42015-01-13 09:41:36 -0800139 if (proxy->pcm == NULL) {
140 return -ENOMEM;
141 }
142
143 if (!pcm_is_ready(proxy->pcm)) {
Paul McLean948a6a42017-03-08 15:06:27 -0700144 ALOGE(" proxy_open() pcm_is_ready() failed: %s", pcm_get_error(proxy->pcm));
Paul McLean98ca8d42015-01-13 09:41:36 -0800145#if defined(LOG_PCM_PARAMS)
146 log_pcm_config(&proxy->alsa_config, "config");
147#endif
148 pcm_close(proxy->pcm);
149 proxy->pcm = NULL;
150 return -ENOMEM;
151 }
152
153 return 0;
154}
155
156void proxy_close(alsa_device_proxy * proxy)
157{
158 ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
159
160 if (proxy->pcm != NULL) {
161 pcm_close(proxy->pcm);
162 proxy->pcm = NULL;
163 }
164}
165
166/*
167 * Sample Rate
168 */
169unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
170{
171 return proxy->alsa_config.rate;
172}
173
174/*
175 * Format
176 */
177enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
178{
179 return proxy->alsa_config.format;
180}
181
182/*
183 * Channel Count
184 */
185unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
186{
187 return proxy->alsa_config.channels;
188}
189
190/*
191 * Other
192 */
193unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
194{
195 return proxy->alsa_config.period_size;
196}
197
198unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
199{
200 return proxy->alsa_config.period_count;
201}
202
203unsigned proxy_get_latency(const alsa_device_proxy * proxy)
204{
205 return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
206 / proxy_get_sample_rate(proxy);
207}
208
Andy Hung8785fe12015-08-04 14:57:50 -0700209int proxy_get_presentation_position(const alsa_device_proxy * proxy,
210 uint64_t *frames, struct timespec *timestamp)
211{
212 int ret = -EPERM; // -1
213 unsigned int avail;
214 if (proxy->pcm != NULL
215 && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
216 const size_t kernel_buffer_size =
217 proxy->alsa_config.period_size * proxy->alsa_config.period_count;
218 if (avail > kernel_buffer_size) {
219 ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
220 } else {
221 int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
222 // It is possible to compensate for additional driver and device delay
223 // by changing signed_frames. Example:
224 // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
225 if (signed_frames >= 0) {
226 *frames = signed_frames;
227 ret = 0;
228 }
229 }
230 }
231 return ret;
232}
233
Paul McLean98ca8d42015-01-13 09:41:36 -0800234/*
235 * I/O
236 */
Andy Hung8785fe12015-08-04 14:57:50 -0700237int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
Paul McLean98ca8d42015-01-13 09:41:36 -0800238{
Andy Hung8785fe12015-08-04 14:57:50 -0700239 int ret = pcm_write(proxy->pcm, data, count);
240 if (ret == 0) {
241 proxy->transferred += count / proxy->frame_size;
242 }
243 return ret;
Paul McLean98ca8d42015-01-13 09:41:36 -0800244}
245
246int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
247{
248 return pcm_read(proxy->pcm, data, count);
249}
Paul McLean21b04ad2016-05-25 14:07:00 -0600250
251/*
252 * Debugging
253 */
254void proxy_dump(const alsa_device_proxy* proxy, int fd)
255{
256 if (proxy != NULL) {
257 dprintf(fd, " channels: %d\n", proxy->alsa_config.channels);
258 dprintf(fd, " rate: %d\n", proxy->alsa_config.rate);
259 dprintf(fd, " period_size: %d\n", proxy->alsa_config.period_size);
260 dprintf(fd, " period_count: %d\n", proxy->alsa_config.period_count);
261 dprintf(fd, " format: %d\n", proxy->alsa_config.format);
262 }
263}
Paul McLean948a6a42017-03-08 15:06:27 -0700264
Andy Hung2c959782017-10-27 20:20:27 -0700265int proxy_scan_rates(alsa_device_proxy * proxy, const unsigned sample_rates[]) {
266 const alsa_device_profile* profile = proxy->profile;
Paul McLean948a6a42017-03-08 15:06:27 -0700267 if (profile->card < 0 || profile->device < 0) {
268 return -EINVAL;
269 }
270
271 struct pcm_config alsa_config;
272 memcpy(&alsa_config, &proxy->alsa_config, sizeof(alsa_config));
273
274 struct pcm * alsa_pcm;
275 int rate_index = 0;
276 while (sample_rates[rate_index] != 0) {
277 alsa_config.rate = sample_rates[rate_index];
278 alsa_pcm = pcm_open(profile->card, profile->device,
279 profile->direction | PCM_MONOTONIC, &alsa_config);
280 if (alsa_pcm != NULL) {
281 if (pcm_is_ready(alsa_pcm)) {
282 pcm_close(alsa_pcm);
283 return rate_index;
284 }
285
286 pcm_close(alsa_pcm);
287 }
288
289 rate_index++;
290 }
291
292 return -EINVAL;
293}