qahw: Add support for voice tester

Add support for voice tester.

Change-Id: I7573ca4eb25be722f7e4b6005d86cb55d016c8c8
diff --git a/qahw_api/test/Makefile.am b/qahw_api/test/Makefile.am
index 40cc24a..97e7438 100644
--- a/qahw_api/test/Makefile.am
+++ b/qahw_api/test/Makefile.am
@@ -41,3 +41,27 @@
 trans_loopback_test_CFLAGS  = $(AM_CFLAGS) -Wno-sign-compare -Werror
 trans_loopback_test_CFLAGS  += $(trans_loopback_test_INCLUDES)
 trans_loopback_test_LDADD = -llog  -lutils ../libqahw.la -lcutils -lm
+
+if QAHW_V1
+bin_PROGRAMS += hal_voice_test
+
+VOICE_CFLAGS = -Wno-sign-compare -Werror
+VOICE_CFLAGS += -D__unused=__attribute__\(\(__unused__\)\)
+VOICE_INCLUDES = -I $(top_srcdir)/qahw_api/inc
+VOICE_INCLUDES += -I $(top_srcdir)/qahw/inc
+
+hal_voice_test_SOURCES = qahw_voice_test.c
+
+hal_voice_test_LDADD = -lutils ../libqahw.la
+AM_CFLAGS = -I $(PKG_CONFIG_SYSROOT_DIR)/usr/include/mm-audio/qahw/inc
+
+if QAP
+AM_CFLAGS += -DQAP
+AM_CFLAGS += -I ${WORKSPACE}/audio/mm-audio/qap_wrapper/inc/
+hal_voice_test_SOURCES += qap_wrapper_extn.c
+hal_voice_test_LDADD += -lqap_wrapper
+endif
+
+hal_voice_test_CFLAGS = $(VOICE_CFLAGS) $(VOICE_INCLUDES)
+hal_voice_test_CFLAGS += $(AM_CFLAGS) -DLINUX_ENABLED
+endif
diff --git a/qahw_api/test/qahw_voice_test.c b/qahw_api/test/qahw_voice_test.c
new file mode 100644
index 0000000..96da2c6
--- /dev/null
+++ b/qahw_api/test/qahw_voice_test.c
@@ -0,0 +1,675 @@
+/*
+* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test app for voice call */
+
+#include "qahw_voice_test.h"
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+#define WAV_HEADER_LENGTH_MAX 128
+#define FORMAT_DESCRIPTOR_SIZE 12
+#define SUBCHUNK1_SIZE(x) ((8) + (x))
+#define SUBCHUNK2_SIZE 8
+
+voice_stream_config stream_params;
+volatile bool stop = false;
+void *context = NULL;
+
+struct wav_header {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t riff_fmt;
+    uint32_t fmt_id;
+    uint32_t fmt_sz;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;       /* sample_rate * num_channels * bps / 8 */
+    uint16_t block_align;     /* num_channels * bps / 8 */
+    uint16_t bits_per_sample;
+    uint32_t data_id;
+    uint32_t data_sz;
+};
+
+static void init_stream(void) {
+    stream_params.vsid = "11C05000";
+    stream_params.qahw_mod_handle = NULL;
+    stream_params.call_length = -1; /*infinite*/
+    stream_params.multi_call = 1;
+    stream_params.output_device[0] = AUDIO_DEVICE_OUT_WIRED_HEADSET;
+    stream_params.output_device[1] = AUDIO_DEVICE_IN_BUILTIN_MIC;
+    stream_params.in_call_rec = false;
+    stream_params.in_call_playback = false;
+    stream_params.hpcm = false;
+    stream_params.hpcm_tp = 2;
+    stream_params.tp_dir = 0;
+    stream_params.rec_file = "/data/default_rec.wav";
+    stream_params.playback_file = NULL;
+    stream_params.vol = .75;
+    stream_params.mute = false;
+    stream_params.mute_dir = 0;
+    stream_params.tty_mode = 0;
+    stream_params.dtmf_gen_enable = 0;
+    stream_params.dtmf_freq_low = 697;
+    stream_params.dtmf_freq_high =  1209;
+    stream_params.dtmf_gain = 100;
+}
+
+void usage() {
+    printf(" \n Command \n");
+    printf(" \n hal_voice_test <options>   - starts voice call\n");
+    printf(" \n Options\n");
+    printf(" -i  --vsid <vsid>                   - vsid to use sim1<297816064> sim2<29965107>.\n");
+    printf(" -d  --device <decimal value>        - see system/media/audio/include/system/audio.h for device values\n");
+    printf(" -l  --length <call length>          - call length in sec.\n");
+    printf(" -m  --multi_call <number of calls>  - number of calls to make.\n");
+    printf(" -r  --in_call_rec <filename to record to> -t  - tp_dir <0 = DL, 1 = UL, 2 = BOTH >\n");
+    printf(" -p  --in_call_playback <filename to play from>  play audio to voice call\n");
+    printf(" -v  --vol <val>               - volume.\n");
+    printf(" -u  --mute <dir>              - <dir 0= tx, 1 = rx> .\n");
+    printf(" -c  --dtmf_gen                                     .\n");
+    printf(" -y  --tty_mode                - <MODE_OFF = 0, MODE_FULL = 1, MODE_VCO  = 2, MODE_HCO = 3\n");
+}
+
+void stop_signal_handler(int signal __unused) {
+    stop = true;
+}
+
+static void qti_audio_server_death_notify_cb(void *ctxt __unused) {
+    fprintf(stderr, "qas died\n");
+    stop = true;
+}
+
+void *rec_start(void *thread_param) {
+    uint32_t rc = 0;
+    voice_stream_config *params = (voice_stream_config *)thread_param;
+    qahw_module_handle_t *qahw_mod_handle = params->qahw_mod_handle;
+    qahw_stream_handle_t *in_handle = NULL;
+    uint32_t num_dev = 1;
+    audio_devices_t in_device[1] = { AUDIO_DEVICE_IN_BUILTIN_MIC };
+    struct qahw_stream_attributes attr;
+    qahw_buffer_t in_buf;
+    int data_sz = 0;
+    ssize_t bytes_read = -1;
+
+    fprintf(stderr, "%s starting rec thread\n", __func__);
+    if (qahw_mod_handle == NULL) {
+        fprintf(stderr, " qahw_load_module failed\n");
+        pthread_exit(0);
+    }
+    if(params->in_call_rec) {
+        fprintf(stderr, " setting in call record params\n");
+        switch (params->tp_dir) {
+        case 0:
+            attr.type = QAHW_AUDIO_CAPTURE_VOICE_CALL_RX;
+            break;
+        case 1:
+            attr.type = QAHW_AUDIO_CAPTURE_VOICE_CALL_TX;
+            break;
+        default:
+            fprintf(stderr, " invalid tp direction");
+            pthread_exit(0);
+            break;
+        }
+            attr.attr.audio.config.sample_rate = 48000;
+    }
+    if(params->hpcm) {
+        fprintf(stderr, "setting host pcm params\n");
+        switch(params->hpcm_tp) {
+            case QAHW_HPCM_TAP_POINT_RX:
+                attr.type = QAHW_AUDIO_HOST_PCM_RX;
+                break;
+            case QAHW_HPCM_TAP_POINT_TX:
+                attr.type = QAHW_AUDIO_HOST_PCM_TX;
+                break;
+            default:
+                fprintf(stderr, "unsupported tp %d\n", params->hpcm_tp);
+                pthread_exit(0);
+                break;
+        }
+        attr.attr.audio.config.sample_rate = 8000;
+    }
+    attr.direction = QAHW_STREAM_INPUT;
+    attr.attr.audio.config.format = AUDIO_FORMAT_PCM_16_BIT;
+
+    rc = qahw_stream_open(qahw_mod_handle,
+                          attr,
+                          num_dev,
+                          in_device,
+                          0,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &(in_handle));
+    if (rc) {
+        fprintf(stderr, " open input device failed!\n");
+        pthread_exit(0);
+    }
+
+    /* Get buffer size to get upper bound on data to read from the HAL */
+    size_t buffer_size;
+    rc = qahw_stream_get_buffer_size(in_handle, &buffer_size, NULL);
+    char *buffer = (char *)calloc(1, buffer_size);
+    size_t written_size;
+    int bps = 16;
+
+    if (buffer == NULL) {
+        fprintf(stderr, "calloc failed!!, handle(%d)\n", in_handle);
+        pthread_exit(0);
+    }
+    if (params->rec_file == NULL) {
+        fprintf(stderr, "no record stream provided\n", in_handle);
+        pthread_exit(0);
+        return NULL;
+    }
+    FILE *fd = fopen(params->rec_file, "w");
+    if (fd == NULL) {
+        fprintf(stderr, "File open failed \n");
+        free(buffer);
+        pthread_exit(0);
+    }
+    struct wav_header hdr;
+    hdr.riff_id = ID_RIFF;
+    hdr.riff_sz = 0;
+    hdr.riff_fmt = ID_WAVE;
+    hdr.fmt_id = ID_FMT;
+    hdr.fmt_sz = 16;
+    hdr.audio_format = FORMAT_PCM;
+    hdr.num_channels = 1;
+    hdr.sample_rate = attr.attr.audio.config.sample_rate;
+    hdr.byte_rate = hdr.sample_rate * hdr.num_channels * (bps / 8);
+    hdr.block_align = hdr.num_channels * (bps / 8);
+    hdr.bits_per_sample = bps;
+    hdr.data_id = ID_DATA;
+    hdr.data_sz = 0;
+    fwrite(&hdr, 1, sizeof(hdr), fd);
+
+    memset(&in_buf, 0, sizeof(qahw_buffer_t));
+    fprintf(stderr, "file %s opened for write", params->rec_file);
+    while (true && !stop) {
+        in_buf.buffer = buffer;
+        in_buf.size = buffer_size;
+
+        bytes_read = qahw_stream_read(in_handle, &in_buf);
+
+        written_size = fwrite(in_buf.buffer, 1, buffer_size, fd);
+        if (written_size < buffer_size) {
+            fprintf(stderr, "Error in fwrite\n");
+            break;
+        }
+        data_sz += buffer_size;
+    }
+    fprintf(stderr, "rec ended\n");
+    /* update lengths in header */
+    hdr.data_sz = data_sz;
+    hdr.riff_sz = data_sz + 44 - 8;
+    fseek(fd, 0, SEEK_SET);
+    fwrite(&hdr, 1, sizeof(hdr), fd);
+    free(buffer);
+    fclose(fd);
+    fd = NULL;
+    fprintf(stderr, " closing input, handle(%d)", in_handle);
+
+    /* Close input stream and device. */
+    rc = qahw_stream_standby(in_handle);
+    if (rc) {
+        fprintf(stderr, "out standby failed %d, handle(%d)\n", rc, in_handle);
+    }
+
+    rc = qahw_stream_close(in_handle);
+    if (rc) {
+        fprintf(stderr, "could not close input stream %d, handle(%d)\n", rc, in_handle);
+    }
+
+    /* Print instructions to access the file.
+     * Caution: Below ADL log shouldnt be altered without notifying automation APT since it used for
+     * automation testing
+     */
+    fprintf(stderr, "\n\n ADL: The audio recording has been saved to %s. Please use adb pull to get "
+            "the file and play it using audacity. The audio data has the "
+            "following characteristics:\n Sample rate: %i\n Format: %d\n "
+            "Num channels: %i\n\n",
+            params->rec_file, attr.attr.audio.config.sample_rate, attr.attr.audio.config.format, 1);
+    pthread_exit(0);
+
+    return NULL;
+}
+
+int get_wav_header_length(FILE *file_stream) {
+    int subchunk_size = 0;
+    int wav_header_len = 0;
+
+    fseek(file_stream, 16, SEEK_SET);
+    if (fread(&subchunk_size, 4, 1, file_stream) != 1) {
+        fprintf(stderr, "Unable to read subchunk:\n");
+        exit(1);
+    }
+    if (subchunk_size < 16) {
+        fprintf(stderr, "This is not a valid wav file \n");
+    } else {
+        wav_header_len = FORMAT_DESCRIPTOR_SIZE + SUBCHUNK1_SIZE(subchunk_size) + SUBCHUNK2_SIZE;
+    }
+    return wav_header_len;
+}
+
+void *playback_start(void *thread_param) {
+    uint32_t rc = 0;
+    voice_stream_config *params = (voice_stream_config *)thread_param;
+    qahw_module_handle_t *qahw_mod_handle = params->qahw_mod_handle;
+    qahw_stream_handle_t *out_handle = NULL;
+    uint32_t num_dev = 1;
+    audio_devices_t out_device[1] = { AUDIO_DEVICE_OUT_WIRED_HEADSET };
+    struct qahw_stream_attributes attr;
+    size_t bytes_wanted = 0;
+    size_t write_length = 0;
+    size_t bytes_remaining = 0;
+    ssize_t bytes_written = 0;
+    FILE *fp = NULL;
+    size_t bytes_read = 0;
+    qahw_buffer_t out_buf;
+    char  *data_ptr = NULL;
+    bool exit = false;
+    bool read_complete_file = true;
+    int wav_header_len;
+    char header[WAV_HEADER_LENGTH_MAX] = { 0 };
+
+    if (qahw_mod_handle == NULL) {
+        fprintf(stderr, " qahw_load_module failed");
+        pthread_exit(0);
+    }
+
+    attr.direction = QAHW_STREAM_OUTPUT;
+    if(params->in_call_playback) {
+        attr.type = QAHW_AUDIO_PLAYBACK_VOICE_CALL_MUSIC;
+        attr.attr.audio.config.sample_rate = 48000;
+        attr.attr.audio.config.format = AUDIO_FORMAT_PCM_16_BIT;
+    }
+    if(params->hpcm) {
+        switch(params->hpcm_tp) {
+            case QAHW_HPCM_TAP_POINT_RX:
+                attr.type = QAHW_AUDIO_HOST_PCM_RX;
+                break;
+            case QAHW_HPCM_TAP_POINT_TX:
+                attr.type = QAHW_AUDIO_HOST_PCM_TX;
+                break;
+            default:
+                fprintf(stderr, "unsupported tp %d\n", params->hpcm_tp);
+                pthread_exit(0);
+                break;
+        }
+        attr.attr.audio.config.sample_rate = 8000;
+        attr.attr.audio.config.format = AUDIO_FORMAT_PCM_16_BIT;
+    }
+
+
+    if (params->playback_file != NULL)
+        fp = fopen(params->playback_file, "r");
+    if (fp == NULL) {
+        fprintf(stderr, "failed to open file %s\n", params->playback_file);
+        pthread_exit(0);
+    }
+    /*
+    * Read the wave header
+    */
+    if ((wav_header_len = get_wav_header_length(fp)) <= 0) {
+        fprintf(stderr, "wav header length is invalid:%d\n", wav_header_len);
+        pthread_exit(0);
+    }
+    fseek(fp, 0, SEEK_SET);
+    rc = fread(header, wav_header_len, 1, fp);
+    if (rc != 1) {
+        fprintf(stderr, "Error fread failed\n");
+        pthread_exit(0);
+    }
+    if (strncmp(header, "RIFF", 4) && strncmp(header + 8, "WAVE", 4)) {;
+        fprintf(stderr, "Not a wave format\n");
+        pthread_exit(0);
+    }
+    //memcpy (&stream_info->channels, &header[22], 2);
+    memcpy(&attr.attr.audio.config.offload_info.sample_rate, &header[24], 4);
+    memcpy(&attr.attr.audio.config.offload_info.bit_width, &header[34], 2);
+    if (attr.attr.audio.config.offload_info.bit_width == 32)
+        attr.attr.audio.config.offload_info.format = AUDIO_FORMAT_PCM_32_BIT;
+    else if (attr.attr.audio.config.offload_info.bit_width == 24)
+        attr.attr.audio.config.offload_info.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    else
+        attr.attr.audio.config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
+
+    attr.attr.audio.config.sample_rate = attr.attr.audio.config.offload_info.sample_rate;
+    attr.attr.audio.config.format = attr.attr.audio.config.offload_info.format;
+
+    rc = qahw_stream_open(qahw_mod_handle,
+                          attr,
+                          num_dev,
+                          out_device,
+                          0,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &(out_handle));
+
+    if (rc) {
+        fprintf(stderr, " open output device failed!\n");
+        pthread_exit(0);
+    }
+    rc = qahw_stream_get_buffer_size(out_handle ,NULL, &bytes_wanted);
+    data_ptr = (char *)malloc(bytes_wanted);
+    if (data_ptr == NULL) {
+        fprintf(stderr, "failed to allocate data buffer\n");
+        pthread_exit(0);
+    }
+
+    while (!exit && !stop) {
+        if (!bytes_remaining) {
+            bytes_read = fread(data_ptr, 1, bytes_wanted, fp);
+            fprintf(stderr, "read bytes %zd\n", bytes_read);
+            bytes_remaining = write_length = bytes_read;
+        }
+
+        bytes_written = bytes_remaining;
+        memset(&out_buf, 0, sizeof(qahw_buffer_t));
+        out_buf.buffer = data_ptr;
+        out_buf.size = bytes_remaining;
+        bytes_written = qahw_stream_write(out_handle, &out_buf);
+        if (bytes_written <= 0) {
+            fprintf(stderr, "write end %d", bytes_written);
+            exit = true;
+            continue;
+        }
+        bytes_remaining -= bytes_written;
+    }
+
+    fclose(fp);
+    if (data_ptr)
+        free(data_ptr);
+
+    qahw_stream_close(out_handle);
+
+    return NULL;
+}
+
+int main(int argc, char *argv[]) {
+
+    uint32_t rc = 0;
+    int opt = 0;
+    int option_index = 0;
+    qahw_stream_direction dir;
+    int call_count = 0;
+    int call_lenght = 0;
+    pthread_t tid_rec;
+    pthread_t tid_pb;
+
+    init_stream();
+
+    struct option long_options[] = {
+        /* These options set a flag. */
+        { "vsid",     required_argument,    0, 'i' },
+        { "device",     required_argument,    0, 'd' },
+        { "call_length",     required_argument,    0, 'l' },
+        { "help",          no_argument,          0, 'h' },
+        { "in_call_playback",  required_argument,  0, 'p' },
+        { "in_call_rec",  required_argument,  0, 'r' },
+        { "host_pcm",  no_argument,  0, 'b' },
+        { "tp_dir",  required_argument,  0, 't' },
+        { "file",  required_argument,  0, 'f' },
+        { "hpcm_tp",  required_argument,  0, 'a' },
+        { "vol",  required_argument,  0, 'v' },
+        { "mute",  required_argument,  0, 'u' },
+        { "tty_mode",  required_argument,  0, 'y' },
+        { "dtmf_gen", no_argument,  0, 'c' },
+        { 0, 0, 0, 0 }
+    };
+
+    while ((opt = getopt_long(argc,
+                              argv,
+                              "-v:d:l:m:p:r:t:f:a:b:h:i:u:y:c:",
+                              long_options,
+                              &option_index)) != -1) {
+
+        fprintf(stderr, "for argument %c, value is %s\n", opt, optarg);
+
+        switch (opt) {
+        case 'i':
+            stream_params.vsid = optarg;
+            break;
+        case 'd':
+            stream_params.output_device[0] = atoll(optarg);
+            break;
+        case 'l':
+            stream_params.call_length = atoll(optarg);
+            break;
+        case 'm':
+            stream_params.multi_call = atoll(optarg);
+            break;
+        case 'p':
+            stream_params.in_call_playback = true;
+            stream_params.playback_file = optarg;
+            break;
+        case 'r':
+            stream_params.in_call_rec = true;
+            stream_params.rec_file = optarg;
+            break;
+        case 'b':
+            stream_params.hpcm = true;
+            break;
+        case 'f':
+            stream_params.playback_file = optarg;
+            break;
+        case 't':
+            stream_params.tp_dir = atoll(optarg);
+            break;
+        case 'a':
+            stream_params.hpcm_tp = atoll(optarg);
+            break;
+        case 'v':
+            stream_params.vol = atof(optarg);
+            break;
+        case 'u':
+            stream_params.mute_dir = atoll(optarg);
+            stream_params.mute = true;
+            break;
+        case 'y':
+            stream_params.tty_mode = atoll(optarg);
+            break;
+        case 'c':
+            stream_params.dtmf_gen_enable = true;
+            break;
+        case 'h':
+        default:
+            usage();
+            return 0;
+        }
+    }
+
+    /* Register the SIGINT to close the App properly */
+    if (signal(SIGINT, stop_signal_handler) == SIG_ERR)
+        fprintf(stderr, "Failed to register SIGINT:%d\n", errno);
+
+    /* Register the SIGTERM to close the App properly */
+    if (signal(SIGTERM, stop_signal_handler) == SIG_ERR)
+        fprintf(stderr, "Failed to register SIGTERM:%d\n", errno);
+
+    qahw_register_qas_death_notify_cb((audio_error_callback)qti_audio_server_death_notify_cb, context);
+
+    fprintf(stderr, "starting voice call\n");
+    if ((stream_params.qahw_mod_handle = qahw_load_module(QAHW_MODULE_ID_PRIMARY)) == NULL) {
+        fprintf(stderr, "failure in Loading primary HAL\n");
+        goto exit;
+    }
+
+    struct qahw_stream_attributes attr;
+
+    attr.type = QAHW_VOICE_CALL;
+    attr.direction = QAHW_STREAM_INPUT_OUTPUT;
+    attr.attr.voice.vsid = stream_params.vsid;
+    stream_params.out_voice_handle = NULL;
+
+    fprintf(stderr, "vsid is %s device is %d \n", attr.attr.voice.vsid, stream_params.output_device[0]);
+    rc = qahw_stream_open(stream_params.qahw_mod_handle,
+                          attr,
+                          1,
+                          stream_params.output_device,
+                          0,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &(stream_params.out_voice_handle));
+    if (rc) {
+        fprintf(stderr, "Could not open output stream.\n");
+        goto unload;
+    }
+    /*set tty mode if needed*/
+    if(stream_params.tty_mode) {
+        qahw_param_payload tty;
+        tty.tty_mode_params.mode = stream_params.tty_mode;
+        rc = qahw_stream_set_parameters(stream_params.out_voice_handle,
+                                        QAHW_PARAM_TTY_MODE, &tty);
+    }
+    while (stream_params.multi_call) {
+        call_count++;
+        rc = qahw_stream_start(stream_params.out_voice_handle);
+
+        if (rc) {
+            fprintf(stderr, "Could not start voice stream.\n");
+            goto close_stream;
+        }
+        fprintf(stderr, "started voice call %d\n", call_count);
+        /*set volume */
+        struct qahw_volume_data vol;
+        struct qahw_channel_vol vol_pair;
+
+        vol_pair.channel = QAHW_CHANNEL_L;
+        vol_pair.vol = stream_params.vol;
+        vol.num_of_channels = 1;
+        vol.vol_pair = &vol_pair;
+
+        rc = qahw_stream_set_volume(stream_params.out_voice_handle, vol);
+        if(rc){
+            fprintf(stderr, "set vol failed rc %d!\n", rc);
+        }
+        call_lenght = stream_params.call_length;
+        if (stream_params.in_call_rec) {
+            fprintf(stderr, "\n Create %s in call record thread \n");
+            rc = pthread_create(&tid_rec, NULL, rec_start, (void *)&stream_params);
+            if (rc) {
+                fprintf(stderr, "in call rec thread creation failed %d\n");
+            }
+        }
+        if (stream_params.in_call_playback) {
+            fprintf(stderr, "\n Create %s incall playback thread \n");
+            rc = pthread_create(&tid_pb, NULL, playback_start, (void *)&stream_params);
+            if (rc) {
+                fprintf(stderr, "in call playback thread creation failed %d\n");
+            }
+        }
+        if(stream_params.mute) {
+           struct qahw_mute_data mute;
+           mute.enable = true;
+           mute.direction = stream_params.mute_dir;
+           rc = qahw_stream_set_mute(stream_params.out_voice_handle, mute);
+        }
+        if(stream_params.dtmf_gen_enable) {
+            qahw_param_payload dtmf;
+            dtmf.dtmf_gen_params.low_freq = stream_params.dtmf_freq_low;
+            dtmf.dtmf_gen_params.high_freq = stream_params.dtmf_freq_high;
+            dtmf.dtmf_gen_params.gain = stream_params.dtmf_gain;
+            dtmf.dtmf_gen_params.enable = true;
+            rc = qahw_stream_set_parameters(stream_params.out_voice_handle,
+                                            QAHW_PARAM_DTMF_GEN, &dtmf);
+            /*let play for 50 ms*/
+            usleep(50000000);
+            dtmf.dtmf_gen_params.enable = false;
+            rc = qahw_stream_set_parameters(stream_params.out_voice_handle,
+                                            QAHW_PARAM_DTMF_GEN, &dtmf);
+
+        }
+        /*setup hpcm if needed*/
+        if(stream_params.hpcm) {
+            fprintf(stderr, "calling hpcm set param.\n");
+            qahw_param_payload hpcm;
+            hpcm.hpcm_params.tap_point = stream_params.hpcm_tp;
+            hpcm.hpcm_params.direction = stream_params.tp_dir;
+            rc = qahw_stream_set_parameters(stream_params.out_voice_handle,
+                                        QAHW_PARAM_HPCM, &hpcm);
+
+            switch(stream_params.tp_dir) {
+                case QAHW_HPCM_DIRECTION_OUT:
+                    fprintf(stderr, "\n Create %s hpcm playback thread \n");
+                    rc = pthread_create(&tid_pb, NULL, playback_start,
+                                        (void *)&stream_params);
+                    break;
+                case QAHW_HPCM_DIRECTION_IN:
+                    fprintf(stderr, "\n Create %s hpcm record thread \n");
+                    rc = pthread_create(&tid_rec, NULL, rec_start,
+                                        (void *)&stream_params);
+                    break;
+                case QAHW_HPCM_DIRECTION_OUT_IN:
+                    fprintf(stderr, "\n Create %s hpcm record thread \n");
+                    rc = pthread_create(&tid_rec, NULL, rec_start,
+                                        (void *)&stream_params);
+                    fprintf(stderr, "\n Create %s hpcm playback thread \n");
+                    rc = pthread_create(&tid_pb, NULL, playback_start,
+                                        (void *)&stream_params);
+                    break;
+                default:
+                    fprintf(stderr, "\n invalid HPCM direction  \n");
+                    break;
+            }
+        }
+        while (call_lenght) {
+            usleep(1000000);
+            call_lenght--;
+        }
+        stop = true;
+        fprintf(stderr, "stoping call %d\n", call_count);
+        rc = qahw_stream_stop(stream_params.out_voice_handle);
+        stream_params.multi_call--;
+        /*let session stop*/
+        usleep(100000);
+    }
+
+ close_stream:
+    fprintf(stderr, "closing voice stream\n");
+    rc = qahw_stream_close(stream_params.out_voice_handle);
+
+ unload:
+    fprintf(stderr, "unloading hal\n");
+    if (qahw_unload_module(stream_params.qahw_mod_handle) < 0) {
+        fprintf(stderr, "failure in Un Loading primary HAL\n");
+        return -1;
+    }
+    fprintf(stderr, "voice test ended\n");
+ exit:
+    return 0;
+}
diff --git a/qahw_api/test/qahw_voice_test.h b/qahw_api/test/qahw_voice_test.h
new file mode 100644
index 0000000..bb14094
--- /dev/null
+++ b/qahw_api/test/qahw_voice_test.h
@@ -0,0 +1,76 @@
+/*
+* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test app for voice call */
+
+#ifndef QAHW_VOICE_TEST_H
+#define QAHW_VOICE_TEST_H
+
+#include <getopt.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <cutils/str_parms.h>
+#include <tinyalsa/asoundlib.h>
+#include "qahw_api.h"
+#include "qahw_defs.h"
+
+#define MAX_VOICE_TEST_DEVICES 2
+
+typedef struct {
+    qahw_module_handle_t *qahw_mod_handle;
+    qahw_stream_handle_t *out_voice_handle;
+    char* vsid;
+    audio_devices_t output_device[MAX_VOICE_TEST_DEVICES];
+    uint32_t call_length; /*sec*/
+    int multi_call; /*number of calls to make */
+    qahw_module_handle_t *out_handle;
+    bool in_call_rec;
+    bool in_call_playback;
+    bool hpcm;
+    int hpcm_tp;
+    int tp_dir;
+    char* rec_file;
+    char* playback_file;
+    float vol;
+    bool mute;
+    int mute_dir;
+    int tty_mode;
+    int dtmf_gen_enable;
+    int dtmf_freq_low;
+    int dtmf_freq_high;
+    int dtmf_gain;
+}voice_stream_config;
+
+#endif /* QAHW_VOICE_TEST_H */