xref: /aosp_15_r20/system/media/alsa_utils/alsa_device_proxy.c (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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 <stdint.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include <audio_utils/clock.h>
29 
30 #include "include/alsa_device_proxy.h"
31 
32 #include "include/alsa_logging.h"
33 
34 #define DEFAULT_PERIOD_SIZE     1024
35 
36 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
37 
38 // These must use the same clock. If we change ALSA clock to real time, the system
39 // clock must be updated, too.
40 #define ALSA_CLOCK_TYPE PCM_MONOTONIC
41 #define SYSTEM_CLOCK_TYPE CLOCK_MONOTONIC
42 
43 static const unsigned format_byte_size_map[] = {
44     2, /* PCM_FORMAT_S16_LE */
45     4, /* PCM_FORMAT_S32_LE */
46     1, /* PCM_FORMAT_S8 */
47     4, /* PCM_FORMAT_S24_LE */
48     3, /* PCM_FORMAT_S24_3LE */
49 };
50 
proxy_prepare(alsa_device_proxy * proxy,const alsa_device_profile * profile,struct pcm_config * config,bool require_exact_match)51 int proxy_prepare(alsa_device_proxy * proxy, const alsa_device_profile* profile,
52                   struct pcm_config * config, bool require_exact_match)
53 {
54     int ret = 0;
55 
56     ALOGD("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
57 
58     proxy->profile = profile;
59 
60 #ifdef LOG_PCM_PARAMS
61     log_pcm_config(config, "proxy_setup()");
62 #endif
63 
64     if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
65         proxy->alsa_config.format = config->format;
66     } else if (require_exact_match) {
67         ret = -EINVAL;
68     } else {
69         proxy->alsa_config.format = profile->default_config.format;
70         ALOGW("Invalid format %d - using default %d.",
71               config->format, profile->default_config.format);
72         // Indicate override when default format was not requested
73         if (config->format != PCM_FORMAT_INVALID) {
74             ret = -EINVAL;
75         }
76     }
77 
78     if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
79         proxy->alsa_config.rate = config->rate;
80     } else if (require_exact_match) {
81         ret = -EINVAL;
82     } else {
83         proxy->alsa_config.rate = profile->default_config.rate;
84         ALOGW("Invalid sample rate %u - using default %u.",
85               config->rate, profile->default_config.rate);
86         // Indicate override when default rate was not requested
87         if (config->rate != 0) {
88             ret = -EINVAL;
89         }
90     }
91 
92     if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
93         proxy->alsa_config.channels = config->channels;
94     } else if (require_exact_match) {
95         ret = -EINVAL;
96     } else {
97         proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels);
98         ALOGW("Invalid channel count %u - using closest %u.",
99               config->channels, proxy->alsa_config.channels);
100         // Indicate override when default channel count was not requested
101         if (config->channels != 0) {
102             ret = -EINVAL;
103         }
104     }
105 
106     proxy->alsa_config.period_count = profile->default_config.period_count;
107     proxy->alsa_config.period_size =
108             profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
109 
110     // Hack for USB accessory audio.
111     // Here we set the correct value for period_count if tinyalsa fails to get it from the
112     // f_audio_source driver.
113     if (proxy->alsa_config.period_count == 0) {
114         proxy->alsa_config.period_count = DEFAULT_PERIOD_COUNT;
115     }
116 
117     proxy->pcm = NULL;
118     // config format should be checked earlier against profile.
119     if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
120         proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
121     } else {
122         proxy->frame_size = 1;
123     }
124 
125     // let's check to make sure we can ACTUALLY use the maximum rate (with the channel count)
126     // Note that profile->sample_rates is sorted highest to lowest, so the scan will get
127     // us the highest working rate
128     int max_rate_index = proxy_scan_rates(proxy, profile->sample_rates, require_exact_match);
129     if (max_rate_index >= 0) {
130         if (proxy->alsa_config.rate > profile->sample_rates[max_rate_index]) {
131             ALOGW("Limiting sampling rate from %u to %u.",
132                   proxy->alsa_config.rate, profile->sample_rates[max_rate_index]);
133             proxy->alsa_config.rate = profile->sample_rates[max_rate_index];
134             ret = -EINVAL;
135         }
136     }
137     return ret;
138 }
139 
proxy_prepare_from_default_config(alsa_device_proxy * proxy,const alsa_device_profile * profile)140 int proxy_prepare_from_default_config(alsa_device_proxy * proxy,
141         const alsa_device_profile * profile)
142 {
143     ALOGD("proxy_prepare_from_default_config(c:%d, d:%d)", profile->card, profile->device);
144 
145     proxy->profile = profile;
146 
147 #ifdef LOG_PCM_PARAMS
148     log_pcm_config(&profile->default_config, "proxy_prepare_from_default_config()");
149 #endif
150 
151     proxy->alsa_config.format = profile->default_config.format;
152     proxy->alsa_config.rate = profile->default_config.rate;
153     proxy->alsa_config.channels = profile->default_config.channels;
154     proxy->alsa_config.period_count = profile->default_config.period_count;
155     proxy->alsa_config.period_size = profile->default_config.period_size;
156     proxy->pcm = NULL;
157     enum pcm_format format = profile->default_config.format;
158     if (format >= 0 && (size_t)format < ARRAY_SIZE(format_byte_size_map)) {
159         proxy->frame_size = format_byte_size_map[format] * proxy->alsa_config.channels;
160     } else {
161         proxy->frame_size = 1;
162     }
163 
164     return 0;
165 }
166 
proxy_open(alsa_device_proxy * proxy)167 int proxy_open(alsa_device_proxy * proxy)
168 {
169     const alsa_device_profile* profile = proxy->profile;
170     ALOGD("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
171           profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
172 
173     if (profile->card < 0 || profile->device < 0) {
174         return -EINVAL;
175     }
176 
177     proxy->pcm = pcm_open(profile->card, profile->device,
178             profile->direction | ALSA_CLOCK_TYPE, &proxy->alsa_config);
179     if (proxy->pcm == NULL) {
180         return -ENOMEM;
181     }
182 
183     if (!pcm_is_ready(proxy->pcm)) {
184         ALOGE("  proxy_open() pcm_is_ready() failed: %s", pcm_get_error(proxy->pcm));
185 #if defined(LOG_PCM_PARAMS)
186         log_pcm_config(&proxy->alsa_config, "config");
187 #endif
188         pcm_close(proxy->pcm);
189         proxy->pcm = NULL;
190         return -ENOMEM;
191     }
192 
193     return 0;
194 }
195 
proxy_close(alsa_device_proxy * proxy)196 void proxy_close(alsa_device_proxy * proxy)
197 {
198     ALOGD("proxy_close() [pcm:%p]", proxy->pcm);
199 
200     if (proxy->pcm != NULL) {
201         pcm_close(proxy->pcm);
202         proxy->pcm = NULL;
203     }
204 }
205 
206 /*
207  * Sample Rate
208  */
proxy_get_sample_rate(const alsa_device_proxy * proxy)209 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
210 {
211     return proxy->alsa_config.rate;
212 }
213 
214 /*
215  * Format
216  */
proxy_get_format(const alsa_device_proxy * proxy)217 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
218 {
219     return proxy->alsa_config.format;
220 }
221 
222 /*
223  * Channel Count
224  */
proxy_get_channel_count(const alsa_device_proxy * proxy)225 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
226 {
227     return proxy->alsa_config.channels;
228 }
229 
230 /*
231  * Other
232  */
proxy_get_period_size(const alsa_device_proxy * proxy)233 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
234 {
235     return proxy->alsa_config.period_size;
236 }
237 
proxy_get_period_count(const alsa_device_proxy * proxy)238 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
239 {
240     return proxy->alsa_config.period_count;
241 }
242 
proxy_get_extra_latency_ms(const alsa_device_proxy * proxy)243 static unsigned int proxy_get_extra_latency_ms(const alsa_device_proxy * proxy)
244 {
245     return proxy->profile->extra_latency_ms;
246 }
247 
proxy_get_latency(const alsa_device_proxy * proxy)248 unsigned proxy_get_latency(const alsa_device_proxy * proxy)
249 {
250     return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
251             / proxy_get_sample_rate(proxy) + proxy_get_extra_latency_ms(proxy);
252 }
253 
proxy_get_presentation_position(const alsa_device_proxy * proxy,uint64_t * frames,struct timespec * timestamp)254 int proxy_get_presentation_position(const alsa_device_proxy * proxy,
255         uint64_t *frames, struct timespec *timestamp)
256 {
257     int ret = -EPERM; // -1
258     unsigned int avail;
259     struct timespec alsaTs;
260     if (proxy->pcm != NULL
261             && pcm_get_htimestamp(proxy->pcm, &avail, &alsaTs) == 0) {
262         const size_t kernel_buffer_size = pcm_get_buffer_size(proxy->pcm);
263         if (avail > kernel_buffer_size) {
264             // pcm_get_htimestamp() computes the available frames by comparing the ALSA driver
265             // hw_ptr and the appl_ptr levels. In underrun, the hw_ptr may keep running and report
266             // an excessively large number available number.
267             ALOGW("available frames(%u) > buffer size(%zu), clamped", avail, kernel_buffer_size);
268             avail = kernel_buffer_size;
269         }
270         if (alsaTs.tv_sec != 0 || alsaTs.tv_nsec != 0) {
271             *timestamp = alsaTs;
272         } else {  // If ALSA returned a zero timestamp, do not use it.
273             clock_gettime(SYSTEM_CLOCK_TYPE, timestamp);
274         }
275         int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
276         // It is possible to compensate for additional driver and device delay
277         // by changing signed_frames.  Example:
278         // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
279         if (signed_frames >= 0) {
280             *frames = signed_frames;
281             ret = 0;
282         }
283     }
284     return ret;
285 }
286 
proxy_get_capture_position(const alsa_device_proxy * proxy,int64_t * frames,int64_t * time)287 int proxy_get_capture_position(const alsa_device_proxy * proxy,
288         int64_t *frames, int64_t *time)
289 {
290     int ret = -ENOSYS;
291     unsigned int avail;
292     struct timespec timestamp;
293     if (proxy->pcm != NULL
294             && pcm_get_htimestamp(proxy->pcm, &avail, &timestamp) == 0) {
295         if (timestamp.tv_sec == 0 && timestamp.tv_nsec == 0) {
296             // If ALSA returned a zero timestamp, do not use it.
297             clock_gettime(SYSTEM_CLOCK_TYPE, &timestamp);
298         }
299         uint64_t framesTemp = proxy->transferred + avail;
300         if (framesTemp > INT64_MAX) {
301             framesTemp -= INT64_MAX;
302         }
303         *frames = framesTemp;
304         *time = audio_utils_ns_from_timespec(&timestamp);
305         ret = 0;
306     }
307     return ret;
308 }
309 
310 /*
311  * I/O
312  */
proxy_write(alsa_device_proxy * proxy,const void * data,unsigned int count)313 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
314 {
315     return proxy_write_with_retries(proxy, data, count, 1);
316 }
317 
proxy_write_with_retries(alsa_device_proxy * proxy,const void * data,unsigned int count,int tries)318 int proxy_write_with_retries(
319         alsa_device_proxy * proxy, const void *data, unsigned int count, int tries)
320 {
321     while (true) {
322         --tries;
323         const int ret = pcm_write(proxy->pcm, data, count);
324         if (ret == 0) {
325             proxy->transferred += count / proxy->frame_size;
326             return 0;
327         } else if (tries > 0 && (ret == -EIO || ret == -EAGAIN)) {
328             continue;
329         }
330         return ret;
331     }
332 }
333 
proxy_read(alsa_device_proxy * proxy,void * data,unsigned int count)334 int proxy_read(alsa_device_proxy * proxy, void *data, unsigned int count)
335 {
336     return proxy_read_with_retries(proxy, data, count, 1);
337 }
338 
proxy_read_with_retries(alsa_device_proxy * proxy,void * data,unsigned int count,int tries)339 int proxy_read_with_retries(alsa_device_proxy * proxy, void *data, unsigned int count, int tries)
340 {
341     while (true) {
342         --tries;
343         const int ret = pcm_read(proxy->pcm, data, count);
344         if (ret == 0) {
345             proxy->transferred += count / proxy->frame_size;
346             return 0;
347         } else if (tries > 0 && (ret == -EIO || ret == -EAGAIN)) {
348             continue;
349         }
350         return ret;
351     }
352 }
353 
354 /*
355  * Debugging
356  */
proxy_dump(const alsa_device_proxy * proxy,int fd)357 void proxy_dump(const alsa_device_proxy* proxy, int fd)
358 {
359     if (proxy != NULL) {
360         dprintf(fd, "  channels: %d\n", proxy->alsa_config.channels);
361         dprintf(fd, "  rate: %d\n", proxy->alsa_config.rate);
362         dprintf(fd, "  period_size: %d\n", proxy->alsa_config.period_size);
363         dprintf(fd, "  period_count: %d\n", proxy->alsa_config.period_count);
364         dprintf(fd, "  format: %d\n", proxy->alsa_config.format);
365     }
366 }
367 
proxy_scan_rates(alsa_device_proxy * proxy,const unsigned sample_rates[],bool require_exact_match)368 int proxy_scan_rates(alsa_device_proxy * proxy,
369                      const unsigned sample_rates[],
370                      bool require_exact_match) {
371     const alsa_device_profile* profile = proxy->profile;
372     if (profile->card < 0 || profile->device < 0) {
373         return -EINVAL;
374     }
375 
376     struct pcm_config alsa_config;
377     memcpy(&alsa_config, &proxy->alsa_config, sizeof(alsa_config));
378 
379     struct pcm * alsa_pcm;
380     int rate_index = 0;
381     while (sample_rates[rate_index] != 0) {
382         if (require_exact_match && alsa_config.rate != sample_rates[rate_index]) {
383             rate_index++;
384             continue;
385         }
386         alsa_config.rate = sample_rates[rate_index];
387         alsa_pcm = pcm_open(profile->card, profile->device,
388                 profile->direction | ALSA_CLOCK_TYPE, &alsa_config);
389         if (alsa_pcm != NULL) {
390             if (pcm_is_ready(alsa_pcm)) {
391                 pcm_close(alsa_pcm);
392                 return rate_index;
393             }
394 
395             pcm_close(alsa_pcm);
396         }
397 
398         rate_index++;
399     }
400 
401     return -EINVAL;
402 }
403