1/* ALSAMixer.cpp
2 **
3 ** Copyright 2008-2010 Wind River Systems
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18#include <errno.h>
19#include <stdarg.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <dlfcn.h>
25
26#define LOG_TAG "AudioHardwareALSA"
27#include <utils/Log.h>
28#include <utils/String8.h>
29
30#include <cutils/properties.h>
31#include <media/AudioRecord.h>
32#include <hardware_legacy/power.h>
33
34#include "AudioHardwareALSA.h"
35
36#define SND_MIXER_VOL_RANGE_MIN  (0)
37#define SND_MIXER_VOL_RANGE_MAX  (100)
38
39#define ALSA_NAME_MAX 128
40
41#define ALSA_STRCAT(x,y) \
42    if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
43        strcat(x, y);
44
45namespace android
46{
47
48// ----------------------------------------------------------------------------
49
50struct mixer_info_t;
51
52struct alsa_properties_t
53{
54    const AudioSystem::audio_devices device;
55    const char         *propName;
56    const char         *propDefault;
57    mixer_info_t       *mInfo;
58};
59
60#define ALSA_PROP(dev, name, out, in) \
61    {\
62        {dev, "alsa.mixer.playback." name, out, NULL},\
63        {dev, "alsa.mixer.capture." name, in, NULL}\
64    }
65
66static alsa_properties_t
67mixerMasterProp[SND_PCM_STREAM_LAST+1] =
68        ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
69
70static alsa_properties_t
71mixerProp[][SND_PCM_STREAM_LAST+1] = {
72    ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),
73    ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker",  ""),
74    ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),
75    ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),
76    ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),
77    ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)
78};
79
80struct mixer_info_t
81{
82    mixer_info_t() :
83        elem(0),
84        min(SND_MIXER_VOL_RANGE_MIN),
85        max(SND_MIXER_VOL_RANGE_MAX),
86        mute(false)
87    {
88    }
89
90    snd_mixer_elem_t *elem;
91    long              min;
92    long              max;
93    long              volume;
94    bool              mute;
95    char              name[ALSA_NAME_MAX];
96};
97
98static int initMixer (snd_mixer_t **mixer, const char *name)
99{
100    int err;
101
102    if ((err = snd_mixer_open(mixer, 0)) < 0) {
103        ALOGE("Unable to open mixer: %s", snd_strerror(err));
104        return err;
105    }
106
107    if ((err = snd_mixer_attach(*mixer, name)) < 0) {
108        ALOGW("Unable to attach mixer to device %s: %s",
109            name, snd_strerror(err));
110
111        if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
112            ALOGE("Unable to attach mixer to device default: %s",
113                snd_strerror(err));
114
115            snd_mixer_close (*mixer);
116            *mixer = NULL;
117            return err;
118        }
119    }
120
121    if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
122        ALOGE("Unable to register mixer elements: %s", snd_strerror(err));
123        snd_mixer_close (*mixer);
124        *mixer = NULL;
125        return err;
126    }
127
128    // Get the mixer controls from the kernel
129    if ((err = snd_mixer_load(*mixer)) < 0) {
130        ALOGE("Unable to load mixer elements: %s", snd_strerror(err));
131        snd_mixer_close (*mixer);
132        *mixer = NULL;
133        return err;
134    }
135
136    return 0;
137}
138
139typedef int (*hasVolume_t)(snd_mixer_elem_t*);
140
141static const hasVolume_t hasVolume[] = {
142    snd_mixer_selem_has_playback_volume,
143    snd_mixer_selem_has_capture_volume
144};
145
146typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
147
148static const getVolumeRange_t getVolumeRange[] = {
149    snd_mixer_selem_get_playback_volume_range,
150    snd_mixer_selem_get_capture_volume_range
151};
152
153typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
154
155static const setVolume_t setVol[] = {
156    snd_mixer_selem_set_playback_volume_all,
157    snd_mixer_selem_set_capture_volume_all
158};
159
160ALSAMixer::ALSAMixer()
161{
162    int err;
163
164    initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidOut");
165    initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidIn");
166
167    snd_mixer_selem_id_t *sid;
168    snd_mixer_selem_id_alloca(&sid);
169
170    for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
171
172        if (!mMixer[i]) continue;
173
174        mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
175
176        property_get (mixerMasterProp[i].propName,
177                      info->name,
178                      mixerMasterProp[i].propDefault);
179
180        for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
181             elem;
182             elem = snd_mixer_elem_next(elem)) {
183
184            if (!snd_mixer_selem_is_active(elem))
185                continue;
186
187            snd_mixer_selem_get_id(elem, sid);
188
189            // Find PCM playback volume control element.
190            const char *elementName = snd_mixer_selem_id_get_name(sid);
191
192            if (info->elem == NULL &&
193                strcmp(elementName, info->name) == 0 &&
194                hasVolume[i] (elem)) {
195
196                info->elem = elem;
197                getVolumeRange[i] (elem, &info->min, &info->max);
198                info->volume = info->max;
199                setVol[i] (elem, info->volume);
200                if (i == SND_PCM_STREAM_PLAYBACK &&
201                    snd_mixer_selem_has_playback_switch (elem))
202                    snd_mixer_selem_set_playback_switch_all (elem, 1);
203                break;
204            }
205        }
206
207        ALOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
208
209        for (int j = 0; mixerProp[j][i].device; j++) {
210
211            mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
212
213            property_get (mixerProp[j][i].propName,
214                          info->name,
215                          mixerProp[j][i].propDefault);
216
217            for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
218                 elem;
219                 elem = snd_mixer_elem_next(elem)) {
220
221                if (!snd_mixer_selem_is_active(elem))
222                    continue;
223
224                snd_mixer_selem_get_id(elem, sid);
225
226                // Find PCM playback volume control element.
227                const char *elementName = snd_mixer_selem_id_get_name(sid);
228
229               if (info->elem == NULL &&
230                    strcmp(elementName, info->name) == 0 &&
231                    hasVolume[i] (elem)) {
232
233                    info->elem = elem;
234                    getVolumeRange[i] (elem, &info->min, &info->max);
235                    info->volume = info->max;
236                    setVol[i] (elem, info->volume);
237                    if (i == SND_PCM_STREAM_PLAYBACK &&
238                        snd_mixer_selem_has_playback_switch (elem))
239                        snd_mixer_selem_set_playback_switch_all (elem, 1);
240                    break;
241                }
242            }
243            ALOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
244        }
245    }
246    ALOGV("mixer initialized.");
247}
248
249ALSAMixer::~ALSAMixer()
250{
251    for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
252        if (mMixer[i]) snd_mixer_close (mMixer[i]);
253        if (mixerMasterProp[i].mInfo) {
254            delete mixerMasterProp[i].mInfo;
255            mixerMasterProp[i].mInfo = NULL;
256        }
257        for (int j = 0; mixerProp[j][i].device; j++) {
258            if (mixerProp[j][i].mInfo) {
259                delete mixerProp[j][i].mInfo;
260                mixerProp[j][i].mInfo = NULL;
261            }
262        }
263    }
264    ALOGV("mixer destroyed.");
265}
266
267status_t ALSAMixer::setMasterVolume(float volume)
268{
269    mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_PLAYBACK].mInfo;
270    if (!info || !info->elem) return INVALID_OPERATION;
271
272    long minVol = info->min;
273    long maxVol = info->max;
274
275    // Make sure volume is between bounds.
276    long vol = minVol + volume * (maxVol - minVol);
277    if (vol > maxVol) vol = maxVol;
278    if (vol < minVol) vol = minVol;
279
280    info->volume = vol;
281    snd_mixer_selem_set_playback_volume_all (info->elem, vol);
282
283    return NO_ERROR;
284}
285
286status_t ALSAMixer::setMasterGain(float gain)
287{
288    mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_CAPTURE].mInfo;
289    if (!info || !info->elem) return INVALID_OPERATION;
290
291    long minVol = info->min;
292    long maxVol = info->max;
293
294    // Make sure volume is between bounds.
295    long vol = minVol + gain * (maxVol - minVol);
296    if (vol > maxVol) vol = maxVol;
297    if (vol < minVol) vol = minVol;
298
299    info->volume = vol;
300    snd_mixer_selem_set_capture_volume_all (info->elem, vol);
301
302    return NO_ERROR;
303}
304
305status_t ALSAMixer::setVolume(uint32_t device, float left, float right)
306{
307    for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
308        if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
309
310            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
311            if (!info || !info->elem) return INVALID_OPERATION;
312
313            long minVol = info->min;
314            long maxVol = info->max;
315
316            // Make sure volume is between bounds.
317            long vol = minVol + left * (maxVol - minVol);
318            if (vol > maxVol) vol = maxVol;
319            if (vol < minVol) vol = minVol;
320
321            info->volume = vol;
322            snd_mixer_selem_set_playback_volume_all (info->elem, vol);
323        }
324
325    return NO_ERROR;
326}
327
328status_t ALSAMixer::setGain(uint32_t device, float gain)
329{
330    for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
331        if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
332
333            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
334            if (!info || !info->elem) return INVALID_OPERATION;
335
336            long minVol = info->min;
337            long maxVol = info->max;
338
339            // Make sure volume is between bounds.
340            long vol = minVol + gain * (maxVol - minVol);
341            if (vol > maxVol) vol = maxVol;
342            if (vol < minVol) vol = minVol;
343
344            info->volume = vol;
345            snd_mixer_selem_set_capture_volume_all (info->elem, vol);
346        }
347
348    return NO_ERROR;
349}
350
351status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
352{
353    for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
354        if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
355
356            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
357            if (!info || !info->elem) return INVALID_OPERATION;
358
359            if (snd_mixer_selem_has_capture_switch (info->elem)) {
360
361                int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
362                if (err < 0) {
363                    ALOGE("Unable to %s capture mixer switch %s",
364                        state ? "enable" : "disable", info->name);
365                    return INVALID_OPERATION;
366                }
367            }
368
369            info->mute = state;
370        }
371
372    return NO_ERROR;
373}
374
375status_t ALSAMixer::getCaptureMuteState(uint32_t device, bool *state)
376{
377    if (!state) return BAD_VALUE;
378
379    for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
380        if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
381
382            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
383            if (!info || !info->elem) return INVALID_OPERATION;
384
385            *state = info->mute;
386            return NO_ERROR;
387        }
388
389    return BAD_VALUE;
390}
391
392status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
393{
394    for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
395        if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
396
397            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
398            if (!info || !info->elem) return INVALID_OPERATION;
399
400            if (snd_mixer_selem_has_playback_switch (info->elem)) {
401
402                int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
403                if (err < 0) {
404                    ALOGE("Unable to %s playback mixer switch %s",
405                        state ? "enable" : "disable", info->name);
406                    return INVALID_OPERATION;
407                }
408            }
409
410            info->mute = state;
411        }
412
413    return NO_ERROR;
414}
415
416status_t ALSAMixer::getPlaybackMuteState(uint32_t device, bool *state)
417{
418    if (!state) return BAD_VALUE;
419
420    for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
421        if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
422
423            mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
424            if (!info || !info->elem) return INVALID_OPERATION;
425
426            *state = info->mute;
427            return NO_ERROR;
428        }
429
430    return BAD_VALUE;
431}
432
433};        // namespace android
434