• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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