blob: 50b776bfc5bbfbd09e423d686a2e544947d1c7f7 [file] [log] [blame]
Garmond Leunge2433c32017-09-28 21:51:22 -07001/*
2 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#define LOG_TAG "audio_hw_ffv"
31/*#define LOG_NDEBUG 0*/
32#define LOG_NDDEBUG 0
33/*#define VERY_VERY_VERBOSE_LOGGING*/
34#ifdef VERY_VERY_VERBOSE_LOGGING
35#define ALOGVV ALOGV
36#else
37#define ALOGVV(a...) do { } while(0)
38#endif
39
40#include <errno.h>
41#include <cutils/properties.h>
42#include <stdlib.h>
43#include <dlfcn.h>
44#include <cutils/str_parms.h>
45#include <cutils/log.h>
46#include <pthread.h>
47#include <sys/resource.h>
48
49#include "audio_hw.h"
50#include "platform.h"
51#include "platform_api.h"
52
53#include "ffv_interface.h"
54
55#define AUDIO_PARAMETER_FFV_MODE_ON "ffvOn"
56#define AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA "ffv_split_ec_ref_data"
57#define AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT "ffv_ec_ref_channel_count"
58#define AUDIO_PARAMETER_FFV_EC_REF_DEVICE "ffv_ec_ref_dev"
59#define AUDIO_PARAMETER_FFV_CHANNEL_INDEX "ffv_channel_index"
60
61#define FFV_LIB "libffv.so"
62#define FFV_CONFIG_FILE_PATH "/etc/BF_1out.cfg"
63#define FFV_SAMPLING_RATE_16000 16000
64#define FFV_EC_REF_LOOPBACK_DEVICE_MONO "ec-ref-loopback-mono"
65#define FFV_EC_REF_LOOPBACK_DEVICE_STEREO "ec-ref-loopback-stereo"
66
67#define FFV_CHANNEL_MODE_MONO 1
68#define FFV_CHANNEL_MODE_STEREO 2
69#define FFV_CHANNEL_MODE_HEX 6
70#define FFV_CHANNEL_MODE_OCT 8
71
72#define FFV_PCM_BUFFER_DURATION_MS 160
73#define FFV_PCM_PERIOD_COUNT (8)
74#define FFV_PCM_PERIOD_SIZE \
75 ((((FFV_SAMPLING_RATE_16000 * FFV_PCM_BUFFER_DURATION_MS) \
76 /(FFV_PCM_PERIOD_COUNT * 1000)) + 0x1f) & ~0x1f)
77
78#define ALIGN(number, align) \
79 ((number + align - 1) & ~(align - 1))
80#define CALCULATE_PERIOD_SIZE(duration_ms, sample_rate, period_cnt, align) \
81 (ALIGN(((sample_rate * duration_ms) /(period_cnt * 1000)), align))
82
83#define FFV_PCM_MAX_RETRY 10
84#define FFV_PCM_SLEEP_WAIT 1000
85
86#define DLSYM(handle, name, err) \
87do {\
88 const char* error; \
89 *(void**)&name##_fn = dlsym(handle, #name);\
90 if ((error = dlerror())) {\
91 ALOGE("%s: dlsym failed for %s error %s", __func__, #name, error);\
92 err = -ENODEV;\
93 }\
94} while(0)\
95
96/* uncomment to collect pcm dumps */
97//#define FFV_PCM_DUMP
98
99static FfvStatusType (*ffv_init_fn)(void** handle, int num_tx_in_ch,
100 int num_out_ch, int num_ec_ref_ch, int frame_len, int sample_rate,
101 const char *config_file_name, char *svaModelBuffer,
102 uint32_t svaModelSize, int* totMemSize);
103static void (*ffv_deinit_fn)(void* handle);
104static void (*ffv_process_fn)(void *handle, const int16_t *in_pcm,
105 int16_t *out_pcm, const int16_t *ec_ref_pcm);
106static int (*ffv_read_fn)(void* handle, int16_t *buf_pcm,
107 int max_buf_len);
108static FfvStatusType (*ffv_get_param_fn)(void *handle, char *params_buffer_ptr,
109 int param_id, int buffer_size, int *param_size_ptr);
110static FfvStatusType (*ffv_set_param_fn)(void *handle, char *params_buffer_ptr,
111 int param_id, int param_size);
112static FfvStatusType (*ffv_register_event_callback_fn)(void *handle,
113 ffv_event_callback_fn_t *fun_ptr);
114
115struct ffvmodule {
116 void *ffv_lib_handle;
117 unsigned char *in_buf;
118 unsigned int in_buf_size;
119 unsigned char *ec_ref_buf;
120 unsigned int ec_ref_buf_size;
121 unsigned char *split_in_buf;
122 unsigned int split_in_buf_size;
123 unsigned char *out_buf;
124 unsigned int out_buf_size;
125
126 struct pcm_config capture_config;
127 struct pcm_config out_config;
128 struct pcm_config ec_ref_config;
129
130 int ec_ref_pcm_id;
131 struct pcm *ec_ref_pcm;
132 int ec_ref_ch_cnt;
133 audio_devices_t ec_ref_dev;
134 bool split_ec_ref_data;
135
136 bool is_ffv_enabled;
137 bool buffers_allocated;
138 struct stream_in *in;
139 bool is_ffvmode_on;
140 void *handle;
141 pthread_mutex_t init_lock;
142 bool capture_started;
143 int target_ch_idx;
144
145#ifdef FFV_PCM_DUMP
146 FILE *fp_input;
147 FILE *fp_ecref;
148 FILE *fp_split_input;
149 FILE *fp_output;
150#endif
151};
152
153static struct ffvmodule ffvmod = {
154 .ffv_lib_handle = NULL,
155 .in_buf = NULL,
156 .in_buf_size = 0,
157 .ec_ref_buf = NULL,
158 .ec_ref_buf_size = 0,
159 .split_in_buf = NULL,
160 .split_in_buf_size = 0,
161 .out_buf = NULL,
162 .out_buf_size = 0,
163
164 .ec_ref_pcm = NULL,
165 .ec_ref_ch_cnt = 1,
166 .ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER,
167 .is_ffv_enabled = false,
168 .buffers_allocated = false,
169 .in = NULL,
170 .is_ffvmode_on = false,
171 .handle = NULL,
172 .capture_started = false,
173 .target_ch_idx = -1,
174};
175
176static struct pcm_config ffv_pcm_config = {
177 .channels = FFV_CHANNEL_MODE_MONO,
178 .rate = FFV_SAMPLING_RATE_16000,
179 .period_size = FFV_PCM_PERIOD_SIZE,
180 .period_count = FFV_PCM_PERIOD_COUNT,
181 .format = PCM_FORMAT_S16_LE,
182};
183
184static int32_t ffv_init_lib()
185{
186 int status = 0;
187
188 if (ffvmod.ffv_lib_handle) {
189 ALOGE("%s: FFV library is already initialized", __func__);
190 return 0;
191 }
192
193 ffvmod.ffv_lib_handle = dlopen(FFV_LIB, RTLD_NOW);
194 if (!ffvmod.ffv_lib_handle) {
195 ALOGE("%s: Unable to open %s, error %s", __func__, FFV_LIB,
196 dlerror());
197 status = -ENOENT;
198 goto exit;
199 }
200
201 dlerror(); /* clear errors */
202 DLSYM(ffvmod.ffv_lib_handle, ffv_init, status);
203 if (status)
204 goto exit;
205 DLSYM(ffvmod.ffv_lib_handle, ffv_deinit, status);
206 if (status)
207 goto exit;
208 DLSYM(ffvmod.ffv_lib_handle, ffv_process, status);
209 if (status)
210 goto exit;
211 DLSYM(ffvmod.ffv_lib_handle, ffv_read, status);
212 if (status)
213 goto exit;
214 DLSYM(ffvmod.ffv_lib_handle, ffv_get_param, status);
215 if (status)
216 goto exit;
217 DLSYM(ffvmod.ffv_lib_handle, ffv_set_param, status);
218 if (status)
219 goto exit;
220 DLSYM(ffvmod.ffv_lib_handle, ffv_register_event_callback, status);
221 if (status)
222 goto exit;
223
224 return status;
225
226exit:
227 if (ffvmod.ffv_lib_handle)
228 dlclose(ffvmod.ffv_lib_handle);
229 ffvmod.ffv_lib_handle = NULL;
230
231 return status;
232}
233
234static int deallocate_buffers()
235{
236 if (ffvmod.in_buf) {
237 free(ffvmod.in_buf);
238 ffvmod.in_buf = NULL;
239 }
240
241 if (ffvmod.split_in_buf) {
242 free(ffvmod.split_in_buf);
243 ffvmod.split_in_buf = NULL;
244 }
245
246 if (ffvmod.ec_ref_buf) {
247 free(ffvmod.ec_ref_buf);
248 ffvmod.ec_ref_buf = NULL;
249 }
250
251 if (ffvmod.out_buf) {
252 free(ffvmod.out_buf);
253 ffvmod.out_buf = NULL;
254 }
255
256 ffvmod.buffers_allocated = false;
257 return 0;
258}
259
260static int allocate_buffers()
261{
262 int status = 0;
263
264 /* in_buf - buffer read from capture session */
265 ffvmod.in_buf_size = ffvmod.capture_config.period_size * ffvmod.capture_config.channels *
266 (pcm_format_to_bits(ffvmod.capture_config.format) >> 3);
267 ffvmod.in_buf = (unsigned char *)calloc(1, ffvmod.in_buf_size);
268 if (!ffvmod.in_buf) {
269 ALOGE("%s: ERROR. Can not allocate in buffer size %d", __func__, ffvmod.in_buf_size);
270 status = -ENOMEM;
271 goto error_exit;
272 }
273 ALOGD("%s: Allocated in buffer size bytes =%d",
274 __func__, ffvmod.in_buf_size);
275
276 /* ec_buf - buffer read from ec ref capture session */
277 ffvmod.ec_ref_buf_size = ffvmod.ec_ref_config.period_size * ffvmod.ec_ref_config.channels *
278 (pcm_format_to_bits(ffvmod.ec_ref_config.format) >> 3);
279 ffvmod.ec_ref_buf = (unsigned char *)calloc(1, ffvmod.ec_ref_buf_size);
280 if (!ffvmod.ec_ref_buf) {
281 ALOGE("%s: ERROR. Can not allocate ec ref buffer size %d",
282 __func__, ffvmod.ec_ref_buf_size);
283 status = -ENOMEM;
284 goto error_exit;
285 }
286 ALOGD("%s: Allocated ec ref buffer size bytes =%d",
287 __func__, ffvmod.ec_ref_buf_size);
288
289 if (ffvmod.split_ec_ref_data) {
290 ffvmod.split_in_buf_size = ffvmod.in_buf_size - ffvmod.ec_ref_buf_size;
291 ffvmod.split_in_buf = (unsigned char *)calloc(1, ffvmod.split_in_buf_size);
292 if (!ffvmod.split_in_buf) {
293 ALOGE("%s: ERROR. Can not allocate split in buffer size %d",
294 __func__, ffvmod.split_in_buf_size);
295 status = -ENOMEM;
296 goto error_exit;
297 }
298 ALOGD("%s: Allocated split in buffer size bytes =%d",
299 __func__, ffvmod.split_in_buf_size);
300 }
301
302 /* out_buf - output buffer from FFV + SVA library */
303 ffvmod.out_buf_size = ffvmod.out_config.period_size * ffvmod.out_config.channels *
304 (pcm_format_to_bits(ffvmod.out_config.format) >> 3);
305 ffvmod.out_buf = (unsigned char *)calloc(1, ffvmod.out_buf_size);
306 if (!ffvmod.out_buf) {
307 ALOGE("%s: ERROR. Can not allocate out buffer size %d", __func__, ffvmod.out_buf_size);
308 status = -ENOMEM;
309 goto error_exit;
310 }
311 ALOGD("%s: Allocated out buffer size bytes =%d",
312 __func__, ffvmod.out_buf_size);
313
314 ffvmod.buffers_allocated = true;
315 return 0;
316
317error_exit:
318 deallocate_buffers();
319 return status;
320}
321
322void audio_extn_ffv_update_enabled()
323{
324 char ffv_enabled[PROPERTY_VALUE_MAX] = "false";
325
326 property_get("ro.qc.sdk.audio.ffv", ffv_enabled, "0");
327 if (!strncmp("true", ffv_enabled, 4)) {
328 ALOGD("%s: ffv is supported", __func__);
329 ffvmod.is_ffv_enabled = true;
330 } else {
331 ALOGD("%s: ffv is not supported", __func__);
332 ffvmod.is_ffv_enabled = false;
333 }
334}
335
336bool audio_extn_ffv_get_enabled()
337{
338 ALOGV("%s: is_ffv_enabled:%d is_ffvmode_on:%d ", __func__, ffvmod.is_ffv_enabled, ffvmod.is_ffvmode_on);
339
340 if(ffvmod.is_ffv_enabled && ffvmod.is_ffvmode_on)
341 return true;
342
343 return false;
344}
345
346bool audio_extn_ffv_check_usecase(struct stream_in *in) {
347 int ret = false;
348 int channel_count = audio_channel_count_from_in_mask(in->channel_mask);
349 audio_devices_t devices = in->device;
350 audio_source_t source = in->source;
351
352 if ((audio_extn_ffv_get_enabled()) &&
353 (channel_count == 1) &&
354 (AUDIO_SOURCE_MIC == source) &&
355 ((AUDIO_DEVICE_IN_BUILTIN_MIC == devices) || (AUDIO_DEVICE_IN_BACK_MIC == devices)) &&
356 (in->format == AUDIO_FORMAT_PCM_16_BIT) &&
357 (in->sample_rate == FFV_SAMPLING_RATE_16000)) {
358 in->config.channels = channel_count;
359 in->config.period_count = FFV_PCM_PERIOD_COUNT;
360 in->config.period_size = FFV_PCM_PERIOD_SIZE;
361 ALOGD("%s: FFV enabled", __func__);
362 ret = true;
363 }
364 return ret;
365}
366
367int audio_extn_ffv_set_usecase(struct stream_in *in)
368{
369 int ret = -EINVAL;
370
371 if (audio_extn_ffv_check_usecase(in)) {
372 if (!audio_extn_ffv_stream_init(in)) {
373 ALOGD("%s: Created FFV session succesfully", __func__);
374 ret = 0;
375 } else {
376 ALOGE("%s: Unable to start FFV record session", __func__);
377 }
378 }
379 return ret;
380}
381
382struct stream_in *audio_extn_ffv_get_stream()
383{
384 return ffvmod.in;
385}
386
387void audio_extn_ffv_update_pcm_config(struct pcm_config *config)
388{
389 config->channels = ffvmod.capture_config.channels;
390 config->period_count = ffvmod.capture_config.period_count;
391 config->period_size = ffvmod.capture_config.period_size;
392}
393
394int32_t audio_extn_ffv_init(struct audio_device *adev)
395{
396 int ret = 0;
397
398 ret = ffv_init_lib();
399 if (ret)
400 ALOGE("%s: ERROR. ffv_init_lib ret %d", __func__, ret);
401
402 pthread_mutex_init(&ffvmod.init_lock, NULL);
403 return ret;
404}
405
406int32_t audio_extn_ffv_deinit()
407{
408 pthread_mutex_destroy(&ffvmod.init_lock);
409 if (ffvmod.ffv_lib_handle) {
410 dlclose(ffvmod.ffv_lib_handle);
411 ffvmod.ffv_lib_handle = NULL;
412 }
413 return 0;
414}
415
416int32_t audio_extn_ffv_stream_init(struct stream_in *in)
417{
418 uint32_t ret = -EINVAL;
419 int num_tx_in_ch, num_out_ch, num_ec_ref_ch;
420 int frame_len;
421 int sample_rate;
422 const char *config_file_path = FFV_CONFIG_FILE_PATH;
423 int total_mem_size;
424 FfvStatusType status_type;
425 const char *sm_buffer = "DISABLE_KEYWORD_DETECTION";
426 ffv_target_channel_index_param_t ch_index_param;
427 char *params_buffer_ptr = NULL;
428 int param_size = 0;
429 int param_id;
430
431 if (!audio_extn_ffv_get_enabled()) {
432 ALOGE("Rejecting FFV -- init is called without enabling FFV");
433 goto fail;
434 }
435
436 if (ffvmod.handle != NULL) {
437 ALOGV("%s: reinitializing ffv library", __func__);
438 audio_extn_ffv_stream_deinit();
439 }
440
441 ffvmod.capture_config = ffv_pcm_config;
442 ffvmod.ec_ref_config = ffv_pcm_config;
443 ffvmod.out_config = ffv_pcm_config;
444 /* configure capture session with 6/8 channels */
445 ffvmod.capture_config.channels = ffvmod.split_ec_ref_data ?
446 FFV_CHANNEL_MODE_OCT : FFV_CHANNEL_MODE_HEX;
447 ffvmod.capture_config.period_size =
448 CALCULATE_PERIOD_SIZE(FFV_PCM_BUFFER_DURATION_MS,
449 ffvmod.capture_config.rate,
450 FFV_PCM_PERIOD_COUNT, 32);
451
452 /* Update channels with ec ref channel count */
453 ffvmod.ec_ref_config.channels = ffvmod.ec_ref_ch_cnt;
454 ffvmod.ec_ref_config.period_size =
455 CALCULATE_PERIOD_SIZE(FFV_PCM_BUFFER_DURATION_MS,
456 ffvmod.ec_ref_config.rate,
457 FFV_PCM_PERIOD_COUNT, 32);
458 ret = allocate_buffers();
459 if (ret)
460 goto fail;
461
462 num_ec_ref_ch = ffvmod.ec_ref_config.channels;
463 num_tx_in_ch = ffvmod.split_ec_ref_data ?
Chaithanya Krishna Bacharaju4cdd5242017-09-18 14:00:17 +0530464 (ffvmod.capture_config.channels - num_ec_ref_ch) :
465 ffvmod.capture_config.channels;
Garmond Leunge2433c32017-09-28 21:51:22 -0700466 num_out_ch = ffvmod.out_config.channels;
467 frame_len = ffvmod.capture_config.period_size;
468 sample_rate = ffvmod.capture_config.rate;
469
470 ALOGD("%s: ec_ref_ch %d, tx_in_ch %d, out_ch %d, frame_len %d, sample_rate %d",
471 __func__, num_ec_ref_ch, num_tx_in_ch, num_out_ch, frame_len, sample_rate);
472 ALOGD("%s: config file path %s", __func__, config_file_path);
473 status_type = ffv_init_fn(&ffvmod.handle, num_tx_in_ch, num_out_ch, num_ec_ref_ch,
474 frame_len, sample_rate, config_file_path, sm_buffer, 0,
475 &total_mem_size);
476 if (status_type) {
477 ALOGE("%s: ERROR. ffv_init returned %d", __func__, status_type);
478 ret = -EINVAL;
479 goto fail;
480 }
481 ALOGD("%s: ffv_init success %p", __func__, ffvmod.handle);
482
483 /* set target channel index if received as part of setparams */
484 if (ffvmod.target_ch_idx != -1) {
485 ALOGD("%s: target channel index %d", __func__, ffvmod.target_ch_idx);
486 ch_index_param.target_chan_idx = ffvmod.target_ch_idx;
487 params_buffer_ptr = (char *)&ch_index_param;
488 param_size = sizeof(ch_index_param);
489 param_id = FFV_TARGET_CHANNEL_INDEX_PARAM;
490 status_type = ffv_set_param_fn(ffvmod.handle, params_buffer_ptr,
491 param_id, param_size);
492 if (status_type) {
493 ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
494 ret = -EINVAL;
495 goto fail;
496 }
497 }
498
499 ffvmod.in = in;
500
501#ifdef FFV_PCM_DUMP
502 if (!ffvmod.fp_input) {
503 ALOGD("%s: Opening input dump file \n", __func__);
504 ffvmod.fp_input = fopen("/data/misc/audio/ffv_input.pcm", "wb");
505 }
506 if (!ffvmod.fp_ecref) {
507 ALOGD("%s: Opening ecref dump file \n", __func__);
508 ffvmod.fp_ecref = fopen("/data/misc/audio/ffv_ecref.pcm", "wb");
509 }
510 if (!ffvmod.fp_split_input && ffvmod.split_ec_ref_data) {
511 ALOGD("%s: Opening split input dump file \n", __func__);
512 ffvmod.fp_split_input = fopen("/data/misc/audio/ffv_split_input.pcm", "wb");
513 }
514 if (!ffvmod.fp_output) {
515 ALOGD("%s: Opening output dump file \n", __func__);
516 ffvmod.fp_output = fopen("/data/misc/audio/ffv_output.pcm", "wb");
517 }
518#endif
519 ALOGV("%s: exit", __func__);
520 return 0;
521
522fail:
523 audio_extn_ffv_stream_deinit();
524 return ret;
525}
526
527int32_t audio_extn_ffv_stream_deinit()
528{
529 ALOGV("%s: entry", __func__);
530
531#ifdef FFV_PCM_DUMP
532 if (ffvmod.fp_input)
533 fclose(ffvmod.fp_input);
534
535 if (ffvmod.fp_ecref)
536 fclose(ffvmod.fp_ecref);
537
538 if (ffvmod.fp_split_input)
539 fclose(ffvmod.fp_split_input);
540
541 if (ffvmod.fp_output)
542 fclose(ffvmod.fp_output);
543#endif
544
545 if (ffvmod.handle)
546 ffv_deinit_fn(ffvmod.handle);
547
548 if (ffvmod.buffers_allocated)
549 deallocate_buffers();
550
Chaithanya Krishna Bacharaju4cdd5242017-09-18 14:00:17 +0530551 ffvmod.handle = NULL;
Garmond Leunge2433c32017-09-28 21:51:22 -0700552 ffvmod.in = NULL;
553 ALOGV("%s: exit", __func__);
554 return 0;
555}
556
557snd_device_t audio_extn_ffv_get_capture_snd_device()
558{
559 if (ffvmod.capture_config.channels == FFV_CHANNEL_MODE_OCT) {
560 return SND_DEVICE_IN_HANDSET_8MIC;
561 } else if (ffvmod.capture_config.channels == FFV_CHANNEL_MODE_HEX) {
562 return SND_DEVICE_IN_HANDSET_6MIC;
563 } else {
564 ALOGE("%s: Invalid channels configured for capture", __func__);
565 return SND_DEVICE_NONE;
566 }
567}
568
569int audio_extn_ffv_init_ec_ref_loopback(struct audio_device *adev,
570 snd_device_t snd_device)
571{
572 struct audio_usecase *uc_info_tx = NULL;
573 snd_device_t in_snd_device;
574 char *params_buffer_ptr = NULL;
575 int param_id = FFV_RESET_AEC_PARAM;
576 int param_size = 0;
577 FfvStatusType status_type;
578 int ret = 0;
579
580 ALOGV("%s: entry", __func__);
581 /* notify library to reset AEC during each start */
582 status_type = ffv_set_param_fn(ffvmod.handle, params_buffer_ptr,
583 param_id, param_size);
584 if (status_type) {
585 ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
586 return -EINVAL;
587 }
588
589 if (ffvmod.split_ec_ref_data) {
590 ALOGV("%s: Ignore ec ref loopback init", __func__);
591 return 0;
592 }
593
594 in_snd_device = platform_get_ec_ref_loopback_snd_device(ffvmod.ec_ref_ch_cnt);
595 uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
596 if (!uc_info_tx) {
597 return -ENOMEM;
598 }
599
600 pthread_mutex_lock(&ffvmod.init_lock);
601 uc_info_tx->id = USECASE_AUDIO_EC_REF_LOOPBACK;
602 uc_info_tx->type = PCM_CAPTURE;
603 uc_info_tx->in_snd_device = in_snd_device;
604 uc_info_tx->out_snd_device = SND_DEVICE_NONE;
605 ffvmod.ec_ref_pcm = NULL;
606 list_add_tail(&adev->usecase_list, &uc_info_tx->list);
607 enable_snd_device(adev, in_snd_device);
608 enable_audio_route(adev, uc_info_tx);
609
610 ffvmod.ec_ref_pcm_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
611 if (ffvmod.ec_ref_pcm_id < 0) {
612 ALOGE("%s: Invalid pcm device for usecase (%d)",
613 __func__, uc_info_tx->id);
614 ret = -ENODEV;
615 goto exit;
616 }
617
Chaithanya Krishna Bacharaju4cdd5242017-09-18 14:00:17 +0530618 ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d format %d",
619 __func__, adev->snd_card, ffvmod.ec_ref_pcm_id, ffvmod.ec_ref_config.channels,
620 ffvmod.ec_ref_config.format);
Garmond Leunge2433c32017-09-28 21:51:22 -0700621 ffvmod.ec_ref_pcm = pcm_open(adev->snd_card,
622 ffvmod.ec_ref_pcm_id,
623 PCM_IN, &ffvmod.ec_ref_config);
624 if (ffvmod.ec_ref_pcm && !pcm_is_ready(ffvmod.ec_ref_pcm)) {
625 ALOGE("%s: %s", __func__, pcm_get_error(ffvmod.ec_ref_pcm));
626 ret = -EIO;
627 goto exit;
628 }
629
Chaithanya Krishna Bacharaju4cdd5242017-09-18 14:00:17 +0530630 ALOGV("%s: pcm_prepare", __func__);
Garmond Leunge2433c32017-09-28 21:51:22 -0700631 if (pcm_prepare(ffvmod.ec_ref_pcm) < 0) {
632 ALOGE("%s: pcm prepare for ec ref loopback failed", __func__);
633 ret = -EINVAL;
634 }
635
636 ffvmod.capture_started = false;
637 pthread_mutex_unlock(&ffvmod.init_lock);
638 ALOGV("%s: exit", __func__);
639 return 0;
640
641exit:
642 if (ffvmod.ec_ref_pcm) {
643 pcm_close(ffvmod.ec_ref_pcm);
644 ffvmod.ec_ref_pcm = NULL;
645 }
646 list_remove(&uc_info_tx->list);
647 disable_snd_device(adev, in_snd_device);
648 disable_audio_route(adev, uc_info_tx);
649 free(uc_info_tx);
650 pthread_mutex_unlock(&ffvmod.init_lock);
651 return ret;
652}
653
654void audio_extn_ffv_append_ec_ref_dev_name(char *device_name)
655{
Chaithanya Krishna Bacharaju95de8e22017-10-17 15:25:33 +0530656 if (ffvmod.ec_ref_dev == AUDIO_DEVICE_OUT_LINE)
Garmond Leunge2433c32017-09-28 21:51:22 -0700657 strlcat(device_name, " lineout", DEVICE_NAME_MAX_SIZE);
658 ALOGD("%s: ec ref dev name %s", __func__, device_name);
659}
660
661int audio_extn_ffv_deinit_ec_ref_loopback(struct audio_device *adev,
662 snd_device_t snd_device)
663{
664 struct audio_usecase *uc_info_tx = NULL;
665 snd_device_t in_snd_device;
666 int ret = 0;
667
668 ALOGV("%s: entry", __func__);
669 if (ffvmod.split_ec_ref_data) {
670 ALOGV("%s: Ignore ec ref loopback init", __func__);
671 return 0;
672 }
673
674 in_snd_device = platform_get_ec_ref_loopback_snd_device(ffvmod.ec_ref_ch_cnt);
675 uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_EC_REF_LOOPBACK);
676 pthread_mutex_lock(&ffvmod.init_lock);
677 if (ffvmod.ec_ref_pcm) {
678 pcm_close(ffvmod.ec_ref_pcm);
679 ffvmod.ec_ref_pcm = NULL;
680 }
681 disable_snd_device(adev, in_snd_device);
682 if (uc_info_tx) {
683 list_remove(&uc_info_tx->list);
684 disable_audio_route(adev, uc_info_tx);
685 free(uc_info_tx);
686 }
687 pthread_mutex_unlock(&ffvmod.init_lock);
688 ALOGV("%s: exit", __func__);
689 return ret;
690}
691
692int32_t audio_extn_ffv_read(struct audio_stream_in *stream,
693 void *buffer, size_t bytes)
694{
695 int status = 0;
696 int16_t *in_ptr = NULL, *process_in_ptr = NULL, *process_out_ptr = NULL;
697 int16_t *process_ec_ref_ptr = NULL;
698 size_t in_buf_size, out_buf_size, bytes_to_copy;
699 int retry_num = 0;
700 int i, j, ch;
701 int total_in_ch, in_ch, ec_ref_ch;
702
703 if (!ffvmod.ffv_lib_handle) {
704 ALOGE("%s: ffv_lib_handle not initialized", __func__);
705 return -EINVAL;
706 }
707
708 if (!ffvmod.handle) {
709 ALOGE("%s: ffv module handle not initialized", __func__);
710 return -EINVAL;
711 }
712
713 if (!ffvmod.in || !ffvmod.in->pcm) {
714 ALOGE("%s: capture session not initiliazed", __func__);
715 return -EINVAL;
716 }
717
718 if (!ffvmod.split_ec_ref_data && !ffvmod.ec_ref_pcm) {
719 ALOGE("%s: ec ref session not initiliazed", __func__);
720 return -EINVAL;
721 }
722
723 if (!ffvmod.capture_started) {
724 /* pcm_start of capture and ec ref session before read to reduce drift */
725 pcm_start(ffvmod.in->pcm);
726 while (status && (retry_num < FFV_PCM_MAX_RETRY)) {
727 usleep(FFV_PCM_SLEEP_WAIT);
728 retry_num++;
729 ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
730 __func__, status, errno, retry_num);
731 status = pcm_start(ffvmod.in->pcm);
732 }
733 if (status) {
734 ALOGE("%s: ERROR. pcm_start failed, returned status %d - %s",
735 __func__, status, pcm_get_error(ffvmod.in->pcm));
736 return status;
737 }
738 retry_num = 0;
739
740 if (!ffvmod.split_ec_ref_data) {
741 pcm_start(ffvmod.ec_ref_pcm);
742 while (status && (retry_num < FFV_PCM_MAX_RETRY)) {
743 usleep(FFV_PCM_SLEEP_WAIT);
744 retry_num++;
745 ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
746 __func__, status, errno, retry_num);
747 status = pcm_start(ffvmod.ec_ref_pcm);
748 }
749 if (status) {
750 ALOGE("%s: ERROR. pcm_start failed, returned status %d - %s",
751 __func__, status, pcm_get_error(ffvmod.ec_ref_pcm));
752 return status;
753 }
754 }
755 ffvmod.capture_started = true;
756 }
757
758 ALOGVV("%s: pcm_read reading bytes=%d", __func__, ffvmod.in_buf_size);
759 status = pcm_read(ffvmod.in->pcm, ffvmod.in_buf, ffvmod.in_buf_size);
760 if (status) {
761 ALOGE("%s: pcm read failed status %d - %s", __func__, status,
762 pcm_get_error(ffvmod.in->pcm));
763 goto exit;
764 }
765 ALOGVV("%s: pcm_read done", __func__);
766
767 if (!ffvmod.split_ec_ref_data) {
768 /* read EC ref data */
769 ALOGVV("%s: ec ref pcm_read reading bytes=%d", __func__, ffvmod.ec_ref_buf_size);
770 status = pcm_read(ffvmod.ec_ref_pcm, ffvmod.ec_ref_buf, ffvmod.ec_ref_buf_size);
771 if (status) {
772 ALOGE("%s: ec ref pcm read failed status %d - %s", __func__, status,
773 pcm_get_error(ffvmod.ec_ref_pcm));
774 goto exit;
775 }
776 ALOGVV("%s: ec ref pcm_read done", __func__);
777 process_in_ptr = (int16_t *)ffvmod.in_buf;
778 process_ec_ref_ptr = (int16_t *)ffvmod.ec_ref_buf;
779 in_buf_size = ffvmod.in_buf_size;
780 } else {
781 /* split input buffer into actual input channels and EC ref channels */
782 in_ptr = (int16_t *)ffvmod.in_buf;
783 process_in_ptr = (int16_t *)ffvmod.split_in_buf;
784 process_ec_ref_ptr = (int16_t *)ffvmod.ec_ref_buf;
785 total_in_ch = ffvmod.capture_config.channels;
786 ec_ref_ch = ffvmod.ec_ref_config.channels;
787 in_ch = total_in_ch - ec_ref_ch;
788 for (i = 0; i < ffvmod.capture_config.period_size; i++) {
789 for (ch = 0; ch < in_ch; ch++) {
790 process_in_ptr[i*in_ch+ch] =
791 in_ptr[i*total_in_ch+ch];
792 }
793 for (ch = 0; ch < ec_ref_ch; ch++) {
794 process_ec_ref_ptr[i*ec_ref_ch+ch] =
795 in_ptr[i*total_in_ch+in_ch+ch];
796 }
797 }
798 in_buf_size = ffvmod.split_in_buf_size;
799 }
800 process_out_ptr = (int16_t *)ffvmod.out_buf;
801
802 ffv_process_fn(ffvmod.handle, process_in_ptr,
803 process_out_ptr, process_ec_ref_ptr);
804 out_buf_size = ffvmod.out_buf_size;
805 bytes_to_copy = (bytes <= out_buf_size) ? bytes : out_buf_size;
806 memcpy(buffer, process_out_ptr, bytes_to_copy);
807 if (bytes_to_copy != out_buf_size)
808 ALOGD("%s: out buffer data dropped, copied %d bytes",
809 __func__, bytes_to_copy);
810
811#ifdef FFV_PCM_DUMP
812 if (ffvmod.fp_input)
813 fwrite(ffvmod.in_buf, 1, ffvmod.in_buf_size, ffvmod.fp_input);
814 if (ffvmod.fp_ecref)
815 fwrite(ffvmod.ec_ref_buf, 1, ffvmod.ec_ref_buf_size, ffvmod.fp_ecref);
816 if (ffvmod.fp_split_input)
817 fwrite(ffvmod.split_in_buf, 1, ffvmod.split_in_buf_size, ffvmod.fp_split_input);
818 if (ffvmod.fp_output)
819 fwrite(process_out_ptr, 1, bytes_to_copy, ffvmod.fp_output);
820#endif
821
822exit:
823 return status;
824}
825
826void audio_extn_ffv_set_parameters(struct audio_device *adev __unused,
827 struct str_parms *parms)
828{
829 int err;
830 int val;
831 int ret = 0;
832 char value[128];
833
834 /* FFV params are required to be set before start of recording */
835 if (!ffvmod.handle) {
836 ret = str_parms_get_str(parms, AUDIO_PARAMETER_FFV_MODE_ON, value,
837 sizeof(value));
838 if (ret >= 0) {
839 str_parms_del(parms, AUDIO_PARAMETER_FFV_MODE_ON);
840 if (strcmp(value, "true") == 0) {
841 ALOGD("%s: Setting FFV mode to true", __func__);
842 ffvmod.is_ffvmode_on = true;
843 } else {
844 ALOGD("%s: Resetting FFV mode to false", __func__);
845 ffvmod.is_ffvmode_on = false;
846 }
847 }
848
849 ret = str_parms_get_str(parms, AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA, value,
850 sizeof(value));
851 if (ret >= 0) {
852 str_parms_del(parms, AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA);
853 if (strcmp(value, "true") == 0) {
854 ALOGD("%s: ec ref is packed with mic captured data", __func__);
855 ffvmod.split_ec_ref_data = true;
856 } else {
857 ALOGD("%s: ec ref is captured separately", __func__);
858 ffvmod.split_ec_ref_data = false;
859 }
860 }
861 ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT, &val);
862 if (ret >= 0) {
863 str_parms_del(parms, AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT);
864 if (val == 1) {
865 ALOGD("%s: mono ec ref", __func__);
866 ffvmod.ec_ref_ch_cnt = FFV_CHANNEL_MODE_MONO;
867 } else if (val == 2) {
868 ALOGD("%s: stereo ec ref", __func__);
869 ffvmod.ec_ref_ch_cnt = FFV_CHANNEL_MODE_STEREO;
870 } else {
871 ALOGE("%s: Invalid ec ref", __func__);
872 }
873 }
874
875 ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE, &val);
876 if (ret >= 0) {
877 str_parms_del(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE);
878 if (val & AUDIO_DEVICE_OUT_SPEAKER) {
879 ALOGD("%s: capture ec ref from speaker", __func__);
880 ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER;
Chaithanya Krishna Bacharaju95de8e22017-10-17 15:25:33 +0530881 } else if (val & AUDIO_DEVICE_OUT_LINE) {
Garmond Leunge2433c32017-09-28 21:51:22 -0700882 ALOGD("%s: capture ec ref from line out", __func__);
Chaithanya Krishna Bacharaju95de8e22017-10-17 15:25:33 +0530883 ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_LINE;
Garmond Leunge2433c32017-09-28 21:51:22 -0700884 } else {
885 ALOGE("%s: Invalid ec ref out device", __func__);
886 }
887 }
888
889 ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_CHANNEL_INDEX, &val);
890 if (ret >= 0) {
891 str_parms_del(parms, AUDIO_PARAMETER_FFV_CHANNEL_INDEX);
892 ALOGD("%s: set target chan index %d", __func__, val);
893 ffvmod.target_ch_idx = val;
894 }
895 }
896}