2016-04-13 18:54:02 +00:00
|
|
|
/*
|
2017-02-03 23:56:10 +00:00
|
|
|
* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
|
2016-04-13 18:54:02 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define LOG_TAG "keep_alive"
|
|
|
|
/*#define LOG_NDEBUG 0*/
|
2017-05-18 11:43:33 +00:00
|
|
|
|
2016-04-13 18:54:02 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <cutils/log.h>
|
|
|
|
#include "audio_hw.h"
|
|
|
|
#include "audio_extn.h"
|
|
|
|
#include "platform_api.h"
|
|
|
|
#include <platform.h>
|
|
|
|
|
2017-05-18 11:43:33 +00:00
|
|
|
#ifdef DYNAMIC_LOG_ENABLED
|
|
|
|
#include <log_xml_parser.h>
|
|
|
|
#define LOG_MASK HAL_MOD_FILE_KEEP_ALIVE
|
|
|
|
#include <log_utils.h>
|
|
|
|
#endif
|
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
#define SILENCE_INTERVAL 2 /*In secs*/
|
2016-04-13 18:54:02 +00:00
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
STATE_DEINIT = -1,
|
|
|
|
STATE_IDLE,
|
|
|
|
STATE_ACTIVE,
|
|
|
|
} state_t;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
REQUEST_WRITE,
|
|
|
|
} request_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pthread_mutex_t lock;
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_mutex_t sleep_lock;
|
2016-04-13 18:54:02 +00:00
|
|
|
pthread_cond_t cond;
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_cond_t wake_up_cond;
|
2016-04-13 18:54:02 +00:00
|
|
|
pthread_t thread;
|
|
|
|
state_t state;
|
|
|
|
struct listnode cmd_list;
|
|
|
|
struct pcm *pcm;
|
|
|
|
bool done;
|
|
|
|
void * userdata;
|
|
|
|
} keep_alive_t;
|
|
|
|
|
|
|
|
struct keep_alive_cmd {
|
|
|
|
struct listnode node;
|
|
|
|
request_t req;
|
|
|
|
};
|
|
|
|
|
|
|
|
static keep_alive_t ka;
|
|
|
|
|
|
|
|
static struct pcm_config silence_config = {
|
|
|
|
.channels = 2,
|
|
|
|
.rate = DEFAULT_OUTPUT_SAMPLING_RATE,
|
|
|
|
.period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,
|
|
|
|
.period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,
|
|
|
|
.format = PCM_FORMAT_S16_LE,
|
|
|
|
.start_threshold = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
|
|
|
|
.stop_threshold = INT_MAX,
|
|
|
|
.avail_min = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void * keep_alive_loop(void * context);
|
|
|
|
|
|
|
|
void audio_extn_keep_alive_init(struct audio_device *adev)
|
|
|
|
{
|
|
|
|
ka.userdata = adev;
|
|
|
|
ka.state = STATE_IDLE;
|
|
|
|
ka.pcm = NULL;
|
|
|
|
pthread_mutex_init(&ka.lock, (const pthread_mutexattr_t *) NULL);
|
|
|
|
pthread_cond_init(&ka.cond, (const pthread_condattr_t *) NULL);
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_cond_init(&ka.wake_up_cond, (const pthread_condattr_t *) NULL);
|
|
|
|
pthread_mutex_init(&ka.sleep_lock, (const pthread_mutexattr_t *) NULL);
|
2016-04-13 18:54:02 +00:00
|
|
|
list_init(&ka.cmd_list);
|
|
|
|
if (pthread_create(&ka.thread, (const pthread_attr_t *) NULL,
|
|
|
|
keep_alive_loop, NULL) < 0) {
|
|
|
|
ALOGW("Failed to create keep_alive_thread");
|
|
|
|
/* can continue without keep alive */
|
|
|
|
ka.state = STATE_DEINIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_cmd_l(request_t r)
|
|
|
|
{
|
|
|
|
if (ka.state == STATE_DEINIT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct keep_alive_cmd *cmd =
|
|
|
|
(struct keep_alive_cmd *)calloc(1, sizeof(struct keep_alive_cmd));
|
|
|
|
|
|
|
|
cmd->req = r;
|
|
|
|
list_add_tail(&ka.cmd_list, &cmd->node);
|
|
|
|
pthread_cond_signal(&ka.cond);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int close_silence_stream()
|
|
|
|
{
|
|
|
|
if (!ka.pcm)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
pcm_close(ka.pcm);
|
|
|
|
ka.pcm = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int open_silence_stream()
|
|
|
|
{
|
|
|
|
unsigned int flags = PCM_OUT|PCM_MONOTONIC;
|
|
|
|
|
|
|
|
if (ka.pcm)
|
|
|
|
return -EEXIST;
|
|
|
|
|
2016-09-03 21:13:26 +00:00
|
|
|
int silence_pcm_dev_id = platform_get_pcm_device_id(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
|
|
|
|
PCM_PLAYBACK);
|
|
|
|
|
|
|
|
ALOGD("opening silence device %d", silence_pcm_dev_id);
|
2016-04-13 18:54:02 +00:00
|
|
|
struct audio_device * adev = (struct audio_device *)ka.userdata;
|
2016-09-03 21:13:26 +00:00
|
|
|
ka.pcm = pcm_open(adev->snd_card, silence_pcm_dev_id,
|
2016-04-13 18:54:02 +00:00
|
|
|
flags, &silence_config);
|
2016-09-03 21:13:26 +00:00
|
|
|
ALOGD("opened silence device %d", silence_pcm_dev_id);
|
2016-04-13 18:54:02 +00:00
|
|
|
if (ka.pcm == NULL || !pcm_is_ready(ka.pcm)) {
|
|
|
|
ALOGE("%s: %s", __func__, pcm_get_error(ka.pcm));
|
|
|
|
if (ka.pcm != NULL) {
|
|
|
|
pcm_close(ka.pcm);
|
|
|
|
ka.pcm = NULL;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
|
|
|
|
static int set_mixer_control(struct mixer *mixer,
|
|
|
|
const char * mixer_ctl_name,
|
|
|
|
const char *mixer_val)
|
|
|
|
{
|
|
|
|
struct mixer_ctl *ctl;
|
|
|
|
if ((mixer == NULL) || (mixer_ctl_name == NULL) || (mixer_val == NULL)) {
|
|
|
|
ALOGE("%s: Invalid input", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ALOGD("setting mixer ctl %s with value %s", mixer_ctl_name, mixer_val);
|
|
|
|
ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
|
|
|
|
if (!ctl) {
|
|
|
|
ALOGE("%s: could not get ctl for mixer cmd - %s",
|
|
|
|
__func__, mixer_ctl_name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mixer_ctl_set_enum_by_string(ctl, mixer_val);
|
|
|
|
}
|
|
|
|
|
2016-04-13 18:54:02 +00:00
|
|
|
/* must be called with adev lock held */
|
|
|
|
void audio_extn_keep_alive_start()
|
|
|
|
{
|
|
|
|
struct audio_device * adev = (struct audio_device *)ka.userdata;
|
2016-05-24 08:17:08 +00:00
|
|
|
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
|
|
|
|
int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
|
|
|
|
struct mixer_ctl *ctl;
|
|
|
|
int acdb_dev_id, snd_device;
|
2016-07-27 07:03:25 +00:00
|
|
|
struct listnode *node;
|
|
|
|
struct audio_usecase *usecase;
|
2016-05-24 08:17:08 +00:00
|
|
|
int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
pthread_mutex_lock(&ka.lock);
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
if (ka.state == STATE_DEINIT) {
|
|
|
|
ALOGE(" %s : Invalid state ",__func__);
|
2016-07-27 07:03:25 +00:00
|
|
|
goto exit;
|
2016-05-24 08:17:08 +00:00
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
if (audio_extn_passthru_is_active()) {
|
|
|
|
ALOGE(" %s : Pass through is already active", __func__);
|
2016-07-27 07:03:25 +00:00
|
|
|
goto exit;
|
2016-05-24 08:17:08 +00:00
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
if (ka.state == STATE_ACTIVE) {
|
|
|
|
ALOGV(" %s : Keep alive state is already Active",__func__ );
|
2016-04-13 18:54:02 +00:00
|
|
|
goto exit;
|
2016-05-24 08:17:08 +00:00
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
/* Dont start keep_alive if any other PCM session is routed to HDMI*/
|
|
|
|
list_for_each(node, &adev->usecase_list) {
|
|
|
|
usecase = node_to_item(node, struct audio_usecase, list);
|
|
|
|
if (usecase->type == PCM_PLAYBACK &&
|
|
|
|
usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2016-04-13 18:54:02 +00:00
|
|
|
ka.done = false;
|
2016-05-24 08:17:08 +00:00
|
|
|
|
|
|
|
/*configure app type */
|
2016-09-03 21:13:26 +00:00
|
|
|
int silence_pcm_dev_id = platform_get_pcm_device_id(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
|
|
|
|
PCM_PLAYBACK);
|
2016-05-24 08:17:08 +00:00
|
|
|
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
|
2016-09-03 21:13:26 +00:00
|
|
|
"Audio Stream %d App Type Cfg", silence_pcm_dev_id);
|
2016-05-24 08:17:08 +00:00
|
|
|
|
|
|
|
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
|
|
|
|
if (!ctl) {
|
|
|
|
ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__,
|
|
|
|
mixer_ctl_name);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2016-09-03 21:13:26 +00:00
|
|
|
/* Configure HDMI/DP Backend with default values, this as well
|
|
|
|
* helps reconfigure HDMI/DP backend after passthrough.
|
|
|
|
*/
|
|
|
|
int ext_disp_type = platform_get_ext_disp_type(adev->platform);
|
|
|
|
switch(ext_disp_type) {
|
|
|
|
case EXT_DISPLAY_TYPE_HDMI:
|
|
|
|
snd_device = SND_DEVICE_OUT_HDMI;
|
|
|
|
set_mixer_control(adev->mixer, "HDMI RX Format", "LPCM");
|
|
|
|
set_mixer_control(adev->mixer, "HDMI_RX SampleRate", "KHZ_48");
|
|
|
|
set_mixer_control(adev->mixer, "HDMI_RX Channels", "Two");
|
|
|
|
break;
|
|
|
|
case EXT_DISPLAY_TYPE_DP:
|
|
|
|
snd_device = SND_DEVICE_OUT_DISPLAY_PORT;
|
2017-02-03 23:56:10 +00:00
|
|
|
set_mixer_control(adev->mixer, "Display Port RX Format", "LPCM");
|
2016-09-03 21:13:26 +00:00
|
|
|
set_mixer_control(adev->mixer, "Display Port RX SampleRate", "KHZ_48");
|
|
|
|
set_mixer_control(adev->mixer, "Display Port RX Channels", "Two");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
|
|
|
|
if (acdb_dev_id < 0) {
|
|
|
|
ALOGE("%s: Couldn't get the acdb dev id", __func__);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
|
|
|
|
app_type_cfg[len++] = platform_get_default_app_type(adev->platform);
|
|
|
|
app_type_cfg[len++] = acdb_dev_id;
|
|
|
|
app_type_cfg[len++] = sample_rate;
|
|
|
|
|
|
|
|
ALOGI("%s:%d PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
|
|
|
|
__func__, __LINE__,
|
|
|
|
platform_get_default_app_type(adev->platform),
|
|
|
|
acdb_dev_id, sample_rate);
|
|
|
|
mixer_ctl_set_array(ctl, app_type_cfg, len);
|
|
|
|
|
|
|
|
/*send calibration*/
|
2016-07-27 07:03:25 +00:00
|
|
|
usecase = calloc(1, sizeof(struct audio_usecase));
|
2016-05-24 08:17:08 +00:00
|
|
|
usecase->type = PCM_PLAYBACK;
|
2016-09-03 21:13:26 +00:00
|
|
|
usecase->out_snd_device = snd_device;
|
2016-05-24 08:17:08 +00:00
|
|
|
|
|
|
|
platform_send_audio_calibration(adev->platform, usecase,
|
|
|
|
platform_get_default_app_type(adev->platform), sample_rate);
|
|
|
|
|
|
|
|
/*apply audio route */
|
2016-09-03 21:13:26 +00:00
|
|
|
switch(ext_disp_type) {
|
|
|
|
case EXT_DISPLAY_TYPE_HDMI:
|
|
|
|
audio_route_apply_and_update_path(adev->audio_route, "silence-playback hdmi");
|
|
|
|
break;
|
|
|
|
case EXT_DISPLAY_TYPE_DP:
|
|
|
|
audio_route_apply_and_update_path(adev->audio_route, "silence-playback display-port");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
|
|
|
|
goto exit;
|
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
|
|
|
|
if (open_silence_stream() == 0) {
|
|
|
|
send_cmd_l(REQUEST_WRITE);
|
|
|
|
while (ka.state != STATE_ACTIVE) {
|
|
|
|
pthread_cond_wait(&ka.cond, &ka.lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* must be called with adev lock held */
|
|
|
|
void audio_extn_keep_alive_stop()
|
|
|
|
{
|
|
|
|
struct audio_device * adev = (struct audio_device *)ka.userdata;
|
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
pthread_mutex_lock(&ka.lock);
|
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
if ((ka.state == STATE_DEINIT) || (ka.state == STATE_IDLE))
|
2016-04-13 18:54:02 +00:00
|
|
|
goto exit;
|
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_mutex_lock(&ka.sleep_lock);
|
2016-04-13 18:54:02 +00:00
|
|
|
ka.done = true;
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_cond_signal(&ka.wake_up_cond);
|
|
|
|
pthread_mutex_unlock(&ka.sleep_lock);
|
2016-04-13 18:54:02 +00:00
|
|
|
while (ka.state != STATE_IDLE) {
|
|
|
|
pthread_cond_wait(&ka.cond, &ka.lock);
|
|
|
|
}
|
|
|
|
close_silence_stream();
|
2016-09-03 21:13:26 +00:00
|
|
|
|
|
|
|
/*apply audio route */
|
|
|
|
int ext_disp_type = platform_get_ext_disp_type(adev->platform);
|
|
|
|
switch(ext_disp_type) {
|
|
|
|
case EXT_DISPLAY_TYPE_HDMI:
|
|
|
|
audio_route_reset_and_update_path(adev->audio_route, "silence-playback hdmi");
|
|
|
|
break;
|
|
|
|
case EXT_DISPLAY_TYPE_DP:
|
|
|
|
audio_route_reset_and_update_path(adev->audio_route, "silence-playback display-port");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
|
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
|
|
|
|
exit:
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_extn_keep_alive_is_active()
|
|
|
|
{
|
|
|
|
return ka.state == STATE_ACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int audio_extn_keep_alive_set_parameters(struct audio_device *adev __unused,
|
|
|
|
struct str_parms *parms)
|
|
|
|
{
|
|
|
|
char value[32];
|
|
|
|
int ret;
|
|
|
|
|
2016-05-24 08:17:08 +00:00
|
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
|
2016-04-13 18:54:02 +00:00
|
|
|
if (ret >= 0) {
|
|
|
|
int val = atoi(value);
|
|
|
|
if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
|
|
if (!audio_extn_passthru_is_active()) {
|
|
|
|
ALOGV("start keep alive");
|
|
|
|
audio_extn_keep_alive_start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
|
|
|
|
sizeof(value));
|
|
|
|
if (ret >= 0) {
|
|
|
|
int val = atoi(value);
|
|
|
|
if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
|
|
|
|
ALOGV("stop keep_alive");
|
|
|
|
audio_extn_keep_alive_stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void * keep_alive_loop(void * context __unused)
|
|
|
|
{
|
|
|
|
struct keep_alive_cmd *cmd = NULL;
|
|
|
|
struct listnode *item;
|
|
|
|
uint8_t * silence = NULL;
|
2016-05-24 08:17:08 +00:00
|
|
|
int32_t bytes = 0;
|
2016-07-27 07:03:25 +00:00
|
|
|
struct timespec ts;
|
2016-04-13 18:54:02 +00:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
pthread_mutex_lock(&ka.lock);
|
|
|
|
if (list_empty(&ka.cmd_list)) {
|
|
|
|
pthread_cond_wait(&ka.cond, &ka.lock);
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = list_head(&ka.cmd_list);
|
|
|
|
cmd = node_to_item(item, struct keep_alive_cmd, node);
|
|
|
|
list_remove(item);
|
|
|
|
|
|
|
|
if (cmd->req != REQUEST_WRITE) {
|
|
|
|
free(cmd);
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(cmd);
|
|
|
|
ka.state = STATE_ACTIVE;
|
|
|
|
pthread_cond_signal(&ka.cond);
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
|
|
|
|
if (!silence) {
|
|
|
|
/* 50 ms */
|
|
|
|
bytes =
|
|
|
|
(silence_config.rate * silence_config.channels * sizeof(int16_t)) / 20;
|
|
|
|
silence = (uint8_t *)calloc(1, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!ka.done) {
|
|
|
|
ALOGV("write %d bytes of silence", bytes);
|
|
|
|
pcm_write(ka.pcm, (void *)silence, bytes);
|
|
|
|
/* This thread does not have to write silence continuously.
|
|
|
|
* Just something to keep the connection alive is sufficient.
|
|
|
|
* Hence a short burst of silence periodically.
|
|
|
|
*/
|
2016-07-27 07:03:25 +00:00
|
|
|
pthread_mutex_lock(&ka.sleep_lock);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
ts.tv_sec += SILENCE_INTERVAL;
|
|
|
|
ts.tv_nsec = 0;
|
2016-04-13 18:54:02 +00:00
|
|
|
|
2016-07-27 07:03:25 +00:00
|
|
|
if (!ka.done)
|
|
|
|
pthread_cond_timedwait(&ka.wake_up_cond,
|
|
|
|
&ka.sleep_lock, &ts);
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&ka.sleep_lock);
|
|
|
|
}
|
2016-04-13 18:54:02 +00:00
|
|
|
pthread_mutex_lock(&ka.lock);
|
|
|
|
ka.state = STATE_IDLE;
|
|
|
|
pthread_cond_signal(&ka.cond);
|
|
|
|
pthread_mutex_unlock(&ka.lock);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|