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
156 pcm_t *pcmRaw = ::pcm_open(dev, card,
157 (isOut ? PCM_OUT : PCM_IN) | PCM_MONOTONIC,
158 &pcm_config);
159 if (!pcmRaw) {
160 ALOGE("%s:%d pcm_open returned nullptr for nChannels=%u sampleRateHz=%zu "
161 "period_count=%d period_size=%d isOut=%d", __func__, __LINE__,
162 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut);
163 return FAILURE(nullptr);
164 }
165
166 PcmPtr pcm(pcmRaw);
167 if (!::pcm_is_ready(pcmRaw)) {
168 ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu "
169 "period_count=%d period_size=%d isOut=%d with %s", __func__, __LINE__,
170 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut,
171 ::pcm_get_error(pcmRaw));
172 return FAILURE(nullptr);
173 }
174
175 if (const int err = ::pcm_prepare(pcmRaw)) {
176 ALOGE("%s:%d pcm_prepare failed for nChannels=%u sampleRateHz=%zu "
177 "period_count=%d period_size=%d isOut=%d with %s (%d)", __func__, __LINE__,
178 nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut,
179 ::pcm_get_error(pcmRaw), err);
180 return FAILURE(nullptr);
181 }
182
183 return pcm;
184 }
185
pcmRead(pcm_t * pcm,void * data,unsigned int count)186 bool pcmRead(pcm_t *pcm, void *data, unsigned int count) {
187 if (!pcm) {
188 return FAILURE(false);
189 }
190
191 int tries = 3;
192 while (true) {
193 --tries;
194 const int r = ::pcm_read(pcm, data, count);
195 switch (-r) {
196 case 0:
197 return true;
198
199 case EIO:
200 case EAGAIN:
201 if (tries > 0) {
202 break;
203 }
204 [[fallthrough]];
205
206 default:
207 ALOGW("%s:%d pcm_read failed with '%s' (%d)",
208 __func__, __LINE__, ::pcm_get_error(pcm), r);
209 return FAILURE(false);
210 }
211 }
212 }
213
pcmWrite(pcm_t * pcm,const void * data,unsigned int count)214 bool pcmWrite(pcm_t *pcm, const void *data, unsigned int count) {
215 if (!pcm) {
216 return FAILURE(false);
217 }
218
219 int tries = 3;
220 while (true) {
221 --tries;
222 const int r = ::pcm_write(pcm, data, count);
223 switch (-r) {
224 case 0:
225 return true;
226
227 case EIO:
228 case EAGAIN:
229 if (tries > 0) {
230 break;
231 }
232 [[fallthrough]];
233
234 default:
235 ALOGW("%s:%d pcm_write failed with '%s' (%d)",
236 __func__, __LINE__, ::pcm_get_error(pcm), r);
237 return FAILURE(false);
238 }
239 }
240 }
241
Mixer(unsigned card)242 Mixer::Mixer(unsigned card): mMixer(mixerGetOrOpen(card)) {}
243
~Mixer()244 Mixer::~Mixer() {
245 if (mMixer) {
246 LOG_ALWAYS_FATAL_IF(!mixerUnref(mMixer));
247 }
248 }
249
250 } // namespace talsa
251 } // namespace implementation
252 } // namespace CPP_VERSION
253 } // namespace audio
254 } // namespace hardware
255 } // namespace android
256