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
45 namespace android
46 {
47
48 // ----------------------------------------------------------------------------
49
50 struct mixer_info_t;
51
52 struct 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
66 static alsa_properties_t
67 mixerMasterProp[SND_PCM_STREAM_LAST+1] =
68 ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
69
70 static alsa_properties_t
71 mixerProp[][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
80 struct mixer_info_t
81 {
mixer_info_tandroid::mixer_info_t82 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
initMixer(snd_mixer_t ** mixer,const char * name)98 static 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
139 typedef int (*hasVolume_t)(snd_mixer_elem_t*);
140
141 static const hasVolume_t hasVolume[] = {
142 snd_mixer_selem_has_playback_volume,
143 snd_mixer_selem_has_capture_volume
144 };
145
146 typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
147
148 static const getVolumeRange_t getVolumeRange[] = {
149 snd_mixer_selem_get_playback_volume_range,
150 snd_mixer_selem_get_capture_volume_range
151 };
152
153 typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
154
155 static const setVolume_t setVol[] = {
156 snd_mixer_selem_set_playback_volume_all,
157 snd_mixer_selem_set_capture_volume_all
158 };
159
ALSAMixer()160 ALSAMixer::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
~ALSAMixer()249 ALSAMixer::~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
setMasterVolume(float volume)267 status_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
setMasterGain(float gain)286 status_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
setVolume(uint32_t device,float left,float right)305 status_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
setGain(uint32_t device,float gain)328 status_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
setCaptureMuteState(uint32_t device,bool state)351 status_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
getCaptureMuteState(uint32_t device,bool * state)375 status_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
setPlaybackMuteState(uint32_t device,bool state)392 status_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
getPlaybackMuteState(uint32_t device,bool * state)416 status_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