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