• Home
  • History
  • Annotate
  • 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      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