Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 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 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 17 | #include <mutex> |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 18 | #include <cutils/properties.h> |
Roman Kiryanov | 2333ad2 | 2020-07-17 19:05:04 -0700 | [diff] [blame] | 19 | #include <log/log.h> |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 20 | #include "talsa.h" |
Roman Kiryanov | b7539ca | 2020-07-10 00:17:56 -0700 | [diff] [blame] | 21 | #include "debug.h" |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 22 | |
| 23 | namespace android { |
| 24 | namespace hardware { |
| 25 | namespace audio { |
Mikhail Naganov | 62b7e0e | 2022-01-27 20:00:41 +0000 | [diff] [blame] | 26 | namespace CPP_VERSION { |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 27 | namespace implementation { |
| 28 | namespace talsa { |
| 29 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 30 | namespace { |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 31 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 32 | struct mixer *gMixer0 = nullptr; |
| 33 | int gMixerRefcounter0 = 0; |
| 34 | std::mutex gMixerMutex; |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 35 | PcmPeriodSettings gPcmPeriodSettings; |
Roman Kiryanov | 9603644 | 2022-07-28 17:40:54 -0700 | [diff] [blame] | 36 | unsigned gPcmHostLatencyMs; |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 37 | |
| 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 | } |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 43 | } |
| 44 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 95 | bool mixerUnref(struct mixer *mixer) { |
| 96 | std::lock_guard<std::mutex> guard(gMixerMutex); |
| 97 | |
| 98 | return mixerUnrefImpl(mixer, gMixer0, gMixerRefcounter0); |
| 99 | } |
| 100 | |
Roman Kiryanov | b0e1c9f | 2022-07-28 17:32:48 -0700 | [diff] [blame] | 101 | unsigned readUnsignedProperty(const char *propName, const unsigned defaultValue) { |
| 102 | char propValue[PROPERTY_VALUE_MAX]; |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 103 | |
Roman Kiryanov | b0e1c9f | 2022-07-28 17:32:48 -0700 | [diff] [blame] | 104 | if (property_get(propName, propValue, nullptr) < 0) { |
| 105 | return defaultValue; |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 106 | } |
| 107 | |
Roman Kiryanov | b0e1c9f | 2022-07-28 17:32:48 -0700 | [diff] [blame] | 108 | unsigned value; |
| 109 | return (sscanf(propValue, "%u", &value) == 1) ? value : defaultValue; |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 110 | } |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 111 | } // namespace |
| 112 | |
Roman Kiryanov | 71e5d59 | 2022-07-28 17:31:03 -0700 | [diff] [blame] | 113 | void init() { |
Roman Kiryanov | b0e1c9f | 2022-07-28 17:32:48 -0700 | [diff] [blame] | 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); |
Roman Kiryanov | 9603644 | 2022-07-28 17:40:54 -0700 | [diff] [blame] | 119 | |
| 120 | gPcmHostLatencyMs = |
| 121 | readUnsignedProperty("ro.hardware.audio.tinyalsa.host_latency_ms", 0); |
Roman Kiryanov | 71e5d59 | 2022-07-28 17:31:03 -0700 | [diff] [blame] | 122 | } |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 123 | |
Roman Kiryanov | 71e5d59 | 2022-07-28 17:31:03 -0700 | [diff] [blame] | 124 | PcmPeriodSettings pcmGetPcmPeriodSettings() { |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 125 | return gPcmPeriodSettings; |
| 126 | } |
| 127 | |
Roman Kiryanov | 9603644 | 2022-07-28 17:40:54 -0700 | [diff] [blame] | 128 | unsigned pcmGetHostLatencyMs() { |
| 129 | return gPcmHostLatencyMs; |
| 130 | } |
| 131 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 132 | void PcmDeleter::operator()(pcm_t *x) const { |
| 133 | LOG_ALWAYS_FATAL_IF(::pcm_close(x) != 0); |
| 134 | }; |
| 135 | |
Roman Kiryanov | 47f4785 | 2022-08-14 21:23:26 -0700 | [diff] [blame] | 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) { |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 142 | const PcmPeriodSettings periodSettings = pcmGetPcmPeriodSettings(); |
| 143 | |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 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; |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 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; |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 154 | pcm_config.format = PCM_FORMAT_S16_LE; |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 155 | |
Roman Kiryanov | ca2b861 | 2022-08-14 22:17:44 -0700 | [diff] [blame] | 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)) { |
Roman Kiryanov | 3f9f4d8 | 2020-06-17 15:31:30 -0700 | [diff] [blame] | 168 | ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu " |
Roman Kiryanov | 5922fa9 | 2022-07-27 15:35:57 -0700 | [diff] [blame] | 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, |
Roman Kiryanov | ca2b861 | 2022-08-14 22:17:44 -0700 | [diff] [blame] | 171 | ::pcm_get_error(pcmRaw)); |
Roman Kiryanov | b7539ca | 2020-07-10 00:17:56 -0700 | [diff] [blame] | 172 | return FAILURE(nullptr); |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 173 | } |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 174 | |
Roman Kiryanov | ca2b861 | 2022-08-14 22:17:44 -0700 | [diff] [blame] | 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); |
Roman Kiryanov | 77158cf | 2022-06-13 11:19:16 -0700 | [diff] [blame] | 181 | } |
| 182 | |
Roman Kiryanov | ca2b861 | 2022-08-14 22:17:44 -0700 | [diff] [blame] | 183 | return pcm; |
Roman Kiryanov | 174a791 | 2022-06-09 19:32:29 -0700 | [diff] [blame] | 184 | } |
| 185 | |
Roman Kiryanov | 174a791 | 2022-06-09 19:32:29 -0700 | [diff] [blame] | 186 | bool pcmRead(pcm_t *pcm, void *data, unsigned int count) { |
Roman Kiryanov | 77158cf | 2022-06-13 11:19:16 -0700 | [diff] [blame] | 187 | if (!pcm) { |
| 188 | return FAILURE(false); |
| 189 | } |
| 190 | |
Roman Kiryanov | 4a73d98 | 2022-08-25 13:27:13 -0700 | [diff] [blame] | 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 | } |
Roman Kiryanov | 174a791 | 2022-06-09 19:32:29 -0700 | [diff] [blame] | 211 | } |
| 212 | } |
| 213 | |
| 214 | bool pcmWrite(pcm_t *pcm, const void *data, unsigned int count) { |
Roman Kiryanov | 77158cf | 2022-06-13 11:19:16 -0700 | [diff] [blame] | 215 | if (!pcm) { |
| 216 | return FAILURE(false); |
| 217 | } |
| 218 | |
Roman Kiryanov | 4a73d98 | 2022-08-25 13:27:13 -0700 | [diff] [blame] | 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 | } |
Roman Kiryanov | 174a791 | 2022-06-09 19:32:29 -0700 | [diff] [blame] | 239 | } |
| 240 | } |
| 241 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 242 | Mixer::Mixer(unsigned card): mMixer(mixerGetOrOpen(card)) {} |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 243 | |
Roman Kiryanov | 2ab979a | 2020-07-17 19:07:03 -0700 | [diff] [blame] | 244 | Mixer::~Mixer() { |
| 245 | if (mMixer) { |
| 246 | LOG_ALWAYS_FATAL_IF(!mixerUnref(mMixer)); |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 247 | } |
| 248 | } |
| 249 | |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 250 | } // namespace talsa |
| 251 | } // namespace implementation |
Mikhail Naganov | 62b7e0e | 2022-01-27 20:00:41 +0000 | [diff] [blame] | 252 | } // namespace CPP_VERSION |
Roman Kiryanov | 9e6e2cb | 2020-05-21 11:37:32 -0700 | [diff] [blame] | 253 | } // namespace audio |
| 254 | } // namespace hardware |
| 255 | } // namespace android |