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 
proxy_prepare(alsa_device_proxy * proxy,alsa_device_profile * profile,struct pcm_config * config)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     proxy->alsa_config.format =
54         config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)
55             ? config->format : profile->default_config.format;
56     proxy->alsa_config.rate =
57         config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)
58             ? config->rate : profile->default_config.rate;
59     proxy->alsa_config.channels =
60         config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)
61             ? config->channels : profile->default_config.channels;
62 
63     proxy->alsa_config.period_count = profile->default_config.period_count;
64     proxy->alsa_config.period_size =
65             profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
66 
67     // Hack for USB accessory audio.
68     // Here we set the correct value for period_count if tinyalsa fails to get it from the
69     // f_audio_source driver.
70     if (proxy->alsa_config.period_count == 0) {
71         proxy->alsa_config.period_count = 4;
72     }
73 
74     proxy->pcm = NULL;
75     // config format should be checked earlier against profile.
76     if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
77         proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
78     } else {
79         proxy->frame_size = 1;
80     }
81 }
82 
proxy_open(alsa_device_proxy * proxy)83 int proxy_open(alsa_device_proxy * proxy)
84 {
85     alsa_device_profile* profile = proxy->profile;
86     ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
87           profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
88 
89     if (profile->card < 0 || profile->device < 0) {
90         return -EINVAL;
91     }
92 
93     proxy->pcm = pcm_open(profile->card, profile->device,
94             profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
95     if (proxy->pcm == NULL) {
96         return -ENOMEM;
97     }
98 
99     if (!pcm_is_ready(proxy->pcm)) {
100         ALOGE("  proxy_open() pcm_open() failed: %s", pcm_get_error(proxy->pcm));
101 #if defined(LOG_PCM_PARAMS)
102         log_pcm_config(&proxy->alsa_config, "config");
103 #endif
104         pcm_close(proxy->pcm);
105         proxy->pcm = NULL;
106         return -ENOMEM;
107     }
108 
109     return 0;
110 }
111 
proxy_close(alsa_device_proxy * proxy)112 void proxy_close(alsa_device_proxy * proxy)
113 {
114     ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
115 
116     if (proxy->pcm != NULL) {
117         pcm_close(proxy->pcm);
118         proxy->pcm = NULL;
119     }
120 }
121 
122 /*
123  * Sample Rate
124  */
proxy_get_sample_rate(const alsa_device_proxy * proxy)125 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
126 {
127     return proxy->alsa_config.rate;
128 }
129 
130 /*
131  * Format
132  */
proxy_get_format(const alsa_device_proxy * proxy)133 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
134 {
135     return proxy->alsa_config.format;
136 }
137 
138 /*
139  * Channel Count
140  */
proxy_get_channel_count(const alsa_device_proxy * proxy)141 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
142 {
143     return proxy->alsa_config.channels;
144 }
145 
146 /*
147  * Other
148  */
proxy_get_period_size(const alsa_device_proxy * proxy)149 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
150 {
151     return proxy->alsa_config.period_size;
152 }
153 
proxy_get_period_count(const alsa_device_proxy * proxy)154 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
155 {
156     return proxy->alsa_config.period_count;
157 }
158 
proxy_get_latency(const alsa_device_proxy * proxy)159 unsigned proxy_get_latency(const alsa_device_proxy * proxy)
160 {
161     return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
162                / proxy_get_sample_rate(proxy);
163 }
164 
proxy_get_presentation_position(const alsa_device_proxy * proxy,uint64_t * frames,struct timespec * timestamp)165 int proxy_get_presentation_position(const alsa_device_proxy * proxy,
166         uint64_t *frames, struct timespec *timestamp)
167 {
168     int ret = -EPERM; // -1
169     unsigned int avail;
170     if (proxy->pcm != NULL
171             && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
172         const size_t kernel_buffer_size =
173                 proxy->alsa_config.period_size * proxy->alsa_config.period_count;
174         if (avail > kernel_buffer_size) {
175             ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
176         } else {
177             int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
178             // It is possible to compensate for additional driver and device delay
179             // by changing signed_frames.  Example:
180             // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
181             if (signed_frames >= 0) {
182                 *frames = signed_frames;
183                 ret = 0;
184             }
185         }
186     }
187     return ret;
188 }
189 
190 /*
191  * I/O
192  */
proxy_write(alsa_device_proxy * proxy,const void * data,unsigned int count)193 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
194 {
195     int ret = pcm_write(proxy->pcm, data, count);
196     if (ret == 0) {
197         proxy->transferred += count / proxy->frame_size;
198     }
199     return ret;
200 }
201 
proxy_read(const alsa_device_proxy * proxy,void * data,unsigned int count)202 int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
203 {
204     return pcm_read(proxy->pcm, data, count);
205 }
206