blob: 4c2d7f8474e12d6ebd248f6087dfa76708db8e78 [file] [log] [blame]
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -07001/*
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 Kiryanov2ab979a2020-07-17 19:07:03 -070017#include <mutex>
Roman Kiryanov5922fa92022-07-27 15:35:57 -070018#include <cutils/properties.h>
Roman Kiryanov2333ad22020-07-17 19:05:04 -070019#include <log/log.h>
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -070020#include "talsa.h"
Roman Kiryanovb7539ca2020-07-10 00:17:56 -070021#include "debug.h"
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -070022
23namespace android {
24namespace hardware {
25namespace audio {
Mikhail Naganov62b7e0e2022-01-27 20:00:41 +000026namespace CPP_VERSION {
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -070027namespace implementation {
28namespace talsa {
29
Roman Kiryanov2ab979a2020-07-17 19:07:03 -070030namespace {
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -070031
Roman Kiryanov2ab979a2020-07-17 19:07:03 -070032struct mixer *gMixer0 = nullptr;
33int gMixerRefcounter0 = 0;
34std::mutex gMixerMutex;
Roman Kiryanov5922fa92022-07-27 15:35:57 -070035PcmPeriodSettings gPcmPeriodSettings;
Roman Kiryanov96036442022-07-28 17:40:54 -070036unsigned gPcmHostLatencyMs;
Roman Kiryanov2ab979a2020-07-17 19:07:03 -070037
38void 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 Kiryanov9e6e2cb2020-05-21 11:37:32 -070043}
44
Roman Kiryanov2ab979a2020-07-17 19:07:03 -070045void 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
52struct 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
74struct 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
83bool 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
95bool mixerUnref(struct mixer *mixer) {
96 std::lock_guard<std::mutex> guard(gMixerMutex);
97
98 return mixerUnrefImpl(mixer, gMixer0, gMixerRefcounter0);
99}
100
Roman Kiryanovb0e1c9f2022-07-28 17:32:48 -0700101unsigned readUnsignedProperty(const char *propName, const unsigned defaultValue) {
102 char propValue[PROPERTY_VALUE_MAX];
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700103
Roman Kiryanovb0e1c9f2022-07-28 17:32:48 -0700104 if (property_get(propName, propValue, nullptr) < 0) {
105 return defaultValue;
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700106 }
107
Roman Kiryanovb0e1c9f2022-07-28 17:32:48 -0700108 unsigned value;
109 return (sscanf(propValue, "%u", &value) == 1) ? value : defaultValue;
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700110}
Roman Kiryanov2ab979a2020-07-17 19:07:03 -0700111} // namespace
112
Roman Kiryanov71e5d592022-07-28 17:31:03 -0700113void init() {
Roman Kiryanovb0e1c9f2022-07-28 17:32:48 -0700114 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 Kiryanov96036442022-07-28 17:40:54 -0700119
120 gPcmHostLatencyMs =
121 readUnsignedProperty("ro.hardware.audio.tinyalsa.host_latency_ms", 0);
Roman Kiryanov71e5d592022-07-28 17:31:03 -0700122}
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700123
Roman Kiryanov71e5d592022-07-28 17:31:03 -0700124PcmPeriodSettings pcmGetPcmPeriodSettings() {
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700125 return gPcmPeriodSettings;
126}
127
Roman Kiryanov96036442022-07-28 17:40:54 -0700128unsigned pcmGetHostLatencyMs() {
129 return gPcmHostLatencyMs;
130}
131
Roman Kiryanov2ab979a2020-07-17 19:07:03 -0700132void PcmDeleter::operator()(pcm_t *x) const {
133 LOG_ALWAYS_FATAL_IF(::pcm_close(x) != 0);
134};
135
Roman Kiryanov47f47852022-08-14 21:23:26 -0700136PcmPtr 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 Kiryanov5922fa92022-07-27 15:35:57 -0700142 const PcmPeriodSettings periodSettings = pcmGetPcmPeriodSettings();
143
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700144 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 Kiryanov5922fa92022-07-27 15:35:57 -0700149 // 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 Kiryanov9e6e2cb2020-05-21 11:37:32 -0700154 pcm_config.format = PCM_FORMAT_S16_LE;
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700155
Roman Kiryanovca2b8612022-08-14 22:17:44 -0700156 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 Kiryanov3f9f4d82020-06-17 15:31:30 -0700168 ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu "
Roman Kiryanov5922fa92022-07-27 15:35:57 -0700169 "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 Kiryanovca2b8612022-08-14 22:17:44 -0700171 ::pcm_get_error(pcmRaw));
Roman Kiryanovb7539ca2020-07-10 00:17:56 -0700172 return FAILURE(nullptr);
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700173 }
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700174
Roman Kiryanovca2b8612022-08-14 22:17:44 -0700175 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 Kiryanov77158cf2022-06-13 11:19:16 -0700181 }
182
Roman Kiryanovca2b8612022-08-14 22:17:44 -0700183 return pcm;
Roman Kiryanov174a7912022-06-09 19:32:29 -0700184}
185
Roman Kiryanov174a7912022-06-09 19:32:29 -0700186bool pcmRead(pcm_t *pcm, void *data, unsigned int count) {
Roman Kiryanov77158cf2022-06-13 11:19:16 -0700187 if (!pcm) {
188 return FAILURE(false);
189 }
190
Roman Kiryanov4a73d982022-08-25 13:27:13 -0700191 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 Kiryanov174a7912022-06-09 19:32:29 -0700211 }
212}
213
214bool pcmWrite(pcm_t *pcm, const void *data, unsigned int count) {
Roman Kiryanov77158cf2022-06-13 11:19:16 -0700215 if (!pcm) {
216 return FAILURE(false);
217 }
218
Roman Kiryanov4a73d982022-08-25 13:27:13 -0700219 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 Kiryanov174a7912022-06-09 19:32:29 -0700239 }
240}
241
Roman Kiryanov2ab979a2020-07-17 19:07:03 -0700242Mixer::Mixer(unsigned card): mMixer(mixerGetOrOpen(card)) {}
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700243
Roman Kiryanov2ab979a2020-07-17 19:07:03 -0700244Mixer::~Mixer() {
245 if (mMixer) {
246 LOG_ALWAYS_FATAL_IF(!mixerUnref(mMixer));
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700247 }
248}
249
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700250} // namespace talsa
251} // namespace implementation
Mikhail Naganov62b7e0e2022-01-27 20:00:41 +0000252} // namespace CPP_VERSION
Roman Kiryanov9e6e2cb2020-05-21 11:37:32 -0700253} // namespace audio
254} // namespace hardware
255} // namespace android