hardware/libaudio
Revisión | 2d37c6cf645f282661995c21106920ef587b1034 (tree) |
---|---|
Tiempo | 2012-01-23 04:20:37 |
Autor | Chih-Wei Huang <cwhuang@linu...> |
Commiter | Chih-Wei Huang |
audio hal for ics-x86
Modified from Linaro project.
@@ -11,3 +11,35 @@ | ||
11 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
14 | + | |
15 | +ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true) | |
16 | + | |
17 | +LOCAL_PATH := $(call my-dir) | |
18 | + | |
19 | +include $(CLEAR_VARS) | |
20 | + | |
21 | +LOCAL_MODULE := audio.primary.default | |
22 | +LOCAL_MODULE_TAGS := optional | |
23 | +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | |
24 | + | |
25 | +LOCAL_SHARED_LIBRARIES := \ | |
26 | + libdl \ | |
27 | + libmedia \ | |
28 | + libutils \ | |
29 | + libcutils \ | |
30 | + libhardware_legacy | |
31 | + | |
32 | +LOCAL_SRC_FILES := \ | |
33 | + alsa_pcm.c \ | |
34 | + alsa_mixer.c \ | |
35 | + AudioHardware.cpp | |
36 | + | |
37 | +LOCAL_STATIC_LIBRARIES := \ | |
38 | + libmedia_helper | |
39 | + | |
40 | +LOCAL_WHOLE_STATIC_LIBRARIES := \ | |
41 | + libaudiohw_legacy | |
42 | + | |
43 | +include $(BUILD_SHARED_LIBRARY) | |
44 | + | |
45 | +endif |
@@ -0,0 +1,1811 @@ | ||
1 | +/* | |
2 | +** Copyright 2011, 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 <math.h> | |
18 | + | |
19 | +//#define LOG_NDEBUG 0 | |
20 | + | |
21 | +#define LOG_TAG "AudioHardware" | |
22 | + | |
23 | +#include <utils/Log.h> | |
24 | +#include <utils/String8.h> | |
25 | + | |
26 | +#include <stdio.h> | |
27 | +#include <unistd.h> | |
28 | +#include <sys/ioctl.h> | |
29 | +#include <sys/types.h> | |
30 | +#include <sys/stat.h> | |
31 | +#include <sys/resource.h> | |
32 | +#include <dlfcn.h> | |
33 | +#include <fcntl.h> | |
34 | + | |
35 | +#include "AudioHardware.h" | |
36 | +#include <media/AudioRecord.h> | |
37 | +#include <hardware_legacy/power.h> | |
38 | + | |
39 | +#include "alsa_audio.h" | |
40 | + | |
41 | + | |
42 | +namespace android_audio_legacy { | |
43 | + | |
44 | +const uint32_t AudioHardware::inputSamplingRates[] = { | |
45 | + 8000, 11025, 16000, 22050, 44100 | |
46 | +}; | |
47 | + | |
48 | +// trace driver operations for dump | |
49 | +// | |
50 | +#define DRIVER_TRACE | |
51 | + | |
52 | +enum { | |
53 | + DRV_NONE, | |
54 | + DRV_PCM_OPEN, | |
55 | + DRV_PCM_CLOSE, | |
56 | + DRV_PCM_WRITE, | |
57 | + DRV_PCM_READ, | |
58 | + DRV_MIXER_OPEN, | |
59 | + DRV_MIXER_CLOSE, | |
60 | + DRV_MIXER_GET, | |
61 | + DRV_MIXER_SEL | |
62 | +}; | |
63 | + | |
64 | +#ifdef DRIVER_TRACE | |
65 | +#define TRACE_DRIVER_IN(op) mDriverOp = op; | |
66 | +#define TRACE_DRIVER_OUT mDriverOp = DRV_NONE; | |
67 | +#else | |
68 | +#define TRACE_DRIVER_IN(op) | |
69 | +#define TRACE_DRIVER_OUT | |
70 | +#endif | |
71 | + | |
72 | +// ---------------------------------------------------------------------------- | |
73 | + | |
74 | +AudioHardware::AudioHardware() : | |
75 | + mInit(false), | |
76 | + mMicMute(false), | |
77 | + mPcm(NULL), | |
78 | + mMixer(NULL), | |
79 | + mPcmOpenCnt(0), | |
80 | + mMixerOpenCnt(0), | |
81 | + mInCallAudioMode(false), | |
82 | + mInputSource("Default"), | |
83 | + mBluetoothNrec(true), | |
84 | + mActivatedCP(false), | |
85 | + mDriverOp(DRV_NONE) | |
86 | +{ | |
87 | + mInit = true; | |
88 | +} | |
89 | + | |
90 | +AudioHardware::~AudioHardware() | |
91 | +{ | |
92 | + for (size_t index = 0; index < mInputs.size(); index++) { | |
93 | + closeInputStream(mInputs[index].get()); | |
94 | + } | |
95 | + mInputs.clear(); | |
96 | + closeOutputStream((AudioStreamOut*)mOutput.get()); | |
97 | + | |
98 | + if (mMixer) { | |
99 | + TRACE_DRIVER_IN(DRV_MIXER_CLOSE) | |
100 | + mixer_close(mMixer); | |
101 | + TRACE_DRIVER_OUT | |
102 | + } | |
103 | + if (mPcm) { | |
104 | + TRACE_DRIVER_IN(DRV_PCM_CLOSE) | |
105 | + pcm_close(mPcm); | |
106 | + TRACE_DRIVER_OUT | |
107 | + } | |
108 | + | |
109 | + mInit = false; | |
110 | +} | |
111 | + | |
112 | +status_t AudioHardware::initCheck() | |
113 | +{ | |
114 | + return mInit ? NO_ERROR : NO_INIT; | |
115 | +} | |
116 | + | |
117 | +AudioStreamOut* AudioHardware::openOutputStream( | |
118 | + uint32_t devices, int *format, uint32_t *channels, | |
119 | + uint32_t *sampleRate, status_t *status) | |
120 | +{ | |
121 | + sp <AudioStreamOutALSA> out; | |
122 | + status_t rc; | |
123 | + | |
124 | + { // scope for the lock | |
125 | + Mutex::Autolock lock(mLock); | |
126 | + | |
127 | + // only one output stream allowed | |
128 | + if (mOutput != 0) { | |
129 | + if (status) { | |
130 | + *status = INVALID_OPERATION; | |
131 | + } | |
132 | + return NULL; | |
133 | + } | |
134 | + | |
135 | + out = new AudioStreamOutALSA(); | |
136 | + | |
137 | + rc = out->set(this, devices, format, channels, sampleRate); | |
138 | + if (rc == NO_ERROR) { | |
139 | + mOutput = out; | |
140 | + } | |
141 | + } | |
142 | + | |
143 | + if (rc != NO_ERROR) { | |
144 | + if (out != 0) { | |
145 | + out.clear(); | |
146 | + } | |
147 | + } | |
148 | + if (status) { | |
149 | + *status = rc; | |
150 | + } | |
151 | + | |
152 | + return out.get(); | |
153 | +} | |
154 | + | |
155 | +void AudioHardware::closeOutputStream(AudioStreamOut* out) { | |
156 | + sp <AudioStreamOutALSA> spOut; | |
157 | + { | |
158 | + Mutex::Autolock lock(mLock); | |
159 | + if (mOutput == 0 || mOutput.get() != out) { | |
160 | + LOGW("Attempt to close invalid output stream"); | |
161 | + return; | |
162 | + } | |
163 | + spOut = mOutput; | |
164 | + mOutput.clear(); | |
165 | + } | |
166 | + spOut.clear(); | |
167 | +} | |
168 | + | |
169 | +AudioStreamIn* AudioHardware::openInputStream( | |
170 | + uint32_t devices, int *format, uint32_t *channels, | |
171 | + uint32_t *sampleRate, status_t *status, | |
172 | + AudioSystem::audio_in_acoustics acoustic_flags) | |
173 | +{ | |
174 | + // check for valid input source | |
175 | + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { | |
176 | + if (status) { | |
177 | + *status = BAD_VALUE; | |
178 | + } | |
179 | + return NULL; | |
180 | + } | |
181 | + | |
182 | + status_t rc = NO_ERROR; | |
183 | + sp <AudioStreamInALSA> in; | |
184 | + | |
185 | + { // scope for the lock | |
186 | + Mutex::Autolock lock(mLock); | |
187 | + | |
188 | + in = new AudioStreamInALSA(); | |
189 | + rc = in->set(this, devices, format, channels, sampleRate, acoustic_flags); | |
190 | + if (rc == NO_ERROR) { | |
191 | + mInputs.add(in); | |
192 | + } | |
193 | + } | |
194 | + | |
195 | + if (rc != NO_ERROR) { | |
196 | + if (in != 0) { | |
197 | + in.clear(); | |
198 | + } | |
199 | + } | |
200 | + if (status) { | |
201 | + *status = rc; | |
202 | + } | |
203 | + | |
204 | + LOGV("AudioHardware::openInputStream()%p", in.get()); | |
205 | + return in.get(); | |
206 | +} | |
207 | + | |
208 | +void AudioHardware::closeInputStream(AudioStreamIn* in) { | |
209 | + | |
210 | + sp<AudioStreamInALSA> spIn; | |
211 | + { | |
212 | + Mutex::Autolock lock(mLock); | |
213 | + | |
214 | + ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in); | |
215 | + if (index < 0) { | |
216 | + LOGW("Attempt to close invalid input stream"); | |
217 | + return; | |
218 | + } | |
219 | + spIn = mInputs[index]; | |
220 | + mInputs.removeAt(index); | |
221 | + } | |
222 | + LOGV("AudioHardware::closeInputStream()%p", in); | |
223 | + spIn.clear(); | |
224 | +} | |
225 | + | |
226 | + | |
227 | +status_t AudioHardware::setMode(int mode) | |
228 | +{ | |
229 | + sp<AudioStreamOutALSA> spOut; | |
230 | + sp<AudioStreamInALSA> spIn; | |
231 | + status_t status; | |
232 | + | |
233 | + // bump thread priority to speed up mutex acquisition | |
234 | + int priority = getpriority(PRIO_PROCESS, 0); | |
235 | + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_URGENT_AUDIO); | |
236 | + | |
237 | + // Mutex acquisition order is always out -> in -> hw | |
238 | + AutoMutex lock(mLock); | |
239 | + | |
240 | + spOut = mOutput; | |
241 | + while (spOut != 0) { | |
242 | + if (!spOut->checkStandby()) { | |
243 | + int cnt = spOut->standbyCnt(); | |
244 | + mLock.unlock(); | |
245 | + spOut->lock(); | |
246 | + mLock.lock(); | |
247 | + // make sure that another thread did not change output state while the | |
248 | + // mutex is released | |
249 | + if ((spOut == mOutput) && (cnt == spOut->standbyCnt())) { | |
250 | + break; | |
251 | + } | |
252 | + spOut->unlock(); | |
253 | + spOut = mOutput; | |
254 | + } else { | |
255 | + spOut.clear(); | |
256 | + } | |
257 | + } | |
258 | + // spOut is not 0 here only if the output is active | |
259 | + | |
260 | + spIn = getActiveInput_l(); | |
261 | + while (spIn != 0) { | |
262 | + int cnt = spIn->standbyCnt(); | |
263 | + mLock.unlock(); | |
264 | + spIn->lock(); | |
265 | + mLock.lock(); | |
266 | + // make sure that another thread did not change input state while the | |
267 | + // mutex is released | |
268 | + if ((spIn == getActiveInput_l()) && (cnt == spIn->standbyCnt())) { | |
269 | + break; | |
270 | + } | |
271 | + spIn->unlock(); | |
272 | + spIn = getActiveInput_l(); | |
273 | + } | |
274 | + // spIn is not 0 here only if the input is active | |
275 | + | |
276 | + setpriority(PRIO_PROCESS, 0, priority); | |
277 | + | |
278 | + int prevMode = mMode; | |
279 | + status = AudioHardwareBase::setMode(mode); | |
280 | + LOGV("setMode() : new %d, old %d", mMode, prevMode); | |
281 | + if (status == NO_ERROR) { | |
282 | + if (mMode == AudioSystem::MODE_IN_CALL && !mInCallAudioMode) { | |
283 | + if (spOut != 0) { | |
284 | + LOGV("setMode() in call force output standby"); | |
285 | + spOut->doStandby_l(); | |
286 | + } | |
287 | + if (spIn != 0) { | |
288 | + LOGV("setMode() in call force input standby"); | |
289 | + spIn->doStandby_l(); | |
290 | + } | |
291 | + | |
292 | + LOGV("setMode() openPcmOut_l()"); | |
293 | + openPcmOut_l(); | |
294 | + openMixer_l(); | |
295 | + setInputSource_l(String8("Default")); | |
296 | + mInCallAudioMode = true; | |
297 | + } | |
298 | + if (mMode == AudioSystem::MODE_NORMAL && mInCallAudioMode) { | |
299 | + setInputSource_l(mInputSource); | |
300 | + if (mMixer != NULL) { | |
301 | + TRACE_DRIVER_IN(DRV_MIXER_GET) | |
302 | + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Playback Path", 0); | |
303 | + TRACE_DRIVER_OUT | |
304 | + if (ctl != NULL) { | |
305 | + LOGV("setMode() reset Playback Path to RCV"); | |
306 | + TRACE_DRIVER_IN(DRV_MIXER_SEL) | |
307 | + mixer_ctl_select(ctl, "RCV"); | |
308 | + TRACE_DRIVER_OUT | |
309 | + } | |
310 | + } | |
311 | + LOGV("setMode() closePcmOut_l()"); | |
312 | + closeMixer_l(); | |
313 | + closePcmOut_l(); | |
314 | + | |
315 | + if (spOut != 0) { | |
316 | + LOGV("setMode() off call force output standby"); | |
317 | + spOut->doStandby_l(); | |
318 | + } | |
319 | + if (spIn != 0) { | |
320 | + LOGV("setMode() off call force input standby"); | |
321 | + spIn->doStandby_l(); | |
322 | + } | |
323 | + | |
324 | + mInCallAudioMode = false; | |
325 | + } | |
326 | + | |
327 | + if (mMode == AudioSystem::MODE_NORMAL) { | |
328 | + if(mActivatedCP) | |
329 | + mActivatedCP = false; | |
330 | + } | |
331 | + } | |
332 | + | |
333 | + if (spIn != 0) { | |
334 | + spIn->unlock(); | |
335 | + } | |
336 | + if (spOut != 0) { | |
337 | + spOut->unlock(); | |
338 | + } | |
339 | + | |
340 | + return status; | |
341 | +} | |
342 | + | |
343 | +status_t AudioHardware::setMicMute(bool state) | |
344 | +{ | |
345 | + LOGV("setMicMute(%d) mMicMute %d", state, mMicMute); | |
346 | + sp<AudioStreamInALSA> spIn; | |
347 | + { | |
348 | + AutoMutex lock(mLock); | |
349 | + if (mMicMute != state) { | |
350 | + mMicMute = state; | |
351 | + if (mMode != AudioSystem::MODE_IN_CALL) { | |
352 | + spIn = getActiveInput_l(); | |
353 | + } | |
354 | + } | |
355 | + } | |
356 | + | |
357 | + if (spIn != 0) { | |
358 | + spIn->standby(); | |
359 | + } | |
360 | + | |
361 | + return NO_ERROR; | |
362 | +} | |
363 | + | |
364 | +status_t AudioHardware::getMicMute(bool* state) | |
365 | +{ | |
366 | + *state = mMicMute; | |
367 | + return NO_ERROR; | |
368 | +} | |
369 | + | |
370 | +status_t AudioHardware::setParameters(const String8& keyValuePairs) | |
371 | +{ | |
372 | + AudioParameter param = AudioParameter(keyValuePairs); | |
373 | + String8 value; | |
374 | + String8 key; | |
375 | + const char BT_NREC_KEY[] = "bt_headset_nrec"; | |
376 | + const char BT_NREC_VALUE_ON[] = "on"; | |
377 | + | |
378 | + key = String8(BT_NREC_KEY); | |
379 | + if (param.get(key, value) == NO_ERROR) { | |
380 | + if (value == BT_NREC_VALUE_ON) { | |
381 | + mBluetoothNrec = true; | |
382 | + } else { | |
383 | + mBluetoothNrec = false; | |
384 | + LOGD("Turning noise reduction and echo cancellation off for BT " | |
385 | + "headset"); | |
386 | + } | |
387 | + } | |
388 | + | |
389 | + return NO_ERROR; | |
390 | +} | |
391 | + | |
392 | +String8 AudioHardware::getParameters(const String8& keys) | |
393 | +{ | |
394 | + AudioParameter request = AudioParameter(keys); | |
395 | + AudioParameter reply = AudioParameter(); | |
396 | + | |
397 | + LOGV("getParameters() %s", keys.string()); | |
398 | + | |
399 | + return reply.toString(); | |
400 | +} | |
401 | + | |
402 | +size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) | |
403 | +{ | |
404 | + if (format != AudioSystem::PCM_16_BIT) { | |
405 | + LOGW("getInputBufferSize bad format: %d", format); | |
406 | + return 0; | |
407 | + } | |
408 | + if (channelCount < 1 || channelCount > 2) { | |
409 | + LOGW("getInputBufferSize bad channel count: %d", channelCount); | |
410 | + return 0; | |
411 | + } | |
412 | + if (sampleRate != 8000 && sampleRate != 11025 && sampleRate != 16000 && | |
413 | + sampleRate != 22050 && sampleRate != 44100) { | |
414 | + LOGW("getInputBufferSize bad sample rate: %d", sampleRate); | |
415 | + return 0; | |
416 | + } | |
417 | + | |
418 | + return AudioStreamInALSA::getBufferSize(sampleRate, channelCount); | |
419 | +} | |
420 | + | |
421 | + | |
422 | +status_t AudioHardware::setVoiceVolume(float volume) | |
423 | +{ | |
424 | + LOGD("### setVoiceVolume: %f", volume); | |
425 | + return NO_ERROR; | |
426 | +} | |
427 | + | |
428 | +status_t AudioHardware::setMasterVolume(float volume) | |
429 | +{ | |
430 | + LOGV("Set master volume to %f.\n", volume); | |
431 | + // We return an error code here to let the audioflinger do in-software | |
432 | + // volume on top of the maximum volume that we set through the SND API. | |
433 | + // return error - software mixer will handle it | |
434 | + return -1; | |
435 | +} | |
436 | + | |
437 | +static const int kDumpLockRetries = 50; | |
438 | +static const int kDumpLockSleep = 20000; | |
439 | + | |
440 | +static bool tryLock(Mutex& mutex) | |
441 | +{ | |
442 | + bool locked = false; | |
443 | + for (int i = 0; i < kDumpLockRetries; ++i) { | |
444 | + if (mutex.tryLock() == NO_ERROR) { | |
445 | + locked = true; | |
446 | + break; | |
447 | + } | |
448 | + usleep(kDumpLockSleep); | |
449 | + } | |
450 | + return locked; | |
451 | +} | |
452 | + | |
453 | +status_t AudioHardware::dump(int fd, const Vector<String16>& args) | |
454 | +{ | |
455 | + const size_t SIZE = 256; | |
456 | + char buffer[SIZE]; | |
457 | + String8 result; | |
458 | + | |
459 | + bool locked = tryLock(mLock); | |
460 | + if (!locked) { | |
461 | + snprintf(buffer, SIZE, "\n\tAudioHardware maybe deadlocked\n"); | |
462 | + } else { | |
463 | + mLock.unlock(); | |
464 | + } | |
465 | + | |
466 | + snprintf(buffer, SIZE, "\tInit %s\n", (mInit) ? "OK" : "Failed"); | |
467 | + result.append(buffer); | |
468 | + snprintf(buffer, SIZE, "\tMic Mute %s\n", (mMicMute) ? "ON" : "OFF"); | |
469 | + result.append(buffer); | |
470 | + snprintf(buffer, SIZE, "\tmPcm: %p\n", mPcm); | |
471 | + result.append(buffer); | |
472 | + snprintf(buffer, SIZE, "\tmPcmOpenCnt: %d\n", mPcmOpenCnt); | |
473 | + result.append(buffer); | |
474 | + snprintf(buffer, SIZE, "\tmMixer: %p\n", mMixer); | |
475 | + result.append(buffer); | |
476 | + snprintf(buffer, SIZE, "\tmMixerOpenCnt: %d\n", mMixerOpenCnt); | |
477 | + result.append(buffer); | |
478 | + snprintf(buffer, SIZE, "\tIn Call Audio Mode %s\n", | |
479 | + (mInCallAudioMode) ? "ON" : "OFF"); | |
480 | + result.append(buffer); | |
481 | + snprintf(buffer, SIZE, "\tInput source %s\n", mInputSource.string()); | |
482 | + result.append(buffer); | |
483 | + snprintf(buffer, SIZE, "\tCP %s\n", | |
484 | + (mActivatedCP) ? "Activated" : "Deactivated"); | |
485 | + result.append(buffer); | |
486 | + snprintf(buffer, SIZE, "\tmDriverOp: %d\n", mDriverOp); | |
487 | + result.append(buffer); | |
488 | + | |
489 | + snprintf(buffer, SIZE, "\n\tmOutput %p dump:\n", mOutput.get()); | |
490 | + result.append(buffer); | |
491 | + write(fd, result.string(), result.size()); | |
492 | + if (mOutput != 0) { | |
493 | + mOutput->dump(fd, args); | |
494 | + } | |
495 | + | |
496 | + snprintf(buffer, SIZE, "\n\t%d inputs opened:\n", mInputs.size()); | |
497 | + write(fd, buffer, strlen(buffer)); | |
498 | + for (size_t i = 0; i < mInputs.size(); i++) { | |
499 | + snprintf(buffer, SIZE, "\t- input %d dump:\n", i); | |
500 | + write(fd, buffer, strlen(buffer)); | |
501 | + mInputs[i]->dump(fd, args); | |
502 | + } | |
503 | + | |
504 | + return NO_ERROR; | |
505 | +} | |
506 | + | |
507 | +status_t AudioHardware::setIncallPath_l(uint32_t device) | |
508 | +{ | |
509 | + LOGV("setIncallPath_l: device %x", device); | |
510 | + return NO_ERROR; | |
511 | +} | |
512 | + | |
513 | +struct pcm *AudioHardware::openPcmOut_l() | |
514 | +{ | |
515 | + LOGD("openPcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); | |
516 | + if (mPcmOpenCnt++ == 0) { | |
517 | + if (mPcm != NULL) { | |
518 | + LOGE("openPcmOut_l() mPcmOpenCnt == 0 and mPcm == %p\n", mPcm); | |
519 | + mPcmOpenCnt--; | |
520 | + return NULL; | |
521 | + } | |
522 | + unsigned flags = PCM_OUT; | |
523 | + | |
524 | + flags |= (AUDIO_HW_OUT_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT; | |
525 | + flags |= (AUDIO_HW_OUT_PERIOD_CNT - PCM_PERIOD_CNT_MIN) << PCM_PERIOD_CNT_SHIFT; | |
526 | + | |
527 | + TRACE_DRIVER_IN(DRV_PCM_OPEN) | |
528 | + mPcm = pcm_open(flags); | |
529 | + TRACE_DRIVER_OUT | |
530 | + if (!pcm_ready(mPcm)) { | |
531 | + LOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_error(mPcm)); | |
532 | + TRACE_DRIVER_IN(DRV_PCM_CLOSE) | |
533 | + pcm_close(mPcm); | |
534 | + TRACE_DRIVER_OUT | |
535 | + mPcmOpenCnt--; | |
536 | + mPcm = NULL; | |
537 | + } | |
538 | + } | |
539 | + return mPcm; | |
540 | +} | |
541 | + | |
542 | +void AudioHardware::closePcmOut_l() | |
543 | +{ | |
544 | + LOGD("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); | |
545 | + if (mPcmOpenCnt == 0) { | |
546 | + LOGE("closePcmOut_l() mPcmOpenCnt == 0"); | |
547 | + return; | |
548 | + } | |
549 | + | |
550 | + if (--mPcmOpenCnt == 0) { | |
551 | + TRACE_DRIVER_IN(DRV_PCM_CLOSE) | |
552 | + pcm_close(mPcm); | |
553 | + TRACE_DRIVER_OUT | |
554 | + mPcm = NULL; | |
555 | + } | |
556 | +} | |
557 | + | |
558 | +struct mixer *AudioHardware::openMixer_l() | |
559 | +{ | |
560 | + LOGV("openMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); | |
561 | + if (mMixerOpenCnt++ == 0) { | |
562 | + if (mMixer != NULL) { | |
563 | + LOGE("openMixer_l() mMixerOpenCnt == 0 and mMixer == %p\n", mMixer); | |
564 | + mMixerOpenCnt--; | |
565 | + return NULL; | |
566 | + } | |
567 | + TRACE_DRIVER_IN(DRV_MIXER_OPEN) | |
568 | + mMixer = mixer_open(); | |
569 | + TRACE_DRIVER_OUT | |
570 | + if (mMixer == NULL) { | |
571 | + LOGE("openMixer_l() cannot open mixer"); | |
572 | + mMixerOpenCnt--; | |
573 | + return NULL; | |
574 | + } | |
575 | + } | |
576 | + return mMixer; | |
577 | +} | |
578 | + | |
579 | +void AudioHardware::closeMixer_l() | |
580 | +{ | |
581 | + LOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); | |
582 | + if (mMixerOpenCnt == 0) { | |
583 | + LOGE("closeMixer_l() mMixerOpenCnt == 0"); | |
584 | + return; | |
585 | + } | |
586 | + | |
587 | + if (--mMixerOpenCnt == 0) { | |
588 | + TRACE_DRIVER_IN(DRV_MIXER_CLOSE) | |
589 | + mixer_close(mMixer); | |
590 | + TRACE_DRIVER_OUT | |
591 | + mMixer = NULL; | |
592 | + } | |
593 | +} | |
594 | + | |
595 | +const char *AudioHardware::getOutputRouteFromDevice(uint32_t device) | |
596 | +{ | |
597 | + switch (device) { | |
598 | + case AudioSystem::DEVICE_OUT_EARPIECE: | |
599 | + return "RCV"; | |
600 | + case AudioSystem::DEVICE_OUT_SPEAKER: | |
601 | + if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK"; | |
602 | + else return "SPK"; | |
603 | + case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: | |
604 | + if (mMode == AudioSystem::MODE_RINGTONE) return "RING_NO_MIC"; | |
605 | + else return "HP_NO_MIC"; | |
606 | + case AudioSystem::DEVICE_OUT_WIRED_HEADSET: | |
607 | + if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP"; | |
608 | + else return "HP"; | |
609 | + case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADPHONE): | |
610 | + case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADSET): | |
611 | + if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK_HP"; | |
612 | + else return "SPK_HP"; | |
613 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: | |
614 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: | |
615 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: | |
616 | + return "BT"; | |
617 | + default: | |
618 | + return "OFF"; | |
619 | + } | |
620 | +} | |
621 | + | |
622 | +const char *AudioHardware::getVoiceRouteFromDevice(uint32_t device) | |
623 | +{ | |
624 | + switch (device) { | |
625 | + case AudioSystem::DEVICE_OUT_EARPIECE: | |
626 | + return "RCV"; | |
627 | + case AudioSystem::DEVICE_OUT_SPEAKER: | |
628 | + return "SPK"; | |
629 | + case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: | |
630 | + return "HP_NO_MIC"; | |
631 | + case AudioSystem::DEVICE_OUT_WIRED_HEADSET: | |
632 | + return "HP"; | |
633 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: | |
634 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: | |
635 | + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: | |
636 | + return "BT"; | |
637 | + default: | |
638 | + return "OFF"; | |
639 | + } | |
640 | +} | |
641 | + | |
642 | +const char *AudioHardware::getInputRouteFromDevice(uint32_t device) | |
643 | +{ | |
644 | + if (mMicMute) { | |
645 | + return "MIC OFF"; | |
646 | + } | |
647 | + | |
648 | + switch (device) { | |
649 | + case AudioSystem::DEVICE_IN_BUILTIN_MIC: | |
650 | + return "Main Mic"; | |
651 | + case AudioSystem::DEVICE_IN_WIRED_HEADSET: | |
652 | + return "Hands Free Mic"; | |
653 | + case AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET: | |
654 | + return "BT Sco Mic"; | |
655 | + default: | |
656 | + return "MIC OFF"; | |
657 | + } | |
658 | +} | |
659 | + | |
660 | +uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate) | |
661 | +{ | |
662 | + uint32_t i; | |
663 | + uint32_t prevDelta; | |
664 | + uint32_t delta; | |
665 | + | |
666 | + for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) { | |
667 | + delta = abs(sampleRate - inputSamplingRates[i]); | |
668 | + if (delta > prevDelta) break; | |
669 | + } | |
670 | + // i is always > 0 here | |
671 | + return inputSamplingRates[i-1]; | |
672 | +} | |
673 | + | |
674 | +// getActiveInput_l() must be called with mLock held | |
675 | +sp <AudioHardware::AudioStreamInALSA> AudioHardware::getActiveInput_l() | |
676 | +{ | |
677 | + sp< AudioHardware::AudioStreamInALSA> spIn; | |
678 | + | |
679 | + for (size_t i = 0; i < mInputs.size(); i++) { | |
680 | + // return first input found not being in standby mode | |
681 | + // as only one input can be in this state | |
682 | + if (!mInputs[i]->checkStandby()) { | |
683 | + spIn = mInputs[i]; | |
684 | + break; | |
685 | + } | |
686 | + } | |
687 | + | |
688 | + return spIn; | |
689 | +} | |
690 | + | |
691 | +status_t AudioHardware::setInputSource_l(String8 source) | |
692 | +{ | |
693 | + LOGV("setInputSource_l(%s)", source.string()); | |
694 | + if (source != mInputSource) { | |
695 | + if ((source == "Default") || (mMode != AudioSystem::MODE_IN_CALL)) { | |
696 | + if (mMixer) { | |
697 | + TRACE_DRIVER_IN(DRV_MIXER_GET) | |
698 | + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Input Source", 0); | |
699 | + TRACE_DRIVER_OUT | |
700 | + if (ctl == NULL) { | |
701 | + return NO_INIT; | |
702 | + } | |
703 | + LOGV("mixer_ctl_select, Input Source, (%s)", source.string()); | |
704 | + TRACE_DRIVER_IN(DRV_MIXER_SEL) | |
705 | + mixer_ctl_select(ctl, source.string()); | |
706 | + TRACE_DRIVER_OUT | |
707 | + } | |
708 | + } | |
709 | + mInputSource = source; | |
710 | + } | |
711 | + | |
712 | + return NO_ERROR; | |
713 | +} | |
714 | + | |
715 | + | |
716 | +//------------------------------------------------------------------------------ | |
717 | +// AudioStreamOutALSA | |
718 | +//------------------------------------------------------------------------------ | |
719 | + | |
720 | +AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() : | |
721 | + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), | |
722 | + mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), | |
723 | + mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES), | |
724 | + mDriverOp(DRV_NONE), mStandbyCnt(0) | |
725 | +{ | |
726 | +} | |
727 | + | |
728 | +status_t AudioHardware::AudioStreamOutALSA::set( | |
729 | + AudioHardware* hw, uint32_t devices, int *pFormat, | |
730 | + uint32_t *pChannels, uint32_t *pRate) | |
731 | +{ | |
732 | + int lFormat = pFormat ? *pFormat : 0; | |
733 | + uint32_t lChannels = pChannels ? *pChannels : 0; | |
734 | + uint32_t lRate = pRate ? *pRate : 0; | |
735 | + | |
736 | + mHardware = hw; | |
737 | + mDevices = devices; | |
738 | + | |
739 | + // fix up defaults | |
740 | + if (lFormat == 0) lFormat = format(); | |
741 | + if (lChannels == 0) lChannels = channels(); | |
742 | + if (lRate == 0) lRate = sampleRate(); | |
743 | + | |
744 | + // check values | |
745 | + if ((lFormat != format()) || | |
746 | + (lChannels != channels()) || | |
747 | + (lRate != sampleRate())) { | |
748 | + if (pFormat) *pFormat = format(); | |
749 | + if (pChannels) *pChannels = channels(); | |
750 | + if (pRate) *pRate = sampleRate(); | |
751 | + return BAD_VALUE; | |
752 | + } | |
753 | + | |
754 | + if (pFormat) *pFormat = lFormat; | |
755 | + if (pChannels) *pChannels = lChannels; | |
756 | + if (pRate) *pRate = lRate; | |
757 | + | |
758 | + mChannels = lChannels; | |
759 | + mSampleRate = lRate; | |
760 | + mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES; | |
761 | + | |
762 | + return NO_ERROR; | |
763 | +} | |
764 | + | |
765 | +AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA() | |
766 | +{ | |
767 | + standby(); | |
768 | +} | |
769 | + | |
770 | +ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes) | |
771 | +{ | |
772 | + // LOGV("AudioStreamOutALSA::write(%p, %u)", buffer, bytes); | |
773 | + status_t status = NO_INIT; | |
774 | + const uint8_t* p = static_cast<const uint8_t*>(buffer); | |
775 | + int ret; | |
776 | + | |
777 | + if (mHardware == NULL) return NO_INIT; | |
778 | + | |
779 | + { // scope for the lock | |
780 | + | |
781 | + AutoMutex lock(mLock); | |
782 | + | |
783 | + if (mStandby) { | |
784 | + AutoMutex hwLock(mHardware->lock()); | |
785 | + | |
786 | + LOGD("AudioHardware pcm playback is exiting standby."); | |
787 | + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock"); | |
788 | + | |
789 | + sp<AudioStreamInALSA> spIn = mHardware->getActiveInput_l(); | |
790 | + while (spIn != 0) { | |
791 | + int cnt = spIn->standbyCnt(); | |
792 | + mHardware->lock().unlock(); | |
793 | + // Mutex acquisition order is always out -> in -> hw | |
794 | + spIn->lock(); | |
795 | + mHardware->lock().lock(); | |
796 | + // make sure that another thread did not change input state | |
797 | + // while the mutex is released | |
798 | + if ((spIn == mHardware->getActiveInput_l()) && | |
799 | + (cnt == spIn->standbyCnt())) { | |
800 | + LOGV("AudioStreamOutALSA::write() force input standby"); | |
801 | + spIn->close_l(); | |
802 | + break; | |
803 | + } | |
804 | + spIn->unlock(); | |
805 | + spIn = mHardware->getActiveInput_l(); | |
806 | + } | |
807 | + // spIn is not 0 here only if the input was active and has been | |
808 | + // closed above | |
809 | + | |
810 | + // open output before input | |
811 | + open_l(); | |
812 | + | |
813 | + if (spIn != 0) { | |
814 | + if (spIn->open_l() != NO_ERROR) { | |
815 | + spIn->doStandby_l(); | |
816 | + } | |
817 | + spIn->unlock(); | |
818 | + } | |
819 | + if (mPcm == NULL) { | |
820 | + release_wake_lock("AudioOutLock"); | |
821 | + goto Error; | |
822 | + } | |
823 | + mStandby = false; | |
824 | + } | |
825 | + | |
826 | + TRACE_DRIVER_IN(DRV_PCM_WRITE) | |
827 | + ret = pcm_write(mPcm,(void*) p, bytes); | |
828 | + TRACE_DRIVER_OUT | |
829 | + | |
830 | + if (ret == 0) { | |
831 | + return bytes; | |
832 | + } | |
833 | + LOGW("write error: %d", errno); | |
834 | + status = -errno; | |
835 | + } | |
836 | +Error: | |
837 | + | |
838 | + standby(); | |
839 | + | |
840 | + // Simulate audio output timing in case of error | |
841 | + usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); | |
842 | + | |
843 | + return status; | |
844 | +} | |
845 | + | |
846 | +status_t AudioHardware::AudioStreamOutALSA::standby() | |
847 | +{ | |
848 | + if (mHardware == NULL) return NO_INIT; | |
849 | + | |
850 | + AutoMutex lock(mLock); | |
851 | + | |
852 | + { // scope for the AudioHardware lock | |
853 | + AutoMutex hwLock(mHardware->lock()); | |
854 | + | |
855 | + doStandby_l(); | |
856 | + } | |
857 | + | |
858 | + return NO_ERROR; | |
859 | +} | |
860 | + | |
861 | +void AudioHardware::AudioStreamOutALSA::doStandby_l() | |
862 | +{ | |
863 | + mStandbyCnt++; | |
864 | + | |
865 | + if (!mStandby) { | |
866 | + LOGD("AudioHardware pcm playback is going to standby."); | |
867 | + release_wake_lock("AudioOutLock"); | |
868 | + mStandby = true; | |
869 | + } | |
870 | + | |
871 | + close_l(); | |
872 | +} | |
873 | + | |
874 | +void AudioHardware::AudioStreamOutALSA::close_l() | |
875 | +{ | |
876 | + if (mMixer) { | |
877 | + mHardware->closeMixer_l(); | |
878 | + mMixer = NULL; | |
879 | + mRouteCtl = NULL; | |
880 | + } | |
881 | + if (mPcm) { | |
882 | + mHardware->closePcmOut_l(); | |
883 | + mPcm = NULL; | |
884 | + } | |
885 | +} | |
886 | + | |
887 | +status_t AudioHardware::AudioStreamOutALSA::open_l() | |
888 | +{ | |
889 | + LOGV("open pcm_out driver"); | |
890 | + mPcm = mHardware->openPcmOut_l(); | |
891 | + if (mPcm == NULL) { | |
892 | + return NO_INIT; | |
893 | + } | |
894 | + | |
895 | + mMixer = mHardware->openMixer_l(); | |
896 | + if (mMixer) { | |
897 | + LOGV("open playback normal"); | |
898 | + TRACE_DRIVER_IN(DRV_MIXER_GET) | |
899 | + mRouteCtl = mixer_get_control(mMixer, "Playback Path", 0); | |
900 | + TRACE_DRIVER_OUT | |
901 | + } | |
902 | + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { | |
903 | + const char *route = mHardware->getOutputRouteFromDevice(mDevices); | |
904 | + LOGV("write() wakeup setting route %s", route); | |
905 | + if (mRouteCtl) { | |
906 | + TRACE_DRIVER_IN(DRV_MIXER_SEL) | |
907 | + mixer_ctl_select(mRouteCtl, route); | |
908 | + TRACE_DRIVER_OUT | |
909 | + } | |
910 | + } | |
911 | + return NO_ERROR; | |
912 | +} | |
913 | + | |
914 | +status_t AudioHardware::AudioStreamOutALSA::dump(int fd, const Vector<String16>& args) | |
915 | +{ | |
916 | + const size_t SIZE = 256; | |
917 | + char buffer[SIZE]; | |
918 | + String8 result; | |
919 | + | |
920 | + bool locked = tryLock(mLock); | |
921 | + if (!locked) { | |
922 | + snprintf(buffer, SIZE, "\n\t\tAudioStreamOutALSA maybe deadlocked\n"); | |
923 | + } else { | |
924 | + mLock.unlock(); | |
925 | + } | |
926 | + | |
927 | + snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); | |
928 | + result.append(buffer); | |
929 | + snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); | |
930 | + result.append(buffer); | |
931 | + snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); | |
932 | + result.append(buffer); | |
933 | + snprintf(buffer, SIZE, "\t\tmRouteCtl: %p\n", mRouteCtl); | |
934 | + result.append(buffer); | |
935 | + snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); | |
936 | + result.append(buffer); | |
937 | + snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); | |
938 | + result.append(buffer); | |
939 | + snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); | |
940 | + result.append(buffer); | |
941 | + snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); | |
942 | + result.append(buffer); | |
943 | + snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); | |
944 | + result.append(buffer); | |
945 | + snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); | |
946 | + result.append(buffer); | |
947 | + | |
948 | + ::write(fd, result.string(), result.size()); | |
949 | + | |
950 | + return NO_ERROR; | |
951 | +} | |
952 | + | |
953 | +bool AudioHardware::AudioStreamOutALSA::checkStandby() | |
954 | +{ | |
955 | + return mStandby; | |
956 | +} | |
957 | + | |
958 | +status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs) | |
959 | +{ | |
960 | + AudioParameter param = AudioParameter(keyValuePairs); | |
961 | + status_t status = NO_ERROR; | |
962 | + int device; | |
963 | + LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string()); | |
964 | + | |
965 | + if (mHardware == NULL) return NO_INIT; | |
966 | + | |
967 | + { | |
968 | + AutoMutex lock(mLock); | |
969 | + | |
970 | + if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR) | |
971 | + { | |
972 | + AutoMutex hwLock(mHardware->lock()); | |
973 | + | |
974 | + if (mDevices != (uint32_t)device) { | |
975 | + mDevices = (uint32_t)device; | |
976 | + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { | |
977 | + doStandby_l(); | |
978 | + } | |
979 | + } | |
980 | + if (mHardware->mode() == AudioSystem::MODE_IN_CALL) { | |
981 | + mHardware->setIncallPath_l(device); | |
982 | + } | |
983 | + param.remove(String8(AudioParameter::keyRouting)); | |
984 | + } | |
985 | + } | |
986 | + | |
987 | + if (param.size()) { | |
988 | + status = BAD_VALUE; | |
989 | + } | |
990 | + | |
991 | + | |
992 | + return status; | |
993 | + | |
994 | +} | |
995 | + | |
996 | +String8 AudioHardware::AudioStreamOutALSA::getParameters(const String8& keys) | |
997 | +{ | |
998 | + AudioParameter param = AudioParameter(keys); | |
999 | + String8 value; | |
1000 | + String8 key = String8(AudioParameter::keyRouting); | |
1001 | + | |
1002 | + if (param.get(key, value) == NO_ERROR) { | |
1003 | + param.addInt(key, (int)mDevices); | |
1004 | + } | |
1005 | + | |
1006 | + LOGV("AudioStreamOutALSA::getParameters() %s", param.toString().string()); | |
1007 | + return param.toString(); | |
1008 | +} | |
1009 | + | |
1010 | +status_t AudioHardware::AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames) | |
1011 | +{ | |
1012 | + //TODO | |
1013 | + return INVALID_OPERATION; | |
1014 | +} | |
1015 | + | |
1016 | +//------------------------------------------------------------------------------ | |
1017 | +// AudioStreamInALSA | |
1018 | +//------------------------------------------------------------------------------ | |
1019 | + | |
1020 | +AudioHardware::AudioStreamInALSA::AudioStreamInALSA() : | |
1021 | + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), | |
1022 | + mStandby(true), mDevices(0), mChannels(AUDIO_HW_IN_CHANNELS), mChannelCount(1), | |
1023 | + mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES), | |
1024 | + mDownSampler(NULL), mReadStatus(NO_ERROR), mDriverOp(DRV_NONE), | |
1025 | + mStandbyCnt(0) | |
1026 | +{ | |
1027 | +} | |
1028 | + | |
1029 | +status_t AudioHardware::AudioStreamInALSA::set( | |
1030 | + AudioHardware* hw, uint32_t devices, int *pFormat, | |
1031 | + uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) | |
1032 | +{ | |
1033 | + if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) { | |
1034 | + *pFormat = AUDIO_HW_IN_FORMAT; | |
1035 | + return BAD_VALUE; | |
1036 | + } | |
1037 | + if (pRate == 0) { | |
1038 | + return BAD_VALUE; | |
1039 | + } | |
1040 | + uint32_t rate = AudioHardware::getInputSampleRate(*pRate); | |
1041 | + if (rate != *pRate) { | |
1042 | + *pRate = rate; | |
1043 | + return BAD_VALUE; | |
1044 | + } | |
1045 | + | |
1046 | + if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO && | |
1047 | + *pChannels != AudioSystem::CHANNEL_IN_STEREO)) { | |
1048 | + *pChannels = AUDIO_HW_IN_CHANNELS; | |
1049 | + return BAD_VALUE; | |
1050 | + } | |
1051 | + | |
1052 | + mHardware = hw; | |
1053 | + | |
1054 | + LOGV("AudioStreamInALSA::set(%d, %d, %u)", *pFormat, *pChannels, *pRate); | |
1055 | + | |
1056 | + mBufferSize = getBufferSize(*pRate, AudioSystem::popCount(*pChannels)); | |
1057 | + mDevices = devices; | |
1058 | + mChannels = *pChannels; | |
1059 | + mChannelCount = AudioSystem::popCount(mChannels); | |
1060 | + mSampleRate = rate; | |
1061 | + if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) { | |
1062 | + mDownSampler = new AudioHardware::DownSampler(mSampleRate, | |
1063 | + mChannelCount, | |
1064 | + AUDIO_HW_IN_PERIOD_SZ, | |
1065 | + this); | |
1066 | + status_t status = mDownSampler->initCheck(); | |
1067 | + if (status != NO_ERROR) { | |
1068 | + delete mDownSampler; | |
1069 | + LOGW("AudioStreamInALSA::set() downsampler init failed: %d", status); | |
1070 | + return status; | |
1071 | + } | |
1072 | + | |
1073 | + mPcmIn = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount]; | |
1074 | + } | |
1075 | + return NO_ERROR; | |
1076 | +} | |
1077 | + | |
1078 | +AudioHardware::AudioStreamInALSA::~AudioStreamInALSA() | |
1079 | +{ | |
1080 | + standby(); | |
1081 | + if (mDownSampler != NULL) { | |
1082 | + delete mDownSampler; | |
1083 | + if (mPcmIn != NULL) { | |
1084 | + delete[] mPcmIn; | |
1085 | + } | |
1086 | + } | |
1087 | +} | |
1088 | + | |
1089 | +ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes) | |
1090 | +{ | |
1091 | + // LOGV("AudioStreamInALSA::read(%p, %u)", buffer, bytes); | |
1092 | + status_t status = NO_INIT; | |
1093 | + int ret; | |
1094 | + | |
1095 | + if (mHardware == NULL) return NO_INIT; | |
1096 | + | |
1097 | + { // scope for the lock | |
1098 | + AutoMutex lock(mLock); | |
1099 | + | |
1100 | + if (mStandby) { | |
1101 | + AutoMutex hwLock(mHardware->lock()); | |
1102 | + | |
1103 | + LOGD("AudioHardware pcm capture is exiting standby."); | |
1104 | + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock"); | |
1105 | + | |
1106 | + sp<AudioStreamOutALSA> spOut = mHardware->output(); | |
1107 | + while (spOut != 0) { | |
1108 | + if (!spOut->checkStandby()) { | |
1109 | + int cnt = spOut->standbyCnt(); | |
1110 | + mHardware->lock().unlock(); | |
1111 | + mLock.unlock(); | |
1112 | + // Mutex acquisition order is always out -> in -> hw | |
1113 | + spOut->lock(); | |
1114 | + mLock.lock(); | |
1115 | + mHardware->lock().lock(); | |
1116 | + // make sure that another thread did not change output state | |
1117 | + // while the mutex is released | |
1118 | + if ((spOut == mHardware->output()) && (cnt == spOut->standbyCnt())) { | |
1119 | + LOGV("AudioStreamInALSA::read() force output standby"); | |
1120 | + spOut->close_l(); | |
1121 | + break; | |
1122 | + } | |
1123 | + spOut->unlock(); | |
1124 | + spOut = mHardware->output(); | |
1125 | + } else { | |
1126 | + spOut.clear(); | |
1127 | + } | |
1128 | + } | |
1129 | + // spOut is not 0 here only if the output was active and has been | |
1130 | + // closed above | |
1131 | + | |
1132 | + // open output before input | |
1133 | + if (spOut != 0) { | |
1134 | + if (spOut->open_l() != NO_ERROR) { | |
1135 | + spOut->doStandby_l(); | |
1136 | + } | |
1137 | + spOut->unlock(); | |
1138 | + } | |
1139 | + | |
1140 | + open_l(); | |
1141 | + | |
1142 | + if (mPcm == NULL) { | |
1143 | + release_wake_lock("AudioInLock"); | |
1144 | + goto Error; | |
1145 | + } | |
1146 | + mStandby = false; | |
1147 | + } | |
1148 | + | |
1149 | + | |
1150 | + if (mDownSampler != NULL) { | |
1151 | + size_t frames = bytes / frameSize(); | |
1152 | + size_t framesIn = 0; | |
1153 | + mReadStatus = 0; | |
1154 | + do { | |
1155 | + size_t outframes = frames - framesIn; | |
1156 | + mDownSampler->resample( | |
1157 | + (int16_t *)buffer + (framesIn * mChannelCount), | |
1158 | + &outframes); | |
1159 | + framesIn += outframes; | |
1160 | + } while ((framesIn < frames) && mReadStatus == 0); | |
1161 | + ret = mReadStatus; | |
1162 | + bytes = framesIn * frameSize(); | |
1163 | + } else { | |
1164 | + TRACE_DRIVER_IN(DRV_PCM_READ) | |
1165 | + ret = pcm_read(mPcm, buffer, bytes); | |
1166 | + TRACE_DRIVER_OUT | |
1167 | + } | |
1168 | + | |
1169 | + if (ret == 0) { | |
1170 | + return bytes; | |
1171 | + } | |
1172 | + | |
1173 | + LOGW("read error: %d", ret); | |
1174 | + status = ret; | |
1175 | + } | |
1176 | + | |
1177 | +Error: | |
1178 | + | |
1179 | + standby(); | |
1180 | + | |
1181 | + // Simulate audio output timing in case of error | |
1182 | + usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); | |
1183 | + | |
1184 | + return status; | |
1185 | +} | |
1186 | + | |
1187 | +status_t AudioHardware::AudioStreamInALSA::standby() | |
1188 | +{ | |
1189 | + if (mHardware == NULL) return NO_INIT; | |
1190 | + | |
1191 | + AutoMutex lock(mLock); | |
1192 | + | |
1193 | + { // scope for AudioHardware lock | |
1194 | + AutoMutex hwLock(mHardware->lock()); | |
1195 | + | |
1196 | + doStandby_l(); | |
1197 | + } | |
1198 | + return NO_ERROR; | |
1199 | +} | |
1200 | + | |
1201 | +void AudioHardware::AudioStreamInALSA::doStandby_l() | |
1202 | +{ | |
1203 | + mStandbyCnt++; | |
1204 | + | |
1205 | + if (!mStandby) { | |
1206 | + LOGD("AudioHardware pcm capture is going to standby."); | |
1207 | + release_wake_lock("AudioInLock"); | |
1208 | + mStandby = true; | |
1209 | + } | |
1210 | + close_l(); | |
1211 | +} | |
1212 | + | |
1213 | +void AudioHardware::AudioStreamInALSA::close_l() | |
1214 | +{ | |
1215 | + if (mMixer) { | |
1216 | + mHardware->closeMixer_l(); | |
1217 | + mMixer = NULL; | |
1218 | + mRouteCtl = NULL; | |
1219 | + } | |
1220 | + | |
1221 | + if (mPcm) { | |
1222 | + TRACE_DRIVER_IN(DRV_PCM_CLOSE) | |
1223 | + pcm_close(mPcm); | |
1224 | + TRACE_DRIVER_OUT | |
1225 | + mPcm = NULL; | |
1226 | + } | |
1227 | +} | |
1228 | + | |
1229 | +status_t AudioHardware::AudioStreamInALSA::open_l() | |
1230 | +{ | |
1231 | + unsigned flags = PCM_IN; | |
1232 | + if (mChannels == AudioSystem::CHANNEL_IN_MONO) { | |
1233 | + flags |= PCM_MONO; | |
1234 | + } | |
1235 | + flags |= (AUDIO_HW_IN_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT; | |
1236 | + flags |= (AUDIO_HW_IN_PERIOD_CNT - PCM_PERIOD_CNT_MIN) | |
1237 | + << PCM_PERIOD_CNT_SHIFT; | |
1238 | + | |
1239 | + LOGV("open pcm_in driver"); | |
1240 | + TRACE_DRIVER_IN(DRV_PCM_OPEN) | |
1241 | + mPcm = pcm_open(flags); | |
1242 | + TRACE_DRIVER_OUT | |
1243 | + if (!pcm_ready(mPcm)) { | |
1244 | + LOGE("cannot open pcm_in driver: %s\n", pcm_error(mPcm)); | |
1245 | + TRACE_DRIVER_IN(DRV_PCM_CLOSE) | |
1246 | + pcm_close(mPcm); | |
1247 | + TRACE_DRIVER_OUT | |
1248 | + mPcm = NULL; | |
1249 | + return NO_INIT; | |
1250 | + } | |
1251 | + | |
1252 | + if (mDownSampler != NULL) { | |
1253 | + mInPcmInBuf = 0; | |
1254 | + mDownSampler->reset(); | |
1255 | + } | |
1256 | + | |
1257 | + mMixer = mHardware->openMixer_l(); | |
1258 | + if (mMixer) { | |
1259 | + TRACE_DRIVER_IN(DRV_MIXER_GET) | |
1260 | + mRouteCtl = mixer_get_control(mMixer, "Capture MIC Path", 0); | |
1261 | + TRACE_DRIVER_OUT | |
1262 | + } | |
1263 | + | |
1264 | + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { | |
1265 | + const char *route = mHardware->getInputRouteFromDevice(mDevices); | |
1266 | + LOGV("read() wakeup setting route %s", route); | |
1267 | + if (mRouteCtl) { | |
1268 | + TRACE_DRIVER_IN(DRV_MIXER_SEL) | |
1269 | + mixer_ctl_select(mRouteCtl, route); | |
1270 | + TRACE_DRIVER_OUT | |
1271 | + } | |
1272 | + } | |
1273 | + | |
1274 | + return NO_ERROR; | |
1275 | +} | |
1276 | + | |
1277 | +status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args) | |
1278 | +{ | |
1279 | + const size_t SIZE = 256; | |
1280 | + char buffer[SIZE]; | |
1281 | + String8 result; | |
1282 | + | |
1283 | + bool locked = tryLock(mLock); | |
1284 | + if (!locked) { | |
1285 | + snprintf(buffer, SIZE, "\n\t\tAudioStreamInALSA maybe deadlocked\n"); | |
1286 | + } else { | |
1287 | + mLock.unlock(); | |
1288 | + } | |
1289 | + | |
1290 | + snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); | |
1291 | + result.append(buffer); | |
1292 | + snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); | |
1293 | + result.append(buffer); | |
1294 | + snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); | |
1295 | + result.append(buffer); | |
1296 | + snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); | |
1297 | + result.append(buffer); | |
1298 | + snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); | |
1299 | + result.append(buffer); | |
1300 | + snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); | |
1301 | + result.append(buffer); | |
1302 | + snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); | |
1303 | + result.append(buffer); | |
1304 | + snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); | |
1305 | + result.append(buffer); | |
1306 | + snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); | |
1307 | + result.append(buffer); | |
1308 | + write(fd, result.string(), result.size()); | |
1309 | + | |
1310 | + return NO_ERROR; | |
1311 | +} | |
1312 | + | |
1313 | +bool AudioHardware::AudioStreamInALSA::checkStandby() | |
1314 | +{ | |
1315 | + return mStandby; | |
1316 | +} | |
1317 | + | |
1318 | +status_t AudioHardware::AudioStreamInALSA::setParameters(const String8& keyValuePairs) | |
1319 | +{ | |
1320 | + AudioParameter param = AudioParameter(keyValuePairs); | |
1321 | + status_t status = NO_ERROR; | |
1322 | + int value; | |
1323 | + String8 source; | |
1324 | + | |
1325 | + LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string()); | |
1326 | + | |
1327 | + if (mHardware == NULL) return NO_INIT; | |
1328 | + | |
1329 | + { | |
1330 | + AutoMutex lock(mLock); | |
1331 | + | |
1332 | + if (param.get(String8(INPUT_SOURCE_KEY), source) == NO_ERROR) { | |
1333 | + AutoMutex hwLock(mHardware->lock()); | |
1334 | + | |
1335 | + mHardware->openMixer_l(); | |
1336 | + mHardware->setInputSource_l(source); | |
1337 | + mHardware->closeMixer_l(); | |
1338 | + | |
1339 | + param.remove(String8(INPUT_SOURCE_KEY)); | |
1340 | + } | |
1341 | + | |
1342 | + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) | |
1343 | + { | |
1344 | + if (value != 0) { | |
1345 | + AutoMutex hwLock(mHardware->lock()); | |
1346 | + | |
1347 | + if (mDevices != (uint32_t)value) { | |
1348 | + mDevices = (uint32_t)value; | |
1349 | + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { | |
1350 | + doStandby_l(); | |
1351 | + } | |
1352 | + } | |
1353 | + } | |
1354 | + param.remove(String8(AudioParameter::keyRouting)); | |
1355 | + } | |
1356 | + } | |
1357 | + | |
1358 | + | |
1359 | + if (param.size()) { | |
1360 | + status = BAD_VALUE; | |
1361 | + } | |
1362 | + | |
1363 | + return status; | |
1364 | + | |
1365 | +} | |
1366 | + | |
1367 | +String8 AudioHardware::AudioStreamInALSA::getParameters(const String8& keys) | |
1368 | +{ | |
1369 | + AudioParameter param = AudioParameter(keys); | |
1370 | + String8 value; | |
1371 | + String8 key = String8(AudioParameter::keyRouting); | |
1372 | + | |
1373 | + if (param.get(key, value) == NO_ERROR) { | |
1374 | + param.addInt(key, (int)mDevices); | |
1375 | + } | |
1376 | + | |
1377 | + LOGV("AudioStreamInALSA::getParameters() %s", param.toString().string()); | |
1378 | + return param.toString(); | |
1379 | +} | |
1380 | + | |
1381 | +status_t AudioHardware::AudioStreamInALSA::getNextBuffer(AudioHardware::BufferProvider::Buffer* buffer) | |
1382 | +{ | |
1383 | + if (mPcm == NULL) { | |
1384 | + buffer->raw = NULL; | |
1385 | + buffer->frameCount = 0; | |
1386 | + mReadStatus = NO_INIT; | |
1387 | + return NO_INIT; | |
1388 | + } | |
1389 | + | |
1390 | + if (mInPcmInBuf == 0) { | |
1391 | + TRACE_DRIVER_IN(DRV_PCM_READ) | |
1392 | + mReadStatus = pcm_read(mPcm,(void*) mPcmIn, AUDIO_HW_IN_PERIOD_SZ * frameSize()); | |
1393 | + TRACE_DRIVER_OUT | |
1394 | + if (mReadStatus != 0) { | |
1395 | + buffer->raw = NULL; | |
1396 | + buffer->frameCount = 0; | |
1397 | + return mReadStatus; | |
1398 | + } | |
1399 | + mInPcmInBuf = AUDIO_HW_IN_PERIOD_SZ; | |
1400 | + } | |
1401 | + | |
1402 | + buffer->frameCount = (buffer->frameCount > mInPcmInBuf) ? mInPcmInBuf : buffer->frameCount; | |
1403 | + buffer->i16 = mPcmIn + (AUDIO_HW_IN_PERIOD_SZ - mInPcmInBuf) * mChannelCount; | |
1404 | + | |
1405 | + return mReadStatus; | |
1406 | +} | |
1407 | + | |
1408 | +void AudioHardware::AudioStreamInALSA::releaseBuffer(Buffer* buffer) | |
1409 | +{ | |
1410 | + mInPcmInBuf -= buffer->frameCount; | |
1411 | +} | |
1412 | + | |
1413 | +size_t AudioHardware::AudioStreamInALSA::getBufferSize(uint32_t sampleRate, int channelCount) | |
1414 | +{ | |
1415 | + size_t ratio; | |
1416 | + | |
1417 | + switch (sampleRate) { | |
1418 | + case 8000: | |
1419 | + case 11025: | |
1420 | + ratio = 4; | |
1421 | + break; | |
1422 | + case 16000: | |
1423 | + case 22050: | |
1424 | + ratio = 2; | |
1425 | + break; | |
1426 | + case 44100: | |
1427 | + default: | |
1428 | + ratio = 1; | |
1429 | + break; | |
1430 | + } | |
1431 | + | |
1432 | + return (AUDIO_HW_IN_PERIOD_SZ*channelCount*sizeof(int16_t)) / ratio ; | |
1433 | +} | |
1434 | + | |
1435 | +//------------------------------------------------------------------------------ | |
1436 | +// DownSampler | |
1437 | +//------------------------------------------------------------------------------ | |
1438 | + | |
1439 | +/* | |
1440 | + * 2.30 fixed point FIR filter coefficients for conversion 44100 -> 22050. | |
1441 | + * (Works equivalently for 22010 -> 11025 or any other halving, of course.) | |
1442 | + * | |
1443 | + * Transition band from about 18 kHz, passband ripple < 0.1 dB, | |
1444 | + * stopband ripple at about -55 dB, linear phase. | |
1445 | + * | |
1446 | + * Design and display in MATLAB or Octave using: | |
1447 | + * | |
1448 | + * filter = fir1(19, 0.5); filter = round(filter * 2**30); freqz(filter * 2**-30); | |
1449 | + */ | |
1450 | +static const int32_t filter_22khz_coeff[] = { | |
1451 | + 2089257, 2898328, -5820678, -10484531, | |
1452 | + 19038724, 30542725, -50469415, -81505260, | |
1453 | + 152544464, 478517512, 478517512, 152544464, | |
1454 | + -81505260, -50469415, 30542725, 19038724, | |
1455 | + -10484531, -5820678, 2898328, 2089257, | |
1456 | +}; | |
1457 | +#define NUM_COEFF_22KHZ (sizeof(filter_22khz_coeff) / sizeof(filter_22khz_coeff[0])) | |
1458 | +#define OVERLAP_22KHZ (NUM_COEFF_22KHZ - 2) | |
1459 | + | |
1460 | +/* | |
1461 | + * Convolution of signals A and reverse(B). (In our case, the filter response | |
1462 | + * is symmetric, so the reversing doesn't matter.) | |
1463 | + * A is taken to be in 0.16 fixed-point, and B is taken to be in 2.30 fixed-point. | |
1464 | + * The answer will be in 16.16 fixed-point, unclipped. | |
1465 | + * | |
1466 | + * This function would probably be the prime candidate for SIMD conversion if | |
1467 | + * you want more speed. | |
1468 | + */ | |
1469 | +int32_t fir_convolve(const int16_t* a, const int32_t* b, int num_samples) | |
1470 | +{ | |
1471 | + int32_t sum = 1 << 13; | |
1472 | + for (int i = 0; i < num_samples; ++i) { | |
1473 | + sum += a[i] * (b[i] >> 16); | |
1474 | + } | |
1475 | + return sum >> 14; | |
1476 | +} | |
1477 | + | |
1478 | +/* Clip from 16.16 fixed-point to 0.16 fixed-point. */ | |
1479 | +int16_t clip(int32_t x) | |
1480 | +{ | |
1481 | + if (x < -32768) { | |
1482 | + return -32768; | |
1483 | + } else if (x > 32767) { | |
1484 | + return 32767; | |
1485 | + } else { | |
1486 | + return x; | |
1487 | + } | |
1488 | +} | |
1489 | + | |
1490 | +/* | |
1491 | + * Convert a chunk from 44 kHz to 22 kHz. Will update num_samples_in and num_samples_out | |
1492 | + * accordingly, since it may leave input samples in the buffer due to overlap. | |
1493 | + * | |
1494 | + * Input and output are taken to be in 0.16 fixed-point. | |
1495 | + */ | |
1496 | +void resample_2_1(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out) | |
1497 | +{ | |
1498 | + if (*num_samples_in < (int)NUM_COEFF_22KHZ) { | |
1499 | + *num_samples_out = 0; | |
1500 | + return; | |
1501 | + } | |
1502 | + | |
1503 | + int odd_smp = *num_samples_in & 0x1; | |
1504 | + int num_samples = *num_samples_in - odd_smp - OVERLAP_22KHZ; | |
1505 | + | |
1506 | + for (int i = 0; i < num_samples; i += 2) { | |
1507 | + output[i / 2] = clip(fir_convolve(input + i, filter_22khz_coeff, NUM_COEFF_22KHZ)); | |
1508 | + } | |
1509 | + | |
1510 | + memmove(input, input + num_samples, (OVERLAP_22KHZ + odd_smp) * sizeof(*input)); | |
1511 | + *num_samples_out = num_samples / 2; | |
1512 | + *num_samples_in = OVERLAP_22KHZ + odd_smp; | |
1513 | +} | |
1514 | + | |
1515 | +/* | |
1516 | + * 2.30 fixed point FIR filter coefficients for conversion 22050 -> 16000, | |
1517 | + * or 11025 -> 8000. | |
1518 | + * | |
1519 | + * Transition band from about 14 kHz, passband ripple < 0.1 dB, | |
1520 | + * stopband ripple at about -50 dB, linear phase. | |
1521 | + * | |
1522 | + * Design and display in MATLAB or Octave using: | |
1523 | + * | |
1524 | + * filter = fir1(23, 16000 / 22050); filter = round(filter * 2**30); freqz(filter * 2**-30); | |
1525 | + */ | |
1526 | +static const int32_t filter_16khz_coeff[] = { | |
1527 | + 2057290, -2973608, 1880478, 4362037, | |
1528 | + -14639744, 18523609, -1609189, -38502470, | |
1529 | + 78073125, -68353935, -59103896, 617555440, | |
1530 | + 617555440, -59103896, -68353935, 78073125, | |
1531 | + -38502470, -1609189, 18523609, -14639744, | |
1532 | + 4362037, 1880478, -2973608, 2057290, | |
1533 | +}; | |
1534 | +#define NUM_COEFF_16KHZ (sizeof(filter_16khz_coeff) / sizeof(filter_16khz_coeff[0])) | |
1535 | +#define OVERLAP_16KHZ (NUM_COEFF_16KHZ - 1) | |
1536 | + | |
1537 | +/* | |
1538 | + * Convert a chunk from 22 kHz to 16 kHz. Will update num_samples_in and | |
1539 | + * num_samples_out accordingly, since it may leave input samples in the buffer | |
1540 | + * due to overlap. | |
1541 | + * | |
1542 | + * This implementation is rather ad-hoc; it first low-pass filters the data | |
1543 | + * into a temporary buffer, and then converts chunks of 441 input samples at a | |
1544 | + * time into 320 output samples by simple linear interpolation. A better | |
1545 | + * implementation would use a polyphase filter bank to do these two operations | |
1546 | + * in one step. | |
1547 | + * | |
1548 | + * Input and output are taken to be in 0.16 fixed-point. | |
1549 | + */ | |
1550 | + | |
1551 | +#define RESAMPLE_16KHZ_SAMPLES_IN 441 | |
1552 | +#define RESAMPLE_16KHZ_SAMPLES_OUT 320 | |
1553 | + | |
1554 | +void resample_441_320(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out) | |
1555 | +{ | |
1556 | + const int num_blocks = (*num_samples_in - OVERLAP_16KHZ) / RESAMPLE_16KHZ_SAMPLES_IN; | |
1557 | + if (num_blocks < 1) { | |
1558 | + *num_samples_out = 0; | |
1559 | + return; | |
1560 | + } | |
1561 | + | |
1562 | + for (int i = 0; i < num_blocks; ++i) { | |
1563 | + uint32_t tmp[RESAMPLE_16KHZ_SAMPLES_IN]; | |
1564 | + for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_IN; ++j) { | |
1565 | + tmp[j] = fir_convolve(input + i * RESAMPLE_16KHZ_SAMPLES_IN + j, | |
1566 | + filter_16khz_coeff, | |
1567 | + NUM_COEFF_16KHZ); | |
1568 | + } | |
1569 | + | |
1570 | + const float step_float = (float)RESAMPLE_16KHZ_SAMPLES_IN / (float)RESAMPLE_16KHZ_SAMPLES_OUT; | |
1571 | + | |
1572 | + uint32_t in_sample_num = 0; // 16.16 fixed point | |
1573 | + const uint32_t step = (uint32_t)(step_float * 65536.0f + 0.5f); // 16.16 fixed point | |
1574 | + for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_OUT; ++j, in_sample_num += step) { | |
1575 | + const uint32_t whole = in_sample_num >> 16; | |
1576 | + const uint32_t frac = (in_sample_num & 0xffff); // 0.16 fixed point | |
1577 | + const int32_t s1 = tmp[whole]; | |
1578 | + const int32_t s2 = tmp[whole + 1]; | |
1579 | + *output++ = clip(s1 + (((s2 - s1) * (int32_t)frac) >> 16)); | |
1580 | + } | |
1581 | + } | |
1582 | + | |
1583 | + const int samples_consumed = num_blocks * RESAMPLE_16KHZ_SAMPLES_IN; | |
1584 | + memmove(input, input + samples_consumed, (*num_samples_in - samples_consumed) * sizeof(*input)); | |
1585 | + *num_samples_in -= samples_consumed; | |
1586 | + *num_samples_out = RESAMPLE_16KHZ_SAMPLES_OUT * num_blocks; | |
1587 | +} | |
1588 | + | |
1589 | + | |
1590 | +AudioHardware::DownSampler::DownSampler(uint32_t outSampleRate, | |
1591 | + uint32_t channelCount, | |
1592 | + uint32_t frameCount, | |
1593 | + AudioHardware::BufferProvider* provider) | |
1594 | + : mStatus(NO_INIT), mProvider(provider), mSampleRate(outSampleRate), | |
1595 | + mChannelCount(channelCount), mFrameCount(frameCount), | |
1596 | + mInLeft(NULL), mInRight(NULL), mTmpLeft(NULL), mTmpRight(NULL), | |
1597 | + mTmp2Left(NULL), mTmp2Right(NULL), mOutLeft(NULL), mOutRight(NULL) | |
1598 | + | |
1599 | +{ | |
1600 | + LOGV("AudioHardware::DownSampler() cstor %p SR %d channels %d frames %d", | |
1601 | + this, mSampleRate, mChannelCount, mFrameCount); | |
1602 | + | |
1603 | + if (mSampleRate != 8000 && mSampleRate != 11025 && mSampleRate != 16000 && | |
1604 | + mSampleRate != 22050) { | |
1605 | + LOGW("AudioHardware::DownSampler cstor: bad sampling rate: %d", mSampleRate); | |
1606 | + return; | |
1607 | + } | |
1608 | + | |
1609 | + mInLeft = new int16_t[mFrameCount]; | |
1610 | + mInRight = new int16_t[mFrameCount]; | |
1611 | + mTmpLeft = new int16_t[mFrameCount]; | |
1612 | + mTmpRight = new int16_t[mFrameCount]; | |
1613 | + mTmp2Left = new int16_t[mFrameCount]; | |
1614 | + mTmp2Right = new int16_t[mFrameCount]; | |
1615 | + mOutLeft = new int16_t[mFrameCount]; | |
1616 | + mOutRight = new int16_t[mFrameCount]; | |
1617 | + | |
1618 | + mStatus = NO_ERROR; | |
1619 | +} | |
1620 | + | |
1621 | +AudioHardware::DownSampler::~DownSampler() | |
1622 | +{ | |
1623 | + if (mInLeft) delete[] mInLeft; | |
1624 | + if (mInRight) delete[] mInRight; | |
1625 | + if (mTmpLeft) delete[] mTmpLeft; | |
1626 | + if (mTmpRight) delete[] mTmpRight; | |
1627 | + if (mTmp2Left) delete[] mTmp2Left; | |
1628 | + if (mTmp2Right) delete[] mTmp2Right; | |
1629 | + if (mOutLeft) delete[] mOutLeft; | |
1630 | + if (mOutRight) delete[] mOutRight; | |
1631 | +} | |
1632 | + | |
1633 | +void AudioHardware::DownSampler::reset() | |
1634 | +{ | |
1635 | + mInInBuf = 0; | |
1636 | + mInTmpBuf = 0; | |
1637 | + mInTmp2Buf = 0; | |
1638 | + mOutBufPos = 0; | |
1639 | + mInOutBuf = 0; | |
1640 | +} | |
1641 | + | |
1642 | + | |
1643 | +int AudioHardware::DownSampler::resample(int16_t* out, size_t *outFrameCount) | |
1644 | +{ | |
1645 | + if (mStatus != NO_ERROR) { | |
1646 | + return mStatus; | |
1647 | + } | |
1648 | + | |
1649 | + if (out == NULL || outFrameCount == NULL) { | |
1650 | + return BAD_VALUE; | |
1651 | + } | |
1652 | + | |
1653 | + int16_t *outLeft = mTmp2Left; | |
1654 | + int16_t *outRight = mTmp2Left; | |
1655 | + if (mSampleRate == 22050) { | |
1656 | + outLeft = mTmpLeft; | |
1657 | + outRight = mTmpRight; | |
1658 | + } else if (mSampleRate == 8000){ | |
1659 | + outLeft = mOutLeft; | |
1660 | + outRight = mOutRight; | |
1661 | + } | |
1662 | + | |
1663 | + int outFrames = 0; | |
1664 | + int remaingFrames = *outFrameCount; | |
1665 | + | |
1666 | + if (mInOutBuf) { | |
1667 | + int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames; | |
1668 | + | |
1669 | + for (int i = 0; i < frames; ++i) { | |
1670 | + out[i] = outLeft[mOutBufPos + i]; | |
1671 | + } | |
1672 | + if (mChannelCount == 2) { | |
1673 | + for (int i = 0; i < frames; ++i) { | |
1674 | + out[i * 2] = outLeft[mOutBufPos + i]; | |
1675 | + out[i * 2 + 1] = outRight[mOutBufPos + i]; | |
1676 | + } | |
1677 | + } | |
1678 | + remaingFrames -= frames; | |
1679 | + mInOutBuf -= frames; | |
1680 | + mOutBufPos += frames; | |
1681 | + outFrames += frames; | |
1682 | + } | |
1683 | + | |
1684 | + while (remaingFrames) { | |
1685 | + LOGW_IF((mInOutBuf != 0), "mInOutBuf should be 0 here"); | |
1686 | + | |
1687 | + AudioHardware::BufferProvider::Buffer buf; | |
1688 | + buf.frameCount = mFrameCount - mInInBuf; | |
1689 | + int ret = mProvider->getNextBuffer(&buf); | |
1690 | + if (buf.raw == NULL) { | |
1691 | + *outFrameCount = outFrames; | |
1692 | + return ret; | |
1693 | + } | |
1694 | + | |
1695 | + for (size_t i = 0; i < buf.frameCount; ++i) { | |
1696 | + mInLeft[i + mInInBuf] = buf.i16[i]; | |
1697 | + } | |
1698 | + if (mChannelCount == 2) { | |
1699 | + for (size_t i = 0; i < buf.frameCount; ++i) { | |
1700 | + mInLeft[i + mInInBuf] = buf.i16[i * 2]; | |
1701 | + mInRight[i + mInInBuf] = buf.i16[i * 2 + 1]; | |
1702 | + } | |
1703 | + } | |
1704 | + mInInBuf += buf.frameCount; | |
1705 | + mProvider->releaseBuffer(&buf); | |
1706 | + | |
1707 | + /* 44010 -> 22050 */ | |
1708 | + { | |
1709 | + int samples_in_left = mInInBuf; | |
1710 | + int samples_out_left; | |
1711 | + resample_2_1(mInLeft, mTmpLeft + mInTmpBuf, &samples_in_left, &samples_out_left); | |
1712 | + | |
1713 | + if (mChannelCount == 2) { | |
1714 | + int samples_in_right = mInInBuf; | |
1715 | + int samples_out_right; | |
1716 | + resample_2_1(mInRight, mTmpRight + mInTmpBuf, &samples_in_right, &samples_out_right); | |
1717 | + } | |
1718 | + | |
1719 | + mInInBuf = samples_in_left; | |
1720 | + mInTmpBuf += samples_out_left; | |
1721 | + mInOutBuf = samples_out_left; | |
1722 | + } | |
1723 | + | |
1724 | + if (mSampleRate == 11025 || mSampleRate == 8000) { | |
1725 | + /* 22050 - > 11025 */ | |
1726 | + int samples_in_left = mInTmpBuf; | |
1727 | + int samples_out_left; | |
1728 | + resample_2_1(mTmpLeft, mTmp2Left + mInTmp2Buf, &samples_in_left, &samples_out_left); | |
1729 | + | |
1730 | + if (mChannelCount == 2) { | |
1731 | + int samples_in_right = mInTmpBuf; | |
1732 | + int samples_out_right; | |
1733 | + resample_2_1(mTmpRight, mTmp2Right + mInTmp2Buf, &samples_in_right, &samples_out_right); | |
1734 | + } | |
1735 | + | |
1736 | + | |
1737 | + mInTmpBuf = samples_in_left; | |
1738 | + mInTmp2Buf += samples_out_left; | |
1739 | + mInOutBuf = samples_out_left; | |
1740 | + | |
1741 | + if (mSampleRate == 8000) { | |
1742 | + /* 11025 -> 8000*/ | |
1743 | + int samples_in_left = mInTmp2Buf; | |
1744 | + int samples_out_left; | |
1745 | + resample_441_320(mTmp2Left, mOutLeft, &samples_in_left, &samples_out_left); | |
1746 | + | |
1747 | + if (mChannelCount == 2) { | |
1748 | + int samples_in_right = mInTmp2Buf; | |
1749 | + int samples_out_right; | |
1750 | + resample_441_320(mTmp2Right, mOutRight, &samples_in_right, &samples_out_right); | |
1751 | + } | |
1752 | + | |
1753 | + mInTmp2Buf = samples_in_left; | |
1754 | + mInOutBuf = samples_out_left; | |
1755 | + } else { | |
1756 | + mInTmp2Buf = 0; | |
1757 | + } | |
1758 | + | |
1759 | + } else if (mSampleRate == 16000) { | |
1760 | + /* 22050 -> 16000*/ | |
1761 | + int samples_in_left = mInTmpBuf; | |
1762 | + int samples_out_left; | |
1763 | + resample_441_320(mTmpLeft, mTmp2Left, &samples_in_left, &samples_out_left); | |
1764 | + | |
1765 | + if (mChannelCount == 2) { | |
1766 | + int samples_in_right = mInTmpBuf; | |
1767 | + int samples_out_right; | |
1768 | + resample_441_320(mTmpRight, mTmp2Right, &samples_in_right, &samples_out_right); | |
1769 | + } | |
1770 | + | |
1771 | + mInTmpBuf = samples_in_left; | |
1772 | + mInOutBuf = samples_out_left; | |
1773 | + } else { | |
1774 | + mInTmpBuf = 0; | |
1775 | + } | |
1776 | + | |
1777 | + int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames; | |
1778 | + | |
1779 | + for (int i = 0; i < frames; ++i) { | |
1780 | + out[outFrames + i] = outLeft[i]; | |
1781 | + } | |
1782 | + if (mChannelCount == 2) { | |
1783 | + for (int i = 0; i < frames; ++i) { | |
1784 | + out[(outFrames + i) * 2] = outLeft[i]; | |
1785 | + out[(outFrames + i) * 2 + 1] = outRight[i]; | |
1786 | + } | |
1787 | + } | |
1788 | + remaingFrames -= frames; | |
1789 | + outFrames += frames; | |
1790 | + mOutBufPos = frames; | |
1791 | + mInOutBuf -= frames; | |
1792 | + } | |
1793 | + | |
1794 | + return 0; | |
1795 | +} | |
1796 | + | |
1797 | + | |
1798 | + | |
1799 | + | |
1800 | + | |
1801 | + | |
1802 | + | |
1803 | +//------------------------------------------------------------------------------ | |
1804 | +// Factory | |
1805 | +//------------------------------------------------------------------------------ | |
1806 | + | |
1807 | +extern "C" AudioHardwareInterface* createAudioHardware(void) { | |
1808 | + return new AudioHardware(); | |
1809 | +} | |
1810 | + | |
1811 | +}; // namespace android |
@@ -0,0 +1,331 @@ | ||
1 | +/* | |
2 | +** Copyright 2011, 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 | +#ifndef ANDROID_AUDIO_HARDWARE_H | |
18 | +#define ANDROID_AUDIO_HARDWARE_H | |
19 | + | |
20 | +#include <stdint.h> | |
21 | +#include <sys/types.h> | |
22 | + | |
23 | +#include <utils/threads.h> | |
24 | +#include <utils/SortedVector.h> | |
25 | + | |
26 | +#include <hardware_legacy/AudioHardwareBase.h> | |
27 | + | |
28 | +extern "C" { | |
29 | + struct pcm; | |
30 | + struct mixer; | |
31 | + struct mixer_ctl; | |
32 | +}; | |
33 | + | |
34 | +namespace android_audio_legacy { | |
35 | + | |
36 | +using android::sp; | |
37 | +using android::Mutex; | |
38 | +using android::AutoMutex; | |
39 | +using android::SortedVector; | |
40 | +using android::RefBase; | |
41 | + | |
42 | +// TODO: determine actual audio DSP and hardware latency | |
43 | +// Additionnal latency introduced by audio DSP and hardware in ms | |
44 | +#define AUDIO_HW_OUT_LATENCY_MS 0 | |
45 | +// Default audio output sample rate | |
46 | +#define AUDIO_HW_OUT_SAMPLERATE 44100 | |
47 | +// Default audio output channel mask | |
48 | +#define AUDIO_HW_OUT_CHANNELS (AudioSystem::CHANNEL_OUT_STEREO) | |
49 | +// Default audio output sample format | |
50 | +#define AUDIO_HW_OUT_FORMAT (AudioSystem::PCM_16_BIT) | |
51 | +// Kernel pcm out buffer size in frames at 44.1kHz | |
52 | +#define AUDIO_HW_OUT_PERIOD_MULT 8 // (8 * 128 = 1024 frames) | |
53 | +#define AUDIO_HW_OUT_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_OUT_PERIOD_MULT) | |
54 | +#define AUDIO_HW_OUT_PERIOD_CNT 4 | |
55 | +// Default audio output buffer size in bytes | |
56 | +#define AUDIO_HW_OUT_PERIOD_BYTES (AUDIO_HW_OUT_PERIOD_SZ * 2 * sizeof(int16_t)) | |
57 | + | |
58 | +// Default audio input sample rate | |
59 | +#define AUDIO_HW_IN_SAMPLERATE 8000 | |
60 | +// Default audio input channel mask | |
61 | +#define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO) | |
62 | +// Default audio input sample format | |
63 | +#define AUDIO_HW_IN_FORMAT (AudioSystem::PCM_16_BIT) | |
64 | +// Number of buffers in audio driver for input | |
65 | +#define AUDIO_HW_NUM_IN_BUF 2 | |
66 | +// Kernel pcm in buffer size in frames at 44.1kHz (before resampling) | |
67 | +#define AUDIO_HW_IN_PERIOD_MULT 16 // (16 * 128 = 2048 frames) | |
68 | +#define AUDIO_HW_IN_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_IN_PERIOD_MULT) | |
69 | +#define AUDIO_HW_IN_PERIOD_CNT 2 | |
70 | +// Default audio input buffer size in bytes (8kHz mono) | |
71 | +#define AUDIO_HW_IN_PERIOD_BYTES ((AUDIO_HW_IN_PERIOD_SZ*sizeof(int16_t))/8) | |
72 | + | |
73 | +#define INPUT_SOURCE_KEY "Input Source" | |
74 | + | |
75 | +class AudioHardware : public AudioHardwareBase { | |
76 | + class AudioStreamOutALSA; | |
77 | + class AudioStreamInALSA; | |
78 | + | |
79 | +public: | |
80 | + | |
81 | + AudioHardware(); | |
82 | + virtual ~AudioHardware(); | |
83 | + virtual status_t initCheck(); | |
84 | + | |
85 | + virtual status_t setVoiceVolume(float volume); | |
86 | + virtual status_t setMasterVolume(float volume); | |
87 | + | |
88 | + virtual status_t setMode(int mode); | |
89 | + | |
90 | + virtual status_t setMicMute(bool state); | |
91 | + virtual status_t getMicMute(bool* state); | |
92 | + | |
93 | + virtual status_t setParameters(const String8& keyValuePairs); | |
94 | + virtual String8 getParameters(const String8& keys); | |
95 | + | |
96 | + virtual AudioStreamOut* openOutputStream( | |
97 | + uint32_t devices, int *format=0, uint32_t *channels=0, | |
98 | + uint32_t *sampleRate=0, status_t *status=0); | |
99 | + | |
100 | + virtual AudioStreamIn* openInputStream( | |
101 | + uint32_t devices, int *format, uint32_t *channels, | |
102 | + uint32_t *sampleRate, status_t *status, | |
103 | + AudioSystem::audio_in_acoustics acoustics); | |
104 | + | |
105 | + virtual void closeOutputStream(AudioStreamOut* out); | |
106 | + virtual void closeInputStream(AudioStreamIn* in); | |
107 | + | |
108 | + virtual size_t getInputBufferSize( | |
109 | + uint32_t sampleRate, int format, int channelCount); | |
110 | + | |
111 | + int mode() { return mMode; } | |
112 | + const char *getOutputRouteFromDevice(uint32_t device); | |
113 | + const char *getInputRouteFromDevice(uint32_t device); | |
114 | + const char *getVoiceRouteFromDevice(uint32_t device); | |
115 | + | |
116 | + status_t setIncallPath_l(uint32_t device); | |
117 | + | |
118 | + status_t setInputSource_l(String8 source); | |
119 | + | |
120 | + static uint32_t getInputSampleRate(uint32_t sampleRate); | |
121 | + sp <AudioStreamInALSA> getActiveInput_l(); | |
122 | + | |
123 | + Mutex& lock() { return mLock; } | |
124 | + | |
125 | + struct pcm *openPcmOut_l(); | |
126 | + void closePcmOut_l(); | |
127 | + | |
128 | + struct mixer *openMixer_l(); | |
129 | + void closeMixer_l(); | |
130 | + | |
131 | + sp <AudioStreamOutALSA> output() { return mOutput; } | |
132 | + | |
133 | +protected: | |
134 | + virtual status_t dump(int fd, const Vector<String16>& args); | |
135 | + | |
136 | +private: | |
137 | + | |
138 | + bool mInit; | |
139 | + bool mMicMute; | |
140 | + sp <AudioStreamOutALSA> mOutput; | |
141 | + SortedVector < sp<AudioStreamInALSA> > mInputs; | |
142 | + Mutex mLock; | |
143 | + struct pcm* mPcm; | |
144 | + struct mixer* mMixer; | |
145 | + uint32_t mPcmOpenCnt; | |
146 | + uint32_t mMixerOpenCnt; | |
147 | + bool mInCallAudioMode; | |
148 | + | |
149 | + String8 mInputSource; | |
150 | + bool mBluetoothNrec; | |
151 | + bool mActivatedCP; | |
152 | + // trace driver operations for dump | |
153 | + int mDriverOp; | |
154 | + | |
155 | + static uint32_t checkInputSampleRate(uint32_t sampleRate); | |
156 | + static const uint32_t inputSamplingRates[]; | |
157 | + | |
158 | + class AudioStreamOutALSA : public AudioStreamOut, public RefBase { | |
159 | + public: | |
160 | + AudioStreamOutALSA(); | |
161 | + virtual ~AudioStreamOutALSA(); | |
162 | + status_t set(AudioHardware* mHardware, | |
163 | + uint32_t devices, | |
164 | + int *pFormat, | |
165 | + uint32_t *pChannels, | |
166 | + uint32_t *pRate); | |
167 | + virtual uint32_t sampleRate() const { return mSampleRate; } | |
168 | + virtual size_t bufferSize() const { return mBufferSize; } | |
169 | + virtual uint32_t channels() const { return mChannels; } | |
170 | + virtual int format() const { return AUDIO_HW_OUT_FORMAT; } | |
171 | + virtual uint32_t latency() const | |
172 | + { | |
173 | + return (1000 * AUDIO_HW_OUT_PERIOD_CNT * | |
174 | + (bufferSize()/frameSize()))/sampleRate() + AUDIO_HW_OUT_LATENCY_MS; | |
175 | + } | |
176 | + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } | |
177 | + virtual ssize_t write(const void* buffer, size_t bytes); | |
178 | + virtual status_t standby(); | |
179 | + bool checkStandby(); | |
180 | + | |
181 | + virtual status_t dump(int fd, const Vector<String16>& args); | |
182 | + virtual status_t setParameters(const String8& keyValuePairs); | |
183 | + virtual String8 getParameters(const String8& keys); | |
184 | + uint32_t device() { return mDevices; } | |
185 | + virtual status_t getRenderPosition(uint32_t *dspFrames); | |
186 | + | |
187 | + void doStandby_l(); | |
188 | + void close_l(); | |
189 | + status_t open_l(); | |
190 | + int standbyCnt() { return mStandbyCnt; } | |
191 | + | |
192 | + void lock() { mLock.lock(); } | |
193 | + void unlock() { mLock.unlock(); } | |
194 | + | |
195 | + private: | |
196 | + Mutex mLock; | |
197 | + AudioHardware *mHardware; | |
198 | + struct pcm *mPcm; | |
199 | + struct mixer *mMixer; | |
200 | + struct mixer_ctl *mRouteCtl; | |
201 | + const char *next_route; | |
202 | + bool mStandby; | |
203 | + uint32_t mDevices; | |
204 | + uint32_t mChannels; | |
205 | + uint32_t mSampleRate; | |
206 | + size_t mBufferSize; | |
207 | + // trace driver operations for dump | |
208 | + int mDriverOp; | |
209 | + int mStandbyCnt; | |
210 | + }; | |
211 | + | |
212 | + class BufferProvider { | |
213 | + public: | |
214 | + | |
215 | + struct Buffer { | |
216 | + union { | |
217 | + void* raw; | |
218 | + short* i16; | |
219 | + int8_t* i8; | |
220 | + }; | |
221 | + size_t frameCount; | |
222 | + }; | |
223 | + | |
224 | + virtual ~BufferProvider() {} | |
225 | + | |
226 | + virtual status_t getNextBuffer(Buffer* buffer) = 0; | |
227 | + virtual void releaseBuffer(Buffer* buffer) = 0; | |
228 | + }; | |
229 | + | |
230 | + class DownSampler { | |
231 | + public: | |
232 | + DownSampler(uint32_t outSampleRate, | |
233 | + uint32_t channelCount, | |
234 | + uint32_t frameCount, | |
235 | + BufferProvider *provider); | |
236 | + | |
237 | + virtual ~DownSampler(); | |
238 | + | |
239 | + void reset(); | |
240 | + status_t initCheck() { return mStatus; } | |
241 | + int resample(int16_t *out, size_t *outFrameCount); | |
242 | + | |
243 | + private: | |
244 | + status_t mStatus; | |
245 | + BufferProvider *mProvider; | |
246 | + uint32_t mSampleRate; | |
247 | + uint32_t mChannelCount; | |
248 | + uint32_t mFrameCount; | |
249 | + int16_t *mInLeft; | |
250 | + int16_t *mInRight; | |
251 | + int16_t *mTmpLeft; | |
252 | + int16_t *mTmpRight; | |
253 | + int16_t *mTmp2Left; | |
254 | + int16_t *mTmp2Right; | |
255 | + int16_t *mOutLeft; | |
256 | + int16_t *mOutRight; | |
257 | + int mInInBuf; | |
258 | + int mInTmpBuf; | |
259 | + int mInTmp2Buf; | |
260 | + int mOutBufPos; | |
261 | + int mInOutBuf; | |
262 | + }; | |
263 | + | |
264 | + | |
265 | + class AudioStreamInALSA : public AudioStreamIn, public BufferProvider, public RefBase { | |
266 | + public: | |
267 | + AudioStreamInALSA(); | |
268 | + virtual ~AudioStreamInALSA(); | |
269 | + status_t set(AudioHardware* hw, | |
270 | + uint32_t devices, | |
271 | + int *pFormat, | |
272 | + uint32_t *pChannels, | |
273 | + uint32_t *pRate, | |
274 | + AudioSystem::audio_in_acoustics acoustics); | |
275 | + virtual size_t bufferSize() const { return mBufferSize; } | |
276 | + virtual uint32_t channels() const { return mChannels; } | |
277 | + virtual int format() const { return AUDIO_HW_IN_FORMAT; } | |
278 | + virtual uint32_t sampleRate() const { return mSampleRate; } | |
279 | + virtual status_t setGain(float gain) { return INVALID_OPERATION; } | |
280 | + virtual ssize_t read(void* buffer, ssize_t bytes); | |
281 | + virtual status_t dump(int fd, const Vector<String16>& args); | |
282 | + virtual status_t standby(); | |
283 | + virtual status_t setParameters(const String8& keyValuePairs); | |
284 | + virtual String8 getParameters(const String8& keys); | |
285 | + virtual unsigned int getInputFramesLost() const { return 0; } | |
286 | + virtual status_t addAudioEffect(effect_handle_t effect) { return NO_ERROR; } | |
287 | + virtual status_t removeAudioEffect(effect_handle_t effect) { return NO_ERROR; } | |
288 | + | |
289 | + bool checkStandby(); | |
290 | + uint32_t device() { return mDevices; } | |
291 | + void doStandby_l(); | |
292 | + void close_l(); | |
293 | + status_t open_l(); | |
294 | + int standbyCnt() { return mStandbyCnt; } | |
295 | + | |
296 | + static size_t getBufferSize(uint32_t sampleRate, int channelCount); | |
297 | + | |
298 | + // BufferProvider | |
299 | + virtual status_t getNextBuffer(BufferProvider::Buffer* buffer); | |
300 | + virtual void releaseBuffer(BufferProvider::Buffer* buffer); | |
301 | + | |
302 | + void lock() { mLock.lock(); } | |
303 | + void unlock() { mLock.unlock(); } | |
304 | + | |
305 | + private: | |
306 | + Mutex mLock; | |
307 | + AudioHardware *mHardware; | |
308 | + struct pcm *mPcm; | |
309 | + struct mixer *mMixer; | |
310 | + struct mixer_ctl *mRouteCtl; | |
311 | + const char *next_route; | |
312 | + bool mStandby; | |
313 | + uint32_t mDevices; | |
314 | + uint32_t mChannels; | |
315 | + uint32_t mChannelCount; | |
316 | + uint32_t mSampleRate; | |
317 | + size_t mBufferSize; | |
318 | + DownSampler *mDownSampler; | |
319 | + status_t mReadStatus; | |
320 | + size_t mInPcmInBuf; | |
321 | + int16_t *mPcmIn; | |
322 | + // trace driver operations for dump | |
323 | + int mDriverOp; | |
324 | + int mStandbyCnt; | |
325 | + }; | |
326 | + | |
327 | +}; | |
328 | + | |
329 | +}; // namespace android | |
330 | + | |
331 | +#endif |
@@ -0,0 +1,53 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2011 The Android-x86 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 | + | |
11 | +TARGET_ALSA_CONF_DIR := system/usr/share/alsa | |
12 | +LOCAL_ALSA_CONF_DIR := external/alsa-lib/src/conf | |
13 | + | |
14 | +copy_conf := \ | |
15 | + alsa.conf \ | |
16 | + pcm/dsnoop.conf \ | |
17 | + pcm/modem.conf \ | |
18 | + pcm/dpl.conf \ | |
19 | + pcm/default.conf \ | |
20 | + pcm/surround51.conf \ | |
21 | + pcm/surround41.conf \ | |
22 | + pcm/surround50.conf \ | |
23 | + pcm/dmix.conf \ | |
24 | + pcm/center_lfe.conf \ | |
25 | + pcm/surround40.conf \ | |
26 | + pcm/side.conf \ | |
27 | + pcm/iec958.conf \ | |
28 | + pcm/rear.conf \ | |
29 | + pcm/surround71.conf \ | |
30 | + pcm/front.conf \ | |
31 | + cards/aliases.conf | |
32 | + | |
33 | +LOCAL_ALSA_INIT_DIR := external/alsa-utils/alsactl/init | |
34 | + | |
35 | +copy_init := \ | |
36 | + 00main \ | |
37 | + default \ | |
38 | + hda \ | |
39 | + help \ | |
40 | + info \ | |
41 | + test | |
42 | + | |
43 | +PRODUCT_COPY_FILES := \ | |
44 | + $(foreach f,$(copy_conf),$(LOCAL_ALSA_CONF_DIR)/$(f):$(TARGET_ALSA_CONF_DIR)/$(f)) \ | |
45 | + $(foreach f,$(copy_init),$(LOCAL_ALSA_INIT_DIR)/$(f):$(TARGET_ALSA_CONF_DIR)/init/$(f)) | |
46 | + | |
47 | +PRODUCT_PACKAGES := \ | |
48 | + audio.primary.$(TARGET_PRODUCT) \ | |
49 | + audio_policy.$(TARGET_PRODUCT) \ | |
50 | + audio.primary.default \ | |
51 | + audio.a2dp.default \ | |
52 | + audio_policy.default \ | |
53 | + alsa_ctl \ |
@@ -0,0 +1,85 @@ | ||
1 | +/* | |
2 | +** Copyright 2010, 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 | +#ifndef _AUDIO_H_ | |
18 | +#define _AUDIO_H_ | |
19 | + | |
20 | +#if defined(__cplusplus) | |
21 | +extern "C" { | |
22 | +#endif | |
23 | + | |
24 | +struct pcm; | |
25 | + | |
26 | +#define PCM_OUT 0x00000000 | |
27 | +#define PCM_IN 0x10000000 | |
28 | + | |
29 | +#define PCM_STEREO 0x00000000 | |
30 | +#define PCM_MONO 0x01000000 | |
31 | + | |
32 | +#define PCM_44100HZ 0x00000000 | |
33 | +#define PCM_48000HZ 0x00100000 | |
34 | +#define PCM_8000HZ 0x00200000 | |
35 | +#define PCM_RATE_MASK 0x00F00000 | |
36 | + | |
37 | +#define PCM_PERIOD_CNT_MIN 2 | |
38 | +#define PCM_PERIOD_CNT_SHIFT 16 | |
39 | +#define PCM_PERIOD_CNT_MASK (0xF << PCM_PERIOD_CNT_SHIFT) | |
40 | +#define PCM_PERIOD_SZ_MIN 128 | |
41 | +#define PCM_PERIOD_SZ_SHIFT 12 | |
42 | +#define PCM_PERIOD_SZ_MASK (0xF << PCM_PERIOD_SZ_SHIFT) | |
43 | + | |
44 | +/* Acquire/release a pcm channel. | |
45 | + * Returns non-zero on error | |
46 | + */ | |
47 | +struct pcm *pcm_open(unsigned flags); | |
48 | +int pcm_close(struct pcm *pcm); | |
49 | +int pcm_ready(struct pcm *pcm); | |
50 | + | |
51 | +/* Returns a human readable reason for the last error. */ | |
52 | +const char *pcm_error(struct pcm *pcm); | |
53 | + | |
54 | +/* Returns the buffer size (int bytes) that should be used for pcm_write. | |
55 | + * This will be 1/2 of the actual fifo size. | |
56 | + */ | |
57 | +unsigned pcm_buffer_size(struct pcm *pcm); | |
58 | + | |
59 | +/* Write data to the fifo. | |
60 | + * Will start playback on the first write or on a write that | |
61 | + * occurs after a fifo underrun. | |
62 | + */ | |
63 | +int pcm_write(struct pcm *pcm, void *data, unsigned count); | |
64 | +int pcm_read(struct pcm *pcm, void *data, unsigned count); | |
65 | + | |
66 | +struct mixer; | |
67 | +struct mixer_ctl; | |
68 | + | |
69 | +struct mixer *mixer_open(void); | |
70 | +void mixer_close(struct mixer *mixer); | |
71 | +void mixer_dump(struct mixer *mixer); | |
72 | + | |
73 | +struct mixer_ctl *mixer_get_control(struct mixer *mixer, | |
74 | + const char *name, unsigned index); | |
75 | +struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n); | |
76 | + | |
77 | +int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent); | |
78 | +int mixer_ctl_select(struct mixer_ctl *ctl, const char *value); | |
79 | +void mixer_ctl_print(struct mixer_ctl *ctl); | |
80 | + | |
81 | +#if defined(__cplusplus) | |
82 | +} /* extern "C" */ | |
83 | +#endif | |
84 | + | |
85 | +#endif |
@@ -0,0 +1,371 @@ | ||
1 | +/* | |
2 | +** Copyright 2010, 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 <stdio.h> | |
18 | +#include <stdlib.h> | |
19 | +#include <string.h> | |
20 | +#include <unistd.h> | |
21 | +#include <fcntl.h> | |
22 | +#include <errno.h> | |
23 | +#include <ctype.h> | |
24 | + | |
25 | +#include <linux/ioctl.h> | |
26 | +#define __force | |
27 | +#define __bitwise | |
28 | +#define __user | |
29 | +#include "asound.h" | |
30 | + | |
31 | +#include "alsa_audio.h" | |
32 | + | |
33 | +static const char *elem_iface_name(snd_ctl_elem_iface_t n) | |
34 | +{ | |
35 | + switch (n) { | |
36 | + case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD"; | |
37 | + case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP"; | |
38 | + case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER"; | |
39 | + case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM"; | |
40 | + case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI"; | |
41 | + case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER"; | |
42 | + case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ"; | |
43 | + default: return "???"; | |
44 | + } | |
45 | +} | |
46 | + | |
47 | +static const char *elem_type_name(snd_ctl_elem_type_t n) | |
48 | +{ | |
49 | + switch (n) { | |
50 | + case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE"; | |
51 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; | |
52 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32"; | |
53 | + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; | |
54 | + case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES"; | |
55 | + case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; | |
56 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; | |
57 | + default: return "???"; | |
58 | + } | |
59 | +} | |
60 | + | |
61 | + | |
62 | +struct mixer_ctl { | |
63 | + struct mixer *mixer; | |
64 | + struct snd_ctl_elem_info *info; | |
65 | + char **ename; | |
66 | +}; | |
67 | + | |
68 | +struct mixer { | |
69 | + int fd; | |
70 | + struct snd_ctl_elem_info *info; | |
71 | + struct mixer_ctl *ctl; | |
72 | + unsigned count; | |
73 | +}; | |
74 | + | |
75 | +void mixer_close(struct mixer *mixer) | |
76 | +{ | |
77 | + unsigned n,m; | |
78 | + | |
79 | + if (mixer->fd >= 0) | |
80 | + close(mixer->fd); | |
81 | + | |
82 | + if (mixer->ctl) { | |
83 | + for (n = 0; n < mixer->count; n++) { | |
84 | + if (mixer->ctl[n].ename) { | |
85 | + unsigned max = mixer->ctl[n].info->value.enumerated.items; | |
86 | + for (m = 0; m < max; m++) | |
87 | + free(mixer->ctl[n].ename[m]); | |
88 | + free(mixer->ctl[n].ename); | |
89 | + } | |
90 | + } | |
91 | + free(mixer->ctl); | |
92 | + } | |
93 | + | |
94 | + if (mixer->info) | |
95 | + free(mixer->info); | |
96 | + | |
97 | + free(mixer); | |
98 | +} | |
99 | + | |
100 | +struct mixer *mixer_open(void) | |
101 | +{ | |
102 | + struct snd_ctl_elem_list elist; | |
103 | + struct snd_ctl_elem_info tmp; | |
104 | + struct snd_ctl_elem_id *eid = NULL; | |
105 | + struct mixer *mixer = NULL; | |
106 | + unsigned n, m; | |
107 | + int fd; | |
108 | + | |
109 | + fd = open("/dev/snd/controlC0", O_RDWR); | |
110 | + if (fd < 0) | |
111 | + return 0; | |
112 | + | |
113 | + memset(&elist, 0, sizeof(elist)); | |
114 | + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) | |
115 | + goto fail; | |
116 | + | |
117 | + mixer = calloc(1, sizeof(*mixer)); | |
118 | + if (!mixer) | |
119 | + goto fail; | |
120 | + | |
121 | + mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); | |
122 | + mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); | |
123 | + if (!mixer->ctl || !mixer->info) | |
124 | + goto fail; | |
125 | + | |
126 | + eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); | |
127 | + if (!eid) | |
128 | + goto fail; | |
129 | + | |
130 | + mixer->count = elist.count; | |
131 | + mixer->fd = fd; | |
132 | + elist.space = mixer->count; | |
133 | + elist.pids = eid; | |
134 | + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) | |
135 | + goto fail; | |
136 | + | |
137 | + for (n = 0; n < mixer->count; n++) { | |
138 | + struct snd_ctl_elem_info *ei = mixer->info + n; | |
139 | + ei->id.numid = eid[n].numid; | |
140 | + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0) | |
141 | + goto fail; | |
142 | + mixer->ctl[n].info = ei; | |
143 | + mixer->ctl[n].mixer = mixer; | |
144 | + if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { | |
145 | + char **enames = calloc(ei->value.enumerated.items, sizeof(char*)); | |
146 | + if (!enames) | |
147 | + goto fail; | |
148 | + mixer->ctl[n].ename = enames; | |
149 | + for (m = 0; m < ei->value.enumerated.items; m++) { | |
150 | + memset(&tmp, 0, sizeof(tmp)); | |
151 | + tmp.id.numid = ei->id.numid; | |
152 | + tmp.value.enumerated.item = m; | |
153 | + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) | |
154 | + goto fail; | |
155 | + enames[m] = strdup(tmp.value.enumerated.name); | |
156 | + if (!enames[m]) | |
157 | + goto fail; | |
158 | + } | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + free(eid); | |
163 | + return mixer; | |
164 | + | |
165 | +fail: | |
166 | + if (eid) | |
167 | + free(eid); | |
168 | + if (mixer) | |
169 | + mixer_close(mixer); | |
170 | + else if (fd >= 0) | |
171 | + close(fd); | |
172 | + return 0; | |
173 | +} | |
174 | + | |
175 | +void mixer_dump(struct mixer *mixer) | |
176 | +{ | |
177 | + unsigned n, m; | |
178 | + | |
179 | + printf(" id iface dev sub idx num perms type name\n"); | |
180 | + for (n = 0; n < mixer->count; n++) { | |
181 | + struct snd_ctl_elem_info *ei = mixer->info + n; | |
182 | + | |
183 | + printf("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %s", | |
184 | + ei->id.numid, elem_iface_name(ei->id.iface), | |
185 | + ei->id.device, ei->id.subdevice, ei->id.index, | |
186 | + ei->count, | |
187 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ', | |
188 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ', | |
189 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ', | |
190 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ', | |
191 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ', | |
192 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ', | |
193 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ', | |
194 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ', | |
195 | + (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ', | |
196 | + elem_type_name(ei->type), | |
197 | + ei->id.name); | |
198 | + switch (ei->type) { | |
199 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: | |
200 | + printf(ei->value.integer.step ? | |
201 | + " { %ld-%ld, %ld }\n" : " { %ld-%ld }", | |
202 | + ei->value.integer.min, | |
203 | + ei->value.integer.max, | |
204 | + ei->value.integer.step); | |
205 | + break; | |
206 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: | |
207 | + printf(ei->value.integer64.step ? | |
208 | + " { %lld-%lld, %lld }\n" : " { %lld-%lld }", | |
209 | + ei->value.integer64.min, | |
210 | + ei->value.integer64.max, | |
211 | + ei->value.integer64.step); | |
212 | + break; | |
213 | + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { | |
214 | + unsigned m; | |
215 | + printf(" { %s=0", mixer->ctl[n].ename[0]); | |
216 | + for (m = 1; m < ei->value.enumerated.items; m++) | |
217 | + printf(", %s=%d", mixer->ctl[n].ename[m],m); | |
218 | + printf(" }"); | |
219 | + break; | |
220 | + } | |
221 | + } | |
222 | + printf("\n"); | |
223 | + } | |
224 | +} | |
225 | + | |
226 | +struct mixer_ctl *mixer_get_control(struct mixer *mixer, | |
227 | + const char *name, unsigned index) | |
228 | +{ | |
229 | + unsigned n; | |
230 | + for (n = 0; n < mixer->count; n++) { | |
231 | + if (mixer->info[n].id.index == index) { | |
232 | + if (!strcmp(name, (char*) mixer->info[n].id.name)) { | |
233 | + return mixer->ctl + n; | |
234 | + } | |
235 | + } | |
236 | + } | |
237 | + return 0; | |
238 | +} | |
239 | + | |
240 | +struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n) | |
241 | +{ | |
242 | + if (n < mixer->count) | |
243 | + return mixer->ctl + n; | |
244 | + return 0; | |
245 | +} | |
246 | + | |
247 | +void mixer_ctl_print(struct mixer_ctl *ctl) | |
248 | +{ | |
249 | + struct snd_ctl_elem_value ev; | |
250 | + unsigned n; | |
251 | + | |
252 | + memset(&ev, 0, sizeof(ev)); | |
253 | + ev.id.numid = ctl->info->id.numid; | |
254 | + if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev)) | |
255 | + return; | |
256 | + printf("%s:", ctl->info->id.name); | |
257 | + | |
258 | + switch (ctl->info->type) { | |
259 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | |
260 | + for (n = 0; n < ctl->info->count; n++) | |
261 | + printf(" %s", ev.value.integer.value[n] ? "ON" : "OFF"); | |
262 | + break; | |
263 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: { | |
264 | + for (n = 0; n < ctl->info->count; n++) | |
265 | + printf(" %ld", ev.value.integer.value[n]); | |
266 | + break; | |
267 | + } | |
268 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: | |
269 | + for (n = 0; n < ctl->info->count; n++) | |
270 | + printf(" %lld", ev.value.integer64.value[n]); | |
271 | + break; | |
272 | + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | |
273 | + for (n = 0; n < ctl->info->count; n++) { | |
274 | + unsigned v = ev.value.enumerated.item[n]; | |
275 | + printf(" %d (%s)", v, | |
276 | + (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???"); | |
277 | + } | |
278 | + break; | |
279 | + default: | |
280 | + printf(" ???"); | |
281 | + } | |
282 | + printf("\n"); | |
283 | +} | |
284 | + | |
285 | +static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent) | |
286 | +{ | |
287 | + long percent; | |
288 | + long range; | |
289 | + | |
290 | + if (_percent > 100) | |
291 | + percent = 100; | |
292 | + else | |
293 | + percent = (long) _percent; | |
294 | + | |
295 | + range = (ei->value.integer.max - ei->value.integer.min); | |
296 | + | |
297 | + return ei->value.integer.min + (range * percent) / 100LL; | |
298 | +} | |
299 | + | |
300 | +static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent) | |
301 | +{ | |
302 | + long long percent; | |
303 | + long long range; | |
304 | + | |
305 | + if (_percent > 100) | |
306 | + percent = 100; | |
307 | + else | |
308 | + percent = (long) _percent; | |
309 | + | |
310 | + range = (ei->value.integer.max - ei->value.integer.min) * 100LL; | |
311 | + | |
312 | + return ei->value.integer.min + (range / percent); | |
313 | +} | |
314 | + | |
315 | +int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent) | |
316 | +{ | |
317 | + struct snd_ctl_elem_value ev; | |
318 | + unsigned n; | |
319 | + | |
320 | + memset(&ev, 0, sizeof(ev)); | |
321 | + ev.id.numid = ctl->info->id.numid; | |
322 | + switch (ctl->info->type) { | |
323 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | |
324 | + for (n = 0; n < ctl->info->count; n++) | |
325 | + ev.value.integer.value[n] = !!percent; | |
326 | + break; | |
327 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: { | |
328 | + long value = scale_int(ctl->info, percent); | |
329 | + for (n = 0; n < ctl->info->count; n++) | |
330 | + ev.value.integer.value[n] = value; | |
331 | + break; | |
332 | + } | |
333 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: { | |
334 | + long long value = scale_int64(ctl->info, percent); | |
335 | + for (n = 0; n < ctl->info->count; n++) | |
336 | + ev.value.integer64.value[n] = value; | |
337 | + break; | |
338 | + } | |
339 | + default: | |
340 | + errno = EINVAL; | |
341 | + return -1; | |
342 | + } | |
343 | + | |
344 | + return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); | |
345 | +} | |
346 | + | |
347 | +int mixer_ctl_select(struct mixer_ctl *ctl, const char *value) | |
348 | +{ | |
349 | + unsigned n, max; | |
350 | + struct snd_ctl_elem_value ev; | |
351 | + | |
352 | + if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) { | |
353 | + errno = EINVAL; | |
354 | + return -1; | |
355 | + } | |
356 | + | |
357 | + max = ctl->info->value.enumerated.items; | |
358 | + for (n = 0; n < max; n++) { | |
359 | + if (!strcmp(value, ctl->ename[n])) { | |
360 | + memset(&ev, 0, sizeof(ev)); | |
361 | + ev.value.enumerated.item[0] = n; | |
362 | + ev.id.numid = ctl->info->id.numid; | |
363 | + if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0) | |
364 | + return -1; | |
365 | + return 0; | |
366 | + } | |
367 | + } | |
368 | + | |
369 | + errno = EINVAL; | |
370 | + return -1; | |
371 | +} |
@@ -0,0 +1,404 @@ | ||
1 | +/* | |
2 | +** Copyright 2010, 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 | +#define LOG_TAG "alsa_pcm" | |
18 | +//#define LOG_NDEBUG 0 | |
19 | +#include <cutils/log.h> | |
20 | +#include <cutils/config_utils.h> | |
21 | + | |
22 | +#include <stdio.h> | |
23 | +#include <stdlib.h> | |
24 | +#include <fcntl.h> | |
25 | +#include <stdarg.h> | |
26 | +#include <string.h> | |
27 | +#include <errno.h> | |
28 | +#include <unistd.h> | |
29 | + | |
30 | +#include <sys/ioctl.h> | |
31 | +#include <sys/mman.h> | |
32 | +#include <sys/time.h> | |
33 | + | |
34 | +#include <linux/ioctl.h> | |
35 | + | |
36 | +#include "alsa_audio.h" | |
37 | + | |
38 | +#define __force | |
39 | +#define __bitwise | |
40 | +#define __user | |
41 | +#include "asound.h" | |
42 | + | |
43 | +#define DEBUG 0 | |
44 | + | |
45 | +/* alsa parameter manipulation cruft */ | |
46 | + | |
47 | +#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL | |
48 | + | |
49 | +static inline int param_is_mask(int p) | |
50 | +{ | |
51 | + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && | |
52 | + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); | |
53 | +} | |
54 | + | |
55 | +static inline int param_is_interval(int p) | |
56 | +{ | |
57 | + return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && | |
58 | + (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); | |
59 | +} | |
60 | + | |
61 | +static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) | |
62 | +{ | |
63 | + return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); | |
64 | +} | |
65 | + | |
66 | +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) | |
67 | +{ | |
68 | + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); | |
69 | +} | |
70 | + | |
71 | +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) | |
72 | +{ | |
73 | + if (bit >= SNDRV_MASK_MAX) | |
74 | + return; | |
75 | + if (param_is_mask(n)) { | |
76 | + struct snd_mask *m = param_to_mask(p, n); | |
77 | + m->bits[0] = 0; | |
78 | + m->bits[1] = 0; | |
79 | + m->bits[bit >> 5] |= (1 << (bit & 31)); | |
80 | + } | |
81 | +} | |
82 | + | |
83 | +static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned val) | |
84 | +{ | |
85 | + if (param_is_interval(n)) { | |
86 | + struct snd_interval *i = param_to_interval(p, n); | |
87 | + i->min = val; | |
88 | + } | |
89 | +} | |
90 | + | |
91 | +static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned val) | |
92 | +{ | |
93 | + if (param_is_interval(n)) { | |
94 | + struct snd_interval *i = param_to_interval(p, n); | |
95 | + i->max = val; | |
96 | + } | |
97 | +} | |
98 | + | |
99 | +static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned val) | |
100 | +{ | |
101 | + if (param_is_interval(n)) { | |
102 | + struct snd_interval *i = param_to_interval(p, n); | |
103 | + i->min = val; | |
104 | + i->max = val; | |
105 | + i->integer = 1; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +static void param_init(struct snd_pcm_hw_params *p) | |
110 | +{ | |
111 | + int n; | |
112 | + memset(p, 0, sizeof(*p)); | |
113 | + for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; | |
114 | + n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { | |
115 | + struct snd_mask *m = param_to_mask(p, n); | |
116 | + m->bits[0] = ~0; | |
117 | + m->bits[1] = ~0; | |
118 | + } | |
119 | + for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; | |
120 | + n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { | |
121 | + struct snd_interval *i = param_to_interval(p, n); | |
122 | + i->min = 0; | |
123 | + i->max = ~0; | |
124 | + } | |
125 | +} | |
126 | + | |
127 | +/* debugging gunk */ | |
128 | + | |
129 | +#if DEBUG | |
130 | +static const char *param_name[PARAM_MAX+1] = { | |
131 | + [SNDRV_PCM_HW_PARAM_ACCESS] = "access", | |
132 | + [SNDRV_PCM_HW_PARAM_FORMAT] = "format", | |
133 | + [SNDRV_PCM_HW_PARAM_SUBFORMAT] = "subformat", | |
134 | + | |
135 | + [SNDRV_PCM_HW_PARAM_SAMPLE_BITS] = "sample_bits", | |
136 | + [SNDRV_PCM_HW_PARAM_FRAME_BITS] = "frame_bits", | |
137 | + [SNDRV_PCM_HW_PARAM_CHANNELS] = "channels", | |
138 | + [SNDRV_PCM_HW_PARAM_RATE] = "rate", | |
139 | + [SNDRV_PCM_HW_PARAM_PERIOD_TIME] = "period_time", | |
140 | + [SNDRV_PCM_HW_PARAM_PERIOD_SIZE] = "period_size", | |
141 | + [SNDRV_PCM_HW_PARAM_PERIOD_BYTES] = "period_bytes", | |
142 | + [SNDRV_PCM_HW_PARAM_PERIODS] = "periods", | |
143 | + [SNDRV_PCM_HW_PARAM_BUFFER_TIME] = "buffer_time", | |
144 | + [SNDRV_PCM_HW_PARAM_BUFFER_SIZE] = "buffer_size", | |
145 | + [SNDRV_PCM_HW_PARAM_BUFFER_BYTES] = "buffer_bytes", | |
146 | + [SNDRV_PCM_HW_PARAM_TICK_TIME] = "tick_time", | |
147 | +}; | |
148 | + | |
149 | +static void param_dump(struct snd_pcm_hw_params *p) | |
150 | +{ | |
151 | + int n; | |
152 | + | |
153 | + for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; | |
154 | + n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { | |
155 | + struct snd_mask *m = param_to_mask(p, n); | |
156 | + LOGV("%s = %08x%08x\n", param_name[n], | |
157 | + m->bits[1], m->bits[0]); | |
158 | + } | |
159 | + for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; | |
160 | + n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { | |
161 | + struct snd_interval *i = param_to_interval(p, n); | |
162 | + LOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n", | |
163 | + param_name[n], i->min, i->max, i->openmin, | |
164 | + i->openmax, i->integer, i->empty); | |
165 | + } | |
166 | + LOGV("info = %08x\n", p->info); | |
167 | + LOGV("msbits = %d\n", p->msbits); | |
168 | + LOGV("rate = %d/%d\n", p->rate_num, p->rate_den); | |
169 | + LOGV("fifo = %d\n", (int) p->fifo_size); | |
170 | +} | |
171 | + | |
172 | +static void info_dump(struct snd_pcm_info *info) | |
173 | +{ | |
174 | + LOGV("device = %d\n", info->device); | |
175 | + LOGV("subdevice = %d\n", info->subdevice); | |
176 | + LOGV("stream = %d\n", info->stream); | |
177 | + LOGV("card = %d\n", info->card); | |
178 | + LOGV("id = '%s'\n", info->id); | |
179 | + LOGV("name = '%s'\n", info->name); | |
180 | + LOGV("subname = '%s'\n", info->subname); | |
181 | + LOGV("dev_class = %d\n", info->dev_class); | |
182 | + LOGV("dev_subclass = %d\n", info->dev_subclass); | |
183 | + LOGV("subdevices_count = %d\n", info->subdevices_count); | |
184 | + LOGV("subdevices_avail = %d\n", info->subdevices_avail); | |
185 | +} | |
186 | +#else | |
187 | +static void param_dump(struct snd_pcm_hw_params *p) {} | |
188 | +static void info_dump(struct snd_pcm_info *info) {} | |
189 | +#endif | |
190 | + | |
191 | +#define PCM_ERROR_MAX 128 | |
192 | + | |
193 | +struct pcm { | |
194 | + int fd; | |
195 | + unsigned flags; | |
196 | + int running:1; | |
197 | + int underruns; | |
198 | + unsigned buffer_size; | |
199 | + char error[PCM_ERROR_MAX]; | |
200 | +}; | |
201 | + | |
202 | +unsigned pcm_buffer_size(struct pcm *pcm) | |
203 | +{ | |
204 | + return pcm->buffer_size; | |
205 | +} | |
206 | + | |
207 | +const char* pcm_error(struct pcm *pcm) | |
208 | +{ | |
209 | + return pcm->error; | |
210 | +} | |
211 | + | |
212 | +static int oops(struct pcm *pcm, int e, const char *fmt, ...) | |
213 | +{ | |
214 | + va_list ap; | |
215 | + int sz; | |
216 | + | |
217 | + va_start(ap, fmt); | |
218 | + vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); | |
219 | + va_end(ap); | |
220 | + sz = strlen(pcm->error); | |
221 | + | |
222 | + if (errno) | |
223 | + snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, | |
224 | + ": %s", strerror(e)); | |
225 | + return -1; | |
226 | +} | |
227 | + | |
228 | +int pcm_write(struct pcm *pcm, void *data, unsigned count) | |
229 | +{ | |
230 | + struct snd_xferi x; | |
231 | + | |
232 | + if (pcm->flags & PCM_IN) | |
233 | + return -EINVAL; | |
234 | + | |
235 | + x.buf = data; | |
236 | + x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); | |
237 | + | |
238 | + for (;;) { | |
239 | + if (!pcm->running) { | |
240 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) | |
241 | + return oops(pcm, errno, "cannot prepare channel"); | |
242 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) | |
243 | + return oops(pcm, errno, "cannot write initial data"); | |
244 | + pcm->running = 1; | |
245 | + return 0; | |
246 | + } | |
247 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { | |
248 | + pcm->running = 0; | |
249 | + if (errno == EPIPE) { | |
250 | + /* we failed to make our window -- try to restart */ | |
251 | + pcm->underruns++; | |
252 | + continue; | |
253 | + } | |
254 | + return oops(pcm, errno, "cannot write stream data"); | |
255 | + } | |
256 | + return 0; | |
257 | + } | |
258 | +} | |
259 | + | |
260 | +int pcm_read(struct pcm *pcm, void *data, unsigned count) | |
261 | +{ | |
262 | + struct snd_xferi x; | |
263 | + | |
264 | + if (!(pcm->flags & PCM_IN)) | |
265 | + return -EINVAL; | |
266 | + | |
267 | + x.buf = data; | |
268 | + x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); | |
269 | + | |
270 | +// LOGV("read() %d frames", x.frames); | |
271 | + for (;;) { | |
272 | + if (!pcm->running) { | |
273 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) | |
274 | + return oops(pcm, errno, "cannot prepare channel"); | |
275 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) | |
276 | + return oops(pcm, errno, "cannot start channel"); | |
277 | + pcm->running = 1; | |
278 | + } | |
279 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { | |
280 | + pcm->running = 0; | |
281 | + if (errno == EPIPE) { | |
282 | + /* we failed to make our window -- try to restart */ | |
283 | + pcm->underruns++; | |
284 | + continue; | |
285 | + } | |
286 | + return oops(pcm, errno, "cannot read stream data"); | |
287 | + } | |
288 | +// LOGV("read() got %d frames", x.frames); | |
289 | + return 0; | |
290 | + } | |
291 | +} | |
292 | + | |
293 | +static struct pcm bad_pcm = { | |
294 | + .fd = -1, | |
295 | +}; | |
296 | + | |
297 | +int pcm_close(struct pcm *pcm) | |
298 | +{ | |
299 | + if (pcm == &bad_pcm) | |
300 | + return 0; | |
301 | + | |
302 | + if (pcm->fd >= 0) | |
303 | + close(pcm->fd); | |
304 | + pcm->running = 0; | |
305 | + pcm->buffer_size = 0; | |
306 | + pcm->fd = -1; | |
307 | + return 0; | |
308 | +} | |
309 | + | |
310 | +struct pcm *pcm_open(unsigned flags) | |
311 | +{ | |
312 | + int i; | |
313 | + char dname[32]; | |
314 | + struct pcm *pcm; | |
315 | + struct snd_pcm_info info; | |
316 | + struct snd_pcm_hw_params params; | |
317 | + struct snd_pcm_sw_params sparams; | |
318 | + unsigned period_sz; | |
319 | + unsigned period_cnt; | |
320 | + | |
321 | + LOGV("pcm_open(0x%08x)",flags); | |
322 | + | |
323 | + pcm = calloc(1, sizeof(struct pcm)); | |
324 | + if (!pcm) | |
325 | + return &bad_pcm; | |
326 | + | |
327 | + LOGV("pcm_open() period sz multiplier %d", | |
328 | + ((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); | |
329 | + period_sz = 128 * (((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); | |
330 | + LOGV("pcm_open() period cnt %d", | |
331 | + ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN); | |
332 | + period_cnt = ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN; | |
333 | + | |
334 | + pcm->flags = flags; | |
335 | + for (i = 0; i < 3; ++i) { | |
336 | + sprintf(dname, "/dev/snd/pcmC%dD0%c", i, (flags & PCM_IN) ? 'c' : 'p'); | |
337 | + pcm->fd = open(dname, O_RDWR); | |
338 | + if (pcm->fd >= 0) | |
339 | + break; | |
340 | + oops(pcm, errno, "cannot open device '%s'", dname); | |
341 | + } | |
342 | + if (i == 3) | |
343 | + return pcm; | |
344 | + | |
345 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { | |
346 | + oops(pcm, errno, "cannot get info - %s", dname); | |
347 | + goto fail; | |
348 | + } | |
349 | + info_dump(&info); | |
350 | + | |
351 | + LOGV("pcm_open() period_cnt %d period_sz %d channels %d", | |
352 | + period_cnt, period_sz, (flags & PCM_MONO) ? 1 : 2); | |
353 | + | |
354 | + param_init(¶ms); | |
355 | + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, | |
356 | + SNDRV_PCM_ACCESS_RW_INTERLEAVED); | |
357 | + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, | |
358 | + SNDRV_PCM_FORMAT_S16_LE); | |
359 | + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, | |
360 | + SNDRV_PCM_SUBFORMAT_STD); | |
361 | + param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_sz); | |
362 | + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); | |
363 | + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, | |
364 | + (flags & PCM_MONO) ? 16 : 32); | |
365 | + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, | |
366 | + (flags & PCM_MONO) ? 1 : 2); | |
367 | + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, period_cnt); | |
368 | + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 44100); | |
369 | + | |
370 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { | |
371 | + oops(pcm, errno, "cannot set hw params"); | |
372 | + goto fail; | |
373 | + } | |
374 | + param_dump(¶ms); | |
375 | + | |
376 | + memset(&sparams, 0, sizeof(sparams)); | |
377 | + sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; | |
378 | + sparams.period_step = 1; | |
379 | + sparams.avail_min = 1; | |
380 | + sparams.start_threshold = period_cnt * period_sz; | |
381 | + sparams.stop_threshold = period_cnt * period_sz; | |
382 | + sparams.xfer_align = period_sz / 2; /* needed for old kernels */ | |
383 | + sparams.silence_size = 0; | |
384 | + sparams.silence_threshold = 0; | |
385 | + | |
386 | + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { | |
387 | + oops(pcm, errno, "cannot set sw params"); | |
388 | + goto fail; | |
389 | + } | |
390 | + | |
391 | + pcm->buffer_size = period_cnt * period_sz; | |
392 | + pcm->underruns = 0; | |
393 | + return pcm; | |
394 | + | |
395 | +fail: | |
396 | + close(pcm->fd); | |
397 | + pcm->fd = -1; | |
398 | + return pcm; | |
399 | +} | |
400 | + | |
401 | +int pcm_ready(struct pcm *pcm) | |
402 | +{ | |
403 | + return pcm->fd >= 0; | |
404 | +} |
@@ -0,0 +1,813 @@ | ||
1 | +/**************************************************************************** | |
2 | + **************************************************************************** | |
3 | + *** | |
4 | + *** This header was automatically generated from a Linux kernel header | |
5 | + *** of the same name, to make information necessary for userspace to | |
6 | + *** call into the kernel available to libc. It contains only constants, | |
7 | + *** structures, and macros generated from the original header, and thus, | |
8 | + *** contains no copyrightable information. | |
9 | + *** | |
10 | + **************************************************************************** | |
11 | + ****************************************************************************/ | |
12 | +#ifndef __SOUND_ASOUND_H | |
13 | +#define __SOUND_ASOUND_H | |
14 | + | |
15 | +#include <linux/types.h> | |
16 | + | |
17 | +#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor)) | |
18 | +#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff) | |
19 | +#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff) | |
20 | +#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff) | |
21 | +#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion))) | |
22 | + | |
23 | +struct snd_aes_iec958 { | |
24 | + unsigned char status[24]; | |
25 | + unsigned char subcode[147]; | |
26 | + unsigned char pad; | |
27 | + unsigned char dig_subframe[4]; | |
28 | +}; | |
29 | + | |
30 | +#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1) | |
31 | + | |
32 | +enum { | |
33 | + SNDRV_HWDEP_IFACE_OPL2 = 0, | |
34 | + SNDRV_HWDEP_IFACE_OPL3, | |
35 | + SNDRV_HWDEP_IFACE_OPL4, | |
36 | + SNDRV_HWDEP_IFACE_SB16CSP, | |
37 | + SNDRV_HWDEP_IFACE_EMU10K1, | |
38 | + SNDRV_HWDEP_IFACE_YSS225, | |
39 | + SNDRV_HWDEP_IFACE_ICS2115, | |
40 | + SNDRV_HWDEP_IFACE_SSCAPE, | |
41 | + SNDRV_HWDEP_IFACE_VX, | |
42 | + SNDRV_HWDEP_IFACE_MIXART, | |
43 | + SNDRV_HWDEP_IFACE_USX2Y, | |
44 | + SNDRV_HWDEP_IFACE_EMUX_WAVETABLE, | |
45 | + SNDRV_HWDEP_IFACE_BLUETOOTH, | |
46 | + SNDRV_HWDEP_IFACE_USX2Y_PCM, | |
47 | + SNDRV_HWDEP_IFACE_PCXHR, | |
48 | + SNDRV_HWDEP_IFACE_SB_RC, | |
49 | + SNDRV_HWDEP_IFACE_HDA, | |
50 | + SNDRV_HWDEP_IFACE_USB_STREAM, | |
51 | + | |
52 | + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM | |
53 | +}; | |
54 | + | |
55 | +struct snd_hwdep_info { | |
56 | + unsigned int device; | |
57 | + int card; | |
58 | + unsigned char id[64]; | |
59 | + unsigned char name[80]; | |
60 | + int iface; | |
61 | + unsigned char reserved[64]; | |
62 | +}; | |
63 | + | |
64 | +struct snd_hwdep_dsp_status { | |
65 | + unsigned int version; | |
66 | + unsigned char id[32]; | |
67 | + unsigned int num_dsps; | |
68 | + unsigned int dsp_loaded; | |
69 | + unsigned int chip_ready; | |
70 | + unsigned char reserved[16]; | |
71 | +}; | |
72 | + | |
73 | +struct snd_hwdep_dsp_image { | |
74 | + unsigned int index; | |
75 | + unsigned char name[64]; | |
76 | + unsigned char __user *image; | |
77 | + size_t length; | |
78 | + unsigned long driver_data; | |
79 | +}; | |
80 | + | |
81 | +#define SNDRV_HWDEP_IOCTL_PVERSION _IOR ('H', 0x00, int) | |
82 | +#define SNDRV_HWDEP_IOCTL_INFO _IOR ('H', 0x01, struct snd_hwdep_info) | |
83 | +#define SNDRV_HWDEP_IOCTL_DSP_STATUS _IOR('H', 0x02, struct snd_hwdep_dsp_status) | |
84 | +#define SNDRV_HWDEP_IOCTL_DSP_LOAD _IOW('H', 0x03, struct snd_hwdep_dsp_image) | |
85 | + | |
86 | +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) | |
87 | + | |
88 | +typedef unsigned long snd_pcm_uframes_t; | |
89 | +typedef signed long snd_pcm_sframes_t; | |
90 | + | |
91 | +enum { | |
92 | + SNDRV_PCM_CLASS_GENERIC = 0, | |
93 | + SNDRV_PCM_CLASS_MULTI, | |
94 | + SNDRV_PCM_CLASS_MODEM, | |
95 | + SNDRV_PCM_CLASS_DIGITIZER, | |
96 | + | |
97 | + SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER, | |
98 | +}; | |
99 | + | |
100 | +enum { | |
101 | + SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, | |
102 | + SNDRV_PCM_SUBCLASS_MULTI_MIX, | |
103 | + | |
104 | + SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX, | |
105 | +}; | |
106 | + | |
107 | +enum { | |
108 | + SNDRV_PCM_STREAM_PLAYBACK = 0, | |
109 | + SNDRV_PCM_STREAM_CAPTURE, | |
110 | + SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, | |
111 | +}; | |
112 | + | |
113 | +typedef int __bitwise snd_pcm_access_t; | |
114 | +#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ((__force snd_pcm_access_t) 0) | |
115 | +#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ((__force snd_pcm_access_t) 1) | |
116 | +#define SNDRV_PCM_ACCESS_MMAP_COMPLEX ((__force snd_pcm_access_t) 2) | |
117 | +#define SNDRV_PCM_ACCESS_RW_INTERLEAVED ((__force snd_pcm_access_t) 3) | |
118 | +#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ((__force snd_pcm_access_t) 4) | |
119 | +#define SNDRV_PCM_ACCESS_LAST SNDRV_PCM_ACCESS_RW_NONINTERLEAVED | |
120 | + | |
121 | +typedef int __bitwise snd_pcm_format_t; | |
122 | +#define SNDRV_PCM_FORMAT_S8 ((__force snd_pcm_format_t) 0) | |
123 | +#define SNDRV_PCM_FORMAT_U8 ((__force snd_pcm_format_t) 1) | |
124 | +#define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2) | |
125 | +#define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3) | |
126 | +#define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4) | |
127 | +#define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5) | |
128 | +#define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6) | |
129 | +#define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) | |
130 | +#define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) | |
131 | +#define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) | |
132 | +#define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) | |
133 | +#define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) | |
134 | +#define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) | |
135 | +#define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13) | |
136 | +#define SNDRV_PCM_FORMAT_FLOAT_LE ((__force snd_pcm_format_t) 14) | |
137 | +#define SNDRV_PCM_FORMAT_FLOAT_BE ((__force snd_pcm_format_t) 15) | |
138 | +#define SNDRV_PCM_FORMAT_FLOAT64_LE ((__force snd_pcm_format_t) 16) | |
139 | +#define SNDRV_PCM_FORMAT_FLOAT64_BE ((__force snd_pcm_format_t) 17) | |
140 | +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18) | |
141 | +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19) | |
142 | +#define SNDRV_PCM_FORMAT_MU_LAW ((__force snd_pcm_format_t) 20) | |
143 | +#define SNDRV_PCM_FORMAT_A_LAW ((__force snd_pcm_format_t) 21) | |
144 | +#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22) | |
145 | +#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23) | |
146 | +#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24) | |
147 | +#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31) | |
148 | +#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) | |
149 | +#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) | |
150 | +#define SNDRV_PCM_FORMAT_U24_3LE ((__force snd_pcm_format_t) 34) | |
151 | +#define SNDRV_PCM_FORMAT_U24_3BE ((__force snd_pcm_format_t) 35) | |
152 | +#define SNDRV_PCM_FORMAT_S20_3LE ((__force snd_pcm_format_t) 36) | |
153 | +#define SNDRV_PCM_FORMAT_S20_3BE ((__force snd_pcm_format_t) 37) | |
154 | +#define SNDRV_PCM_FORMAT_U20_3LE ((__force snd_pcm_format_t) 38) | |
155 | +#define SNDRV_PCM_FORMAT_U20_3BE ((__force snd_pcm_format_t) 39) | |
156 | +#define SNDRV_PCM_FORMAT_S18_3LE ((__force snd_pcm_format_t) 40) | |
157 | +#define SNDRV_PCM_FORMAT_S18_3BE ((__force snd_pcm_format_t) 41) | |
158 | +#define SNDRV_PCM_FORMAT_U18_3LE ((__force snd_pcm_format_t) 42) | |
159 | +#define SNDRV_PCM_FORMAT_U18_3BE ((__force snd_pcm_format_t) 43) | |
160 | +#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_U18_3BE | |
161 | + | |
162 | +#ifdef SNDRV_LITTLE_ENDIAN | |
163 | +#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE | |
164 | +#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_LE | |
165 | +#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_LE | |
166 | +#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_LE | |
167 | +#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_LE | |
168 | +#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_LE | |
169 | +#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_LE | |
170 | +#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_LE | |
171 | +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE | |
172 | +#endif | |
173 | +#ifdef SNDRV_BIG_ENDIAN | |
174 | +#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE | |
175 | +#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_BE | |
176 | +#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_BE | |
177 | +#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_BE | |
178 | +#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_BE | |
179 | +#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_BE | |
180 | +#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_BE | |
181 | +#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_BE | |
182 | +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE | |
183 | +#endif | |
184 | + | |
185 | +typedef int __bitwise snd_pcm_subformat_t; | |
186 | +#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) | |
187 | +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD | |
188 | + | |
189 | +#define SNDRV_PCM_INFO_MMAP 0x00000001 | |
190 | +#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 | |
191 | +#define SNDRV_PCM_INFO_DOUBLE 0x00000004 | |
192 | +#define SNDRV_PCM_INFO_BATCH 0x00000010 | |
193 | +#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 | |
194 | +#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 | |
195 | +#define SNDRV_PCM_INFO_COMPLEX 0x00000400 | |
196 | +#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000 | |
197 | +#define SNDRV_PCM_INFO_OVERRANGE 0x00020000 | |
198 | +#define SNDRV_PCM_INFO_RESUME 0x00040000 | |
199 | +#define SNDRV_PCM_INFO_PAUSE 0x00080000 | |
200 | +#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 | |
201 | +#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 | |
202 | +#define SNDRV_PCM_INFO_SYNC_START 0x00400000 | |
203 | +#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 | |
204 | + | |
205 | +typedef int __bitwise snd_pcm_state_t; | |
206 | +#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) | |
207 | +#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) | |
208 | +#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2) | |
209 | +#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3) | |
210 | +#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4) | |
211 | +#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5) | |
212 | +#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6) | |
213 | +#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7) | |
214 | +#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8) | |
215 | +#define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED | |
216 | + | |
217 | +enum { | |
218 | + SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, | |
219 | + SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, | |
220 | + SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, | |
221 | +}; | |
222 | + | |
223 | +union snd_pcm_sync_id { | |
224 | + unsigned char id[16]; | |
225 | + unsigned short id16[8]; | |
226 | + unsigned int id32[4]; | |
227 | +}; | |
228 | + | |
229 | +struct snd_pcm_info { | |
230 | + unsigned int device; | |
231 | + unsigned int subdevice; | |
232 | + int stream; | |
233 | + int card; | |
234 | + unsigned char id[64]; | |
235 | + unsigned char name[80]; | |
236 | + unsigned char subname[32]; | |
237 | + int dev_class; | |
238 | + int dev_subclass; | |
239 | + unsigned int subdevices_count; | |
240 | + unsigned int subdevices_avail; | |
241 | + union snd_pcm_sync_id sync; | |
242 | + unsigned char reserved[64]; | |
243 | +}; | |
244 | + | |
245 | +typedef int snd_pcm_hw_param_t; | |
246 | +#define SNDRV_PCM_HW_PARAM_ACCESS 0 | |
247 | +#define SNDRV_PCM_HW_PARAM_FORMAT 1 | |
248 | +#define SNDRV_PCM_HW_PARAM_SUBFORMAT 2 | |
249 | +#define SNDRV_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_ACCESS | |
250 | +#define SNDRV_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_SUBFORMAT | |
251 | + | |
252 | +#define SNDRV_PCM_HW_PARAM_SAMPLE_BITS 8 | |
253 | +#define SNDRV_PCM_HW_PARAM_FRAME_BITS 9 | |
254 | +#define SNDRV_PCM_HW_PARAM_CHANNELS 10 | |
255 | +#define SNDRV_PCM_HW_PARAM_RATE 11 | |
256 | +#define SNDRV_PCM_HW_PARAM_PERIOD_TIME 12 | |
257 | +#define SNDRV_PCM_HW_PARAM_PERIOD_SIZE 13 | |
258 | +#define SNDRV_PCM_HW_PARAM_PERIOD_BYTES 14 | |
259 | +#define SNDRV_PCM_HW_PARAM_PERIODS 15 | |
260 | +#define SNDRV_PCM_HW_PARAM_BUFFER_TIME 16 | |
261 | +#define SNDRV_PCM_HW_PARAM_BUFFER_SIZE 17 | |
262 | +#define SNDRV_PCM_HW_PARAM_BUFFER_BYTES 18 | |
263 | +#define SNDRV_PCM_HW_PARAM_TICK_TIME 19 | |
264 | +#define SNDRV_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_SAMPLE_BITS | |
265 | +#define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME | |
266 | + | |
267 | +#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) | |
268 | + | |
269 | +struct snd_interval { | |
270 | + unsigned int min, max; | |
271 | + unsigned int openmin:1, | |
272 | + openmax:1, | |
273 | + integer:1, | |
274 | + empty:1; | |
275 | +}; | |
276 | + | |
277 | +#define SNDRV_MASK_MAX 256 | |
278 | + | |
279 | +struct snd_mask { | |
280 | + __u32 bits[(SNDRV_MASK_MAX+31)/32]; | |
281 | +}; | |
282 | + | |
283 | +struct snd_pcm_hw_params { | |
284 | + unsigned int flags; | |
285 | + struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - | |
286 | + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; | |
287 | + struct snd_mask mres[5]; | |
288 | + struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - | |
289 | + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; | |
290 | + struct snd_interval ires[9]; | |
291 | + unsigned int rmask; | |
292 | + unsigned int cmask; | |
293 | + unsigned int info; | |
294 | + unsigned int msbits; | |
295 | + unsigned int rate_num; | |
296 | + unsigned int rate_den; | |
297 | + snd_pcm_uframes_t fifo_size; | |
298 | + unsigned char reserved[64]; | |
299 | +}; | |
300 | + | |
301 | +enum { | |
302 | + SNDRV_PCM_TSTAMP_NONE = 0, | |
303 | + SNDRV_PCM_TSTAMP_ENABLE, | |
304 | + SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE, | |
305 | +}; | |
306 | + | |
307 | +struct snd_pcm_sw_params { | |
308 | + int tstamp_mode; | |
309 | + unsigned int period_step; | |
310 | + unsigned int sleep_min; | |
311 | + snd_pcm_uframes_t avail_min; | |
312 | + snd_pcm_uframes_t xfer_align; | |
313 | + snd_pcm_uframes_t start_threshold; | |
314 | + snd_pcm_uframes_t stop_threshold; | |
315 | + snd_pcm_uframes_t silence_threshold; | |
316 | + snd_pcm_uframes_t silence_size; | |
317 | + snd_pcm_uframes_t boundary; | |
318 | + unsigned char reserved[64]; | |
319 | +}; | |
320 | + | |
321 | +struct snd_pcm_channel_info { | |
322 | + unsigned int channel; | |
323 | + __kernel_off_t offset; | |
324 | + unsigned int first; | |
325 | + unsigned int step; | |
326 | +}; | |
327 | + | |
328 | +struct snd_pcm_status { | |
329 | + snd_pcm_state_t state; | |
330 | + struct timespec trigger_tstamp; | |
331 | + struct timespec tstamp; | |
332 | + snd_pcm_uframes_t appl_ptr; | |
333 | + snd_pcm_uframes_t hw_ptr; | |
334 | + snd_pcm_sframes_t delay; | |
335 | + snd_pcm_uframes_t avail; | |
336 | + snd_pcm_uframes_t avail_max; | |
337 | + snd_pcm_uframes_t overrange; | |
338 | + snd_pcm_state_t suspended_state; | |
339 | + unsigned char reserved[60]; | |
340 | +}; | |
341 | + | |
342 | +struct snd_pcm_mmap_status { | |
343 | + snd_pcm_state_t state; | |
344 | + int pad1; | |
345 | + snd_pcm_uframes_t hw_ptr; | |
346 | + struct timespec tstamp; | |
347 | + snd_pcm_state_t suspended_state; | |
348 | +}; | |
349 | + | |
350 | +struct snd_pcm_mmap_control { | |
351 | + snd_pcm_uframes_t appl_ptr; | |
352 | + snd_pcm_uframes_t avail_min; | |
353 | +}; | |
354 | + | |
355 | +#define SNDRV_PCM_SYNC_PTR_HWSYNC (1<<0) | |
356 | +#define SNDRV_PCM_SYNC_PTR_APPL (1<<1) | |
357 | +#define SNDRV_PCM_SYNC_PTR_AVAIL_MIN (1<<2) | |
358 | + | |
359 | +struct snd_pcm_sync_ptr { | |
360 | + unsigned int flags; | |
361 | + union { | |
362 | + struct snd_pcm_mmap_status status; | |
363 | + unsigned char reserved[64]; | |
364 | + } s; | |
365 | + union { | |
366 | + struct snd_pcm_mmap_control control; | |
367 | + unsigned char reserved[64]; | |
368 | + } c; | |
369 | +}; | |
370 | + | |
371 | +struct snd_xferi { | |
372 | + snd_pcm_sframes_t result; | |
373 | + void __user *buf; | |
374 | + snd_pcm_uframes_t frames; | |
375 | +}; | |
376 | + | |
377 | +struct snd_xfern { | |
378 | + snd_pcm_sframes_t result; | |
379 | + void __user * __user *bufs; | |
380 | + snd_pcm_uframes_t frames; | |
381 | +}; | |
382 | + | |
383 | +enum { | |
384 | + SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, | |
385 | + SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, | |
386 | + SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, | |
387 | +}; | |
388 | + | |
389 | +#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int) | |
390 | +#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info) | |
391 | +#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int) | |
392 | +#define SNDRV_PCM_IOCTL_TTSTAMP _IOW('A', 0x03, int) | |
393 | +#define SNDRV_PCM_IOCTL_HW_REFINE _IOWR('A', 0x10, struct snd_pcm_hw_params) | |
394 | +#define SNDRV_PCM_IOCTL_HW_PARAMS _IOWR('A', 0x11, struct snd_pcm_hw_params) | |
395 | +#define SNDRV_PCM_IOCTL_HW_FREE _IO('A', 0x12) | |
396 | +#define SNDRV_PCM_IOCTL_SW_PARAMS _IOWR('A', 0x13, struct snd_pcm_sw_params) | |
397 | +#define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status) | |
398 | +#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) | |
399 | +#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) | |
400 | +#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) | |
401 | +#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) | |
402 | +#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) | |
403 | +#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41) | |
404 | +#define SNDRV_PCM_IOCTL_START _IO('A', 0x42) | |
405 | +#define SNDRV_PCM_IOCTL_DROP _IO('A', 0x43) | |
406 | +#define SNDRV_PCM_IOCTL_DRAIN _IO('A', 0x44) | |
407 | +#define SNDRV_PCM_IOCTL_PAUSE _IOW('A', 0x45, int) | |
408 | +#define SNDRV_PCM_IOCTL_REWIND _IOW('A', 0x46, snd_pcm_uframes_t) | |
409 | +#define SNDRV_PCM_IOCTL_RESUME _IO('A', 0x47) | |
410 | +#define SNDRV_PCM_IOCTL_XRUN _IO('A', 0x48) | |
411 | +#define SNDRV_PCM_IOCTL_FORWARD _IOW('A', 0x49, snd_pcm_uframes_t) | |
412 | +#define SNDRV_PCM_IOCTL_WRITEI_FRAMES _IOW('A', 0x50, struct snd_xferi) | |
413 | +#define SNDRV_PCM_IOCTL_READI_FRAMES _IOR('A', 0x51, struct snd_xferi) | |
414 | +#define SNDRV_PCM_IOCTL_WRITEN_FRAMES _IOW('A', 0x52, struct snd_xfern) | |
415 | +#define SNDRV_PCM_IOCTL_READN_FRAMES _IOR('A', 0x53, struct snd_xfern) | |
416 | +#define SNDRV_PCM_IOCTL_LINK _IOW('A', 0x60, int) | |
417 | +#define SNDRV_PCM_IOCTL_UNLINK _IO('A', 0x61) | |
418 | + | |
419 | +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) | |
420 | + | |
421 | +enum { | |
422 | + SNDRV_RAWMIDI_STREAM_OUTPUT = 0, | |
423 | + SNDRV_RAWMIDI_STREAM_INPUT, | |
424 | + SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT, | |
425 | +}; | |
426 | + | |
427 | +#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 | |
428 | +#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 | |
429 | +#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 | |
430 | + | |
431 | +struct snd_rawmidi_info { | |
432 | + unsigned int device; | |
433 | + unsigned int subdevice; | |
434 | + int stream; | |
435 | + int card; | |
436 | + unsigned int flags; | |
437 | + unsigned char id[64]; | |
438 | + unsigned char name[80]; | |
439 | + unsigned char subname[32]; | |
440 | + unsigned int subdevices_count; | |
441 | + unsigned int subdevices_avail; | |
442 | + unsigned char reserved[64]; | |
443 | +}; | |
444 | + | |
445 | +struct snd_rawmidi_params { | |
446 | + int stream; | |
447 | + size_t buffer_size; | |
448 | + size_t avail_min; | |
449 | + unsigned int no_active_sensing: 1; | |
450 | + unsigned char reserved[16]; | |
451 | +}; | |
452 | + | |
453 | +struct snd_rawmidi_status { | |
454 | + int stream; | |
455 | + struct timespec tstamp; | |
456 | + size_t avail; | |
457 | + size_t xruns; | |
458 | + unsigned char reserved[16]; | |
459 | +}; | |
460 | + | |
461 | +#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) | |
462 | +#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) | |
463 | +#define SNDRV_RAWMIDI_IOCTL_PARAMS _IOWR('W', 0x10, struct snd_rawmidi_params) | |
464 | +#define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) | |
465 | +#define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) | |
466 | +#define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int) | |
467 | + | |
468 | +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) | |
469 | + | |
470 | +enum { | |
471 | + SNDRV_TIMER_CLASS_NONE = -1, | |
472 | + SNDRV_TIMER_CLASS_SLAVE = 0, | |
473 | + SNDRV_TIMER_CLASS_GLOBAL, | |
474 | + SNDRV_TIMER_CLASS_CARD, | |
475 | + SNDRV_TIMER_CLASS_PCM, | |
476 | + SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM, | |
477 | +}; | |
478 | + | |
479 | +enum { | |
480 | + SNDRV_TIMER_SCLASS_NONE = 0, | |
481 | + SNDRV_TIMER_SCLASS_APPLICATION, | |
482 | + SNDRV_TIMER_SCLASS_SEQUENCER, | |
483 | + SNDRV_TIMER_SCLASS_OSS_SEQUENCER, | |
484 | + SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, | |
485 | +}; | |
486 | + | |
487 | +#define SNDRV_TIMER_GLOBAL_SYSTEM 0 | |
488 | +#define SNDRV_TIMER_GLOBAL_RTC 1 | |
489 | +#define SNDRV_TIMER_GLOBAL_HPET 2 | |
490 | +#define SNDRV_TIMER_GLOBAL_HRTIMER 3 | |
491 | + | |
492 | +#define SNDRV_TIMER_FLG_SLAVE (1<<0) | |
493 | + | |
494 | +struct snd_timer_id { | |
495 | + int dev_class; | |
496 | + int dev_sclass; | |
497 | + int card; | |
498 | + int device; | |
499 | + int subdevice; | |
500 | +}; | |
501 | + | |
502 | +struct snd_timer_ginfo { | |
503 | + struct snd_timer_id tid; | |
504 | + unsigned int flags; | |
505 | + int card; | |
506 | + unsigned char id[64]; | |
507 | + unsigned char name[80]; | |
508 | + unsigned long reserved0; | |
509 | + unsigned long resolution; | |
510 | + unsigned long resolution_min; | |
511 | + unsigned long resolution_max; | |
512 | + unsigned int clients; | |
513 | + unsigned char reserved[32]; | |
514 | +}; | |
515 | + | |
516 | +struct snd_timer_gparams { | |
517 | + struct snd_timer_id tid; | |
518 | + unsigned long period_num; | |
519 | + unsigned long period_den; | |
520 | + unsigned char reserved[32]; | |
521 | +}; | |
522 | + | |
523 | +struct snd_timer_gstatus { | |
524 | + struct snd_timer_id tid; | |
525 | + unsigned long resolution; | |
526 | + unsigned long resolution_num; | |
527 | + unsigned long resolution_den; | |
528 | + unsigned char reserved[32]; | |
529 | +}; | |
530 | + | |
531 | +struct snd_timer_select { | |
532 | + struct snd_timer_id id; | |
533 | + unsigned char reserved[32]; | |
534 | +}; | |
535 | + | |
536 | +struct snd_timer_info { | |
537 | + unsigned int flags; | |
538 | + int card; | |
539 | + unsigned char id[64]; | |
540 | + unsigned char name[80]; | |
541 | + unsigned long reserved0; | |
542 | + unsigned long resolution; | |
543 | + unsigned char reserved[64]; | |
544 | +}; | |
545 | + | |
546 | +#define SNDRV_TIMER_PSFLG_AUTO (1<<0) | |
547 | +#define SNDRV_TIMER_PSFLG_EXCLUSIVE (1<<1) | |
548 | +#define SNDRV_TIMER_PSFLG_EARLY_EVENT (1<<2) | |
549 | + | |
550 | +struct snd_timer_params { | |
551 | + unsigned int flags; | |
552 | + unsigned int ticks; | |
553 | + unsigned int queue_size; | |
554 | + unsigned int reserved0; | |
555 | + unsigned int filter; | |
556 | + unsigned char reserved[60]; | |
557 | +}; | |
558 | + | |
559 | +struct snd_timer_status { | |
560 | + struct timespec tstamp; | |
561 | + unsigned int resolution; | |
562 | + unsigned int lost; | |
563 | + unsigned int overrun; | |
564 | + unsigned int queue; | |
565 | + unsigned char reserved[64]; | |
566 | +}; | |
567 | + | |
568 | +#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int) | |
569 | +#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id) | |
570 | +#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int) | |
571 | +#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo) | |
572 | +#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams) | |
573 | +#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus) | |
574 | +#define SNDRV_TIMER_IOCTL_SELECT _IOW('T', 0x10, struct snd_timer_select) | |
575 | +#define SNDRV_TIMER_IOCTL_INFO _IOR('T', 0x11, struct snd_timer_info) | |
576 | +#define SNDRV_TIMER_IOCTL_PARAMS _IOW('T', 0x12, struct snd_timer_params) | |
577 | +#define SNDRV_TIMER_IOCTL_STATUS _IOR('T', 0x14, struct snd_timer_status) | |
578 | + | |
579 | +#define SNDRV_TIMER_IOCTL_START _IO('T', 0xa0) | |
580 | +#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1) | |
581 | +#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2) | |
582 | +#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3) | |
583 | + | |
584 | +struct snd_timer_read { | |
585 | + unsigned int resolution; | |
586 | + unsigned int ticks; | |
587 | +}; | |
588 | + | |
589 | +enum { | |
590 | + SNDRV_TIMER_EVENT_RESOLUTION = 0, | |
591 | + SNDRV_TIMER_EVENT_TICK, | |
592 | + SNDRV_TIMER_EVENT_START, | |
593 | + SNDRV_TIMER_EVENT_STOP, | |
594 | + SNDRV_TIMER_EVENT_CONTINUE, | |
595 | + SNDRV_TIMER_EVENT_PAUSE, | |
596 | + SNDRV_TIMER_EVENT_EARLY, | |
597 | + SNDRV_TIMER_EVENT_SUSPEND, | |
598 | + SNDRV_TIMER_EVENT_RESUME, | |
599 | + | |
600 | + SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10, | |
601 | + SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10, | |
602 | + SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10, | |
603 | + SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10, | |
604 | + SNDRV_TIMER_EVENT_MSUSPEND = SNDRV_TIMER_EVENT_SUSPEND + 10, | |
605 | + SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10, | |
606 | +}; | |
607 | + | |
608 | +struct snd_timer_tread { | |
609 | + int event; | |
610 | + struct timespec tstamp; | |
611 | + unsigned int val; | |
612 | +}; | |
613 | + | |
614 | +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) | |
615 | + | |
616 | +struct snd_ctl_card_info { | |
617 | + int card; | |
618 | + int pad; | |
619 | + unsigned char id[16]; | |
620 | + unsigned char driver[16]; | |
621 | + unsigned char name[32]; | |
622 | + unsigned char longname[80]; | |
623 | + unsigned char reserved_[16]; | |
624 | + unsigned char mixername[80]; | |
625 | + unsigned char components[128]; | |
626 | +}; | |
627 | + | |
628 | +typedef int __bitwise snd_ctl_elem_type_t; | |
629 | +#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) | |
630 | +#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) | |
631 | +#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2) | |
632 | +#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3) | |
633 | +#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4) | |
634 | +#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5) | |
635 | +#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6) | |
636 | +#define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64 | |
637 | + | |
638 | +typedef int __bitwise snd_ctl_elem_iface_t; | |
639 | +#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0) | |
640 | +#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1) | |
641 | +#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2) | |
642 | +#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3) | |
643 | +#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4) | |
644 | +#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5) | |
645 | +#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6) | |
646 | +#define SNDRV_CTL_ELEM_IFACE_LAST SNDRV_CTL_ELEM_IFACE_SEQUENCER | |
647 | + | |
648 | +#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) | |
649 | +#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) | |
650 | +#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) | |
651 | +#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) | |
652 | +#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) | |
653 | +#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) | |
654 | +#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) | |
655 | +#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) | |
656 | +#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) | |
657 | +#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) | |
658 | +#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) | |
659 | +#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) | |
660 | +#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) | |
661 | +#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) | |
662 | + | |
663 | +#define SNDRV_CTL_POWER_D0 0x0000 | |
664 | +#define SNDRV_CTL_POWER_D1 0x0100 | |
665 | +#define SNDRV_CTL_POWER_D2 0x0200 | |
666 | +#define SNDRV_CTL_POWER_D3 0x0300 | |
667 | +#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) | |
668 | +#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) | |
669 | + | |
670 | +struct snd_ctl_elem_id { | |
671 | + unsigned int numid; | |
672 | + snd_ctl_elem_iface_t iface; | |
673 | + unsigned int device; | |
674 | + unsigned int subdevice; | |
675 | + unsigned char name[44]; | |
676 | + unsigned int index; | |
677 | +}; | |
678 | + | |
679 | +struct snd_ctl_elem_list { | |
680 | + unsigned int offset; | |
681 | + unsigned int space; | |
682 | + unsigned int used; | |
683 | + unsigned int count; | |
684 | + struct snd_ctl_elem_id __user *pids; | |
685 | + unsigned char reserved[50]; | |
686 | +}; | |
687 | + | |
688 | +struct snd_ctl_elem_info { | |
689 | + struct snd_ctl_elem_id id; | |
690 | + snd_ctl_elem_type_t type; | |
691 | + unsigned int access; | |
692 | + unsigned int count; | |
693 | + __kernel_pid_t owner; | |
694 | + union { | |
695 | + struct { | |
696 | + long min; | |
697 | + long max; | |
698 | + long step; | |
699 | + } integer; | |
700 | + struct { | |
701 | + long long min; | |
702 | + long long max; | |
703 | + long long step; | |
704 | + } integer64; | |
705 | + struct { | |
706 | + unsigned int items; | |
707 | + unsigned int item; | |
708 | + char name[64]; | |
709 | + } enumerated; | |
710 | + unsigned char reserved[128]; | |
711 | + } value; | |
712 | + union { | |
713 | + unsigned short d[4]; | |
714 | + unsigned short *d_ptr; | |
715 | + } dimen; | |
716 | + unsigned char reserved[64-4*sizeof(unsigned short)]; | |
717 | +}; | |
718 | + | |
719 | +struct snd_ctl_elem_value { | |
720 | + struct snd_ctl_elem_id id; | |
721 | + unsigned int indirect: 1; | |
722 | + union { | |
723 | + union { | |
724 | + long value[128]; | |
725 | + long *value_ptr; | |
726 | + } integer; | |
727 | + union { | |
728 | + long long value[64]; | |
729 | + long long *value_ptr; | |
730 | + } integer64; | |
731 | + union { | |
732 | + unsigned int item[128]; | |
733 | + unsigned int *item_ptr; | |
734 | + } enumerated; | |
735 | + union { | |
736 | + unsigned char data[512]; | |
737 | + unsigned char *data_ptr; | |
738 | + } bytes; | |
739 | + struct snd_aes_iec958 iec958; | |
740 | + } value; | |
741 | + struct timespec tstamp; | |
742 | + unsigned char reserved[128-sizeof(struct timespec)]; | |
743 | +}; | |
744 | + | |
745 | +struct snd_ctl_tlv { | |
746 | + unsigned int numid; | |
747 | + unsigned int length; | |
748 | + unsigned int tlv[0]; | |
749 | +}; | |
750 | + | |
751 | +#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int) | |
752 | +#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info) | |
753 | +#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list) | |
754 | +#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info) | |
755 | +#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value) | |
756 | +#define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value) | |
757 | +#define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id) | |
758 | +#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id) | |
759 | +#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int) | |
760 | +#define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info) | |
761 | +#define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info) | |
762 | +#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id) | |
763 | +#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv) | |
764 | +#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv) | |
765 | +#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv) | |
766 | +#define SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE _IOWR('U', 0x20, int) | |
767 | +#define SNDRV_CTL_IOCTL_HWDEP_INFO _IOR('U', 0x21, struct snd_hwdep_info) | |
768 | +#define SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE _IOR('U', 0x30, int) | |
769 | +#define SNDRV_CTL_IOCTL_PCM_INFO _IOWR('U', 0x31, struct snd_pcm_info) | |
770 | +#define SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE _IOW('U', 0x32, int) | |
771 | +#define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int) | |
772 | +#define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info) | |
773 | +#define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int) | |
774 | +#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int) | |
775 | +#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int) | |
776 | + | |
777 | +enum sndrv_ctl_event_type { | |
778 | + SNDRV_CTL_EVENT_ELEM = 0, | |
779 | + SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM, | |
780 | +}; | |
781 | + | |
782 | +#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) | |
783 | +#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) | |
784 | +#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) | |
785 | +#define SNDRV_CTL_EVENT_MASK_TLV (1<<3) | |
786 | +#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) | |
787 | + | |
788 | +struct snd_ctl_event { | |
789 | + int type; | |
790 | + union { | |
791 | + struct { | |
792 | + unsigned int mask; | |
793 | + struct snd_ctl_elem_id id; | |
794 | + } elem; | |
795 | + unsigned char data8[60]; | |
796 | + } data; | |
797 | +}; | |
798 | + | |
799 | +#define SNDRV_CTL_NAME_NONE "" | |
800 | +#define SNDRV_CTL_NAME_PLAYBACK "Playback " | |
801 | +#define SNDRV_CTL_NAME_CAPTURE "Capture " | |
802 | + | |
803 | +#define SNDRV_CTL_NAME_IEC958_NONE "" | |
804 | +#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch" | |
805 | +#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume" | |
806 | +#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default" | |
807 | +#define SNDRV_CTL_NAME_IEC958_MASK "Mask" | |
808 | +#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask" | |
809 | +#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask" | |
810 | +#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" | |
811 | +#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what | |
812 | + | |
813 | +#endif |