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