1 /*
2 * Copyright (C) 2020 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 #include <mutex>
18 #include <cutils/properties.h>
19 #include <log/log.h>
20 #include "talsa.h"
21 #include "debug.h"
22
23 namespace android {
24 namespace hardware {
25 namespace audio {
26 namespace CPP_VERSION {
27 namespace implementation {
28 namespace talsa {
29
30 namespace {
31
32 struct mixer *gMixer0 = nullptr;
33 int gMixerRefcounter0 = 0;
34 std::mutex gMixerMutex;
35 PcmPeriodSettings gPcmPeriodSettings;
36 unsigned gPcmHostLatencyMs;
37
mixerSetValueAll(struct mixer_ctl * ctl,int value)38 void mixerSetValueAll(struct mixer_ctl *ctl, int value) {
39 const unsigned int n = mixer_ctl_get_num_values(ctl);
40 for (unsigned int i = 0; i < n; i++) {
41 ::mixer_ctl_set_value(ctl, i, value);
42 }
43 }
44
mixerSetPercentAll(struct mixer_ctl * ctl,int percent)45 void mixerSetPercentAll(struct mixer_ctl *ctl, int percent) {
46 const unsigned int n = mixer_ctl_get_num_values(ctl);
47 for (unsigned int i = 0; i < n; i++) {
48 ::mixer_ctl_set_percent(ctl, i, percent);
49 }
50 }
51
mixerGetOrOpenImpl(const unsigned card,struct mixer * & gMixer,int & refcounter)52 struct mixer *mixerGetOrOpenImpl(const unsigned card,
53 struct mixer *&gMixer,
54 int &refcounter) {
55 if (!gMixer) {
56 struct mixer *mixer = ::mixer_open(card);
57 if (!mixer) {
58 return FAILURE(nullptr);
59 }
60
61 mixerSetPercentAll(::mixer_get_ctl_by_name(mixer, "Master Playback Volume"), 100);
62 mixerSetPercentAll(::mixer_get_ctl_by_name(mixer, "Capture Volume"), 100);
63
64 mixerSetValueAll(::mixer_get_ctl_by_name(mixer, "Master Playback Switch"), 1);
65 mixerSetValueAll(::mixer_get_ctl_by_name(mixer, "Capture Switch"), 1);
66
67 gMixer = mixer;
68 }
69
70 ++refcounter;
71 return gMixer;
72 }
73
mixerGetOrOpen(const unsigned card)74 struct mixer *mixerGetOrOpen(const unsigned card) {
75 std::lock_guard<std::mutex> guard(gMixerMutex);
76
77 switch (card) {
78 case 0: return mixerGetOrOpenImpl(card, gMixer0, gMixerRefcounter0);
79 default: return FAILURE(nullptr);
80 }
81 }
82
mixerUnrefImpl(struct mixer * mixer,struct mixer * & gMixer,int & refcounter)83 bool mixerUnrefImpl(struct mixer *mixer, struct mixer *&gMixer, int &refcounter) {
84 if (mixer == gMixer) {
85 if (0 == --refcounter) {
86 ::mixer_close(mixer);
87 gMixer = nullptr;
88 }
89 return true;
90 } else {
91 return false;
92 }
93 }
94
mixerUnref(struct mixer * mixer)95 bool mixerUnref(struct mixer *mixer) {
96 std::lock_guard<std::mutex> guard(gMixerMutex);
97
98 return mixerUnrefImpl(mixer, gMixer0, gMixerRefcounter0);
99 }
100
readUnsignedProperty(const char * propName,const unsigned defaultValue)101 unsigned readUnsignedProperty(const char *propName, const unsigned defaultValue) {
102 char propValue[PROPERTY_VALUE_MAX];
103
104 if (property_get(propName, propValue, nullptr) < 0) {
105 return defaultValue;
106 }
107
108 unsigned value;
109 return (sscanf(propValue, "%u", &value) == 1) ? value : defaultValue;
110 }
111 } // namespace
112
init()113 void init() {
114 gPcmPeriodSettings.periodCount =
115 readUnsignedProperty("ro.hardware.audio.tinyalsa.period_count", 4);
116
117 gPcmPeriodSettings.periodSizeMultiplier =
118 readUnsignedProperty("ro.hardware.audio.tinyalsa.period_size_multiplier", 1);
119
120 gPcmHostLatencyMs =
121 readUnsignedProperty("ro.hardware.audio.tinyalsa.host_latency_ms", 0);
122 }
123
pcmGetPcmPeriodSettings()124 PcmPeriodSettings pcmGetPcmPeriodSettings() {
125 return gPcmPeriodSettings;
126 }
127
pcmGetHostLatencyMs()128 unsigned pcmGetHostLatencyMs() {
129 return gPcmHostLatencyMs;
130 }
131
operator ()(pcm_t * x) const132 void PcmDeleter::operator()(pcm_t *x) const {
133 LOG_ALWAYS_FATAL_IF(::pcm_close(x) != 0);
134 };
135
pcmOpen(const unsigned int dev,const unsigned int card,const unsigned int nChannels,const size_t sampleRateHz,const size_t frameCount,const bool isOut)136 PcmPtr pcmOpen(const unsigned int dev,
137 const unsigned int card,
138 const unsigned int nChannels,
139 const size_t sampleRateHz,
140 const size_t frameCount,
141 const bool isOut) {
142 const PcmPeriodSettings periodSettings = pcmGetPcmPeriodSettings();
143
144 struct pcm_config pcm_config;
145 memset(&pcm_config, 0, sizeof(pcm_config));
146
147 pcm_config.channels = nChannels;
148 pcm_config.rate = sampleRateHz;
149 // Approx interrupts per buffer
150 pcm_config.period_count = periodSettings.periodCount;
151 // Approx frames between interrupts
152 pcm_config.period_size =
153 periodSettings.periodSizeMultiplier * frameCount / periodSettings.periodCount;
154 pcm_config.format = PCM_FORMAT_S16_LE;
155 if (isOut) {
156 pcm_config.start_threshold = pcm_config.period_size * (pcm_config.period_count - 1);
157 pcm_config.stop_threshold = pcm_config.period_size * pcm_config.period_count;
158 }
159
160 pcm_t *pcmRaw = ::pcm_open(dev, card,
161 (isOut ? PCM_OUT : PCM_IN) | PCM_MONOTONIC,
162 &pcm_config);
163 if (!pcmRaw) {
164 ALOGE("%s:%d pcm_open returned nullptr for nChannels=%u sampleRateHz=%zu "
165 "period_count=%d period_size=%d isOut=%d", __func__, __LINE__,
166 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut);
167 return FAILURE(nullptr);
168 }
169
170 PcmPtr pcm(pcmRaw);
171 if (!::pcm_is_ready(pcmRaw)) {
172 ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu "
173 "period_count=%d period_size=%d isOut=%d with %s", __func__, __LINE__,
174 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut,
175 ::pcm_get_error(pcmRaw));
176 return FAILURE(nullptr);
177 }
178
179 if (const int err = ::pcm_prepare(pcmRaw)) {
180 ALOGE("%s:%d pcm_prepare failed for nChannels=%u sampleRateHz=%zu "
181 "period_count=%d period_size=%d isOut=%d with %s (%d)", __func__, __LINE__,
182 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut,
183 ::pcm_get_error(pcmRaw), err);
184 return FAILURE(nullptr);
185 }
186
187 return pcm;
188 }
189
pcmRead(pcm_t * pcm,void * data,const int szBytes,const unsigned int frameSize)190 int pcmRead(pcm_t *pcm, void *data, const int szBytes,
191 const unsigned int frameSize) {
192 LOG_ALWAYS_FATAL_IF(frameSize == 0);
193 LOG_ALWAYS_FATAL_IF(szBytes < 0, "szBytes=%d", szBytes);
194 LOG_ALWAYS_FATAL_IF((szBytes % frameSize) != 0, "szBytes=%d frameSize=%u",
195 szBytes, frameSize);
196 if (!pcm) {
197 return FAILURE(-1);
198 }
199
200 const int szFrames = szBytes / frameSize;
201 int tries = 3;
202 while (true) {
203 const int framesRead = ::pcm_readi(pcm, data, szFrames);
204 if (framesRead > 0) {
205 LOG_ALWAYS_FATAL_IF(framesRead > szFrames,
206 "framesRead=%d szFrames=%d szBytes=%u frameSize=%u",
207 framesRead, szFrames, szBytes, frameSize);
208 return framesRead * frameSize;
209 } else {
210 --tries;
211 switch (-framesRead) {
212 case EIO:
213 case EAGAIN:
214 if (tries > 0) {
215 break;
216 }
217 [[fallthrough]];
218
219 default:
220 ALOGW("%s:%d pcm_readi failed with '%s' (%d)",
221 __func__, __LINE__, ::pcm_get_error(pcm), framesRead);
222 return FAILURE(-1);
223 }
224 }
225 }
226 }
227
pcmWrite(pcm_t * pcm,const void * data,const int szBytes,const unsigned int frameSize)228 int pcmWrite(pcm_t *pcm, const void *data, const int szBytes,
229 const unsigned int frameSize) {
230 LOG_ALWAYS_FATAL_IF(frameSize == 0);
231 LOG_ALWAYS_FATAL_IF(szBytes < 0, "szBytes=%d", szBytes);
232 LOG_ALWAYS_FATAL_IF((szBytes % frameSize) != 0, "szBytes=%d frameSize=%u",
233 szBytes, frameSize);
234 if (!pcm) {
235 return FAILURE(-1);
236 }
237
238 const int szFrames = szBytes / frameSize;
239 int tries = 3;
240 while (true) {
241 const int framesWritten = ::pcm_writei(pcm, data, szFrames);
242 if (framesWritten > 0) {
243 LOG_ALWAYS_FATAL_IF(framesWritten > szFrames,
244 "framesWritten=%d szFrames=%d szBytes=%u frameSize=%u",
245 framesWritten, szFrames, szBytes, frameSize);
246 return framesWritten * frameSize;
247 } else {
248 --tries;
249 switch (-framesWritten) {
250 case EIO:
251 case EAGAIN:
252 if (tries > 0) {
253 break;
254 }
255 [[fallthrough]];
256
257 default:
258 ALOGW("%s:%d pcm_writei failed with '%s' (%d)",
259 __func__, __LINE__, ::pcm_get_error(pcm), framesWritten);
260 return FAILURE(-1);
261 }
262 }
263 }
264 }
265
Mixer(unsigned card)266 Mixer::Mixer(unsigned card): mMixer(mixerGetOrOpen(card)) {}
267
~Mixer()268 Mixer::~Mixer() {
269 if (mMixer) {
270 LOG_ALWAYS_FATAL_IF(!mixerUnref(mMixer));
271 }
272 }
273
274 } // namespace talsa
275 } // namespace implementation
276 } // namespace CPP_VERSION
277 } // namespace audio
278 } // namespace hardware
279 } // namespace android
280