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 <log/log.h>
19 #include "talsa.h"
20 #include "debug.h"
21 
22 namespace android {
23 namespace hardware {
24 namespace audio {
25 namespace V6_0 {
26 namespace implementation {
27 namespace talsa {
28 
29 namespace {
30 
31 struct mixer *gMixer0 = nullptr;
32 int gMixerRefcounter0 = 0;
33 std::mutex gMixerMutex;
34 
mixerSetValueAll(struct mixer_ctl * ctl,int value)35 void mixerSetValueAll(struct mixer_ctl *ctl, int value) {
36     const unsigned int n = mixer_ctl_get_num_values(ctl);
37     for (unsigned int i = 0; i < n; i++) {
38         ::mixer_ctl_set_value(ctl, i, value);
39     }
40 }
41 
mixerSetPercentAll(struct mixer_ctl * ctl,int percent)42 void mixerSetPercentAll(struct mixer_ctl *ctl, int percent) {
43     const unsigned int n = mixer_ctl_get_num_values(ctl);
44     for (unsigned int i = 0; i < n; i++) {
45         ::mixer_ctl_set_percent(ctl, i, percent);
46     }
47 }
48 
mixerGetOrOpenImpl(const unsigned card,struct mixer * & gMixer,int & refcounter)49 struct mixer *mixerGetOrOpenImpl(const unsigned card,
50                                  struct mixer *&gMixer,
51                                  int &refcounter) {
52     if (!gMixer) {
53         struct mixer *mixer = ::mixer_open(card);
54         if (!mixer) {
55             return FAILURE(nullptr);
56         }
57 
58         mixerSetPercentAll(::mixer_get_ctl_by_name(mixer, "Master Playback Volume"), 100);
59         mixerSetPercentAll(::mixer_get_ctl_by_name(mixer, "Capture Volume"), 100);
60 
61         mixerSetValueAll(::mixer_get_ctl_by_name(mixer, "Master Playback Switch"), 1);
62         mixerSetValueAll(::mixer_get_ctl_by_name(mixer, "Capture Switch"), 1);
63 
64         gMixer = mixer;
65     }
66 
67     ++refcounter;
68     return gMixer;
69 }
70 
mixerGetOrOpen(const unsigned card)71 struct mixer *mixerGetOrOpen(const unsigned card) {
72     std::lock_guard<std::mutex> guard(gMixerMutex);
73 
74     switch (card) {
75     case 0:  return mixerGetOrOpenImpl(card, gMixer0, gMixerRefcounter0);
76     default: return FAILURE(nullptr);
77     }
78 }
79 
mixerUnrefImpl(struct mixer * mixer,struct mixer * & gMixer,int & refcounter)80 bool mixerUnrefImpl(struct mixer *mixer, struct mixer *&gMixer, int &refcounter) {
81     if (mixer == gMixer) {
82         if (0 == --refcounter) {
83             ::mixer_close(mixer);
84             gMixer = nullptr;
85         }
86         return true;
87     } else {
88         return false;
89     }
90 }
91 
mixerUnref(struct mixer * mixer)92 bool mixerUnref(struct mixer *mixer) {
93     std::lock_guard<std::mutex> guard(gMixerMutex);
94 
95     return mixerUnrefImpl(mixer, gMixer0, gMixerRefcounter0);
96 }
97 
98 }  // namespace
99 
operator ()(pcm_t * x) const100 void PcmDeleter::operator()(pcm_t *x) const {
101     LOG_ALWAYS_FATAL_IF(::pcm_close(x) != 0);
102 };
103 
pcmOpen(const unsigned int dev,const unsigned int card,const unsigned int nChannels,const size_t sampleRateHz,const size_t frameCount,const bool isOut)104 std::unique_ptr<pcm_t, PcmDeleter> pcmOpen(const unsigned int dev,
105                                            const unsigned int card,
106                                            const unsigned int nChannels,
107                                            const size_t sampleRateHz,
108                                            const size_t frameCount,
109                                            const bool isOut) {
110     struct pcm_config pcm_config;
111     memset(&pcm_config, 0, sizeof(pcm_config));
112 
113     pcm_config.channels = nChannels;
114     pcm_config.rate = sampleRateHz;
115     pcm_config.period_size = frameCount;     // Approx frames between interrupts
116     pcm_config.period_count = 8;             // Approx interrupts per buffer
117     pcm_config.format = PCM_FORMAT_S16_LE;
118     pcm_config.start_threshold = 0;
119     pcm_config.stop_threshold = isOut ? 0 : INT_MAX;
120 
121     PcmPtr pcm =
122         PcmPtr(::pcm_open(dev, card,
123                           (isOut ? PCM_OUT : PCM_IN) | PCM_MONOTONIC,
124                            &pcm_config));
125     if (::pcm_is_ready(pcm.get())) {
126         return pcm;
127     } else {
128         ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu "
129               "frameCount=%zu isOut=%d with %s", __func__, __LINE__,
130               nChannels, sampleRateHz, frameCount, isOut,
131               pcm_get_error(pcm.get()));
132         return FAILURE(nullptr);
133     }
134 }
135 
Mixer(unsigned card)136 Mixer::Mixer(unsigned card): mMixer(mixerGetOrOpen(card)) {}
137 
~Mixer()138 Mixer::~Mixer() {
139     if (mMixer) {
140         LOG_ALWAYS_FATAL_IF(!mixerUnref(mMixer));
141     }
142 }
143 
144 }  // namespace talsa
145 }  // namespace implementation
146 }  // namespace V6_0
147 }  // namespace audio
148 }  // namespace hardware
149 }  // namespace android
150