| /* |
| * (C) 2014 DTS, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "audio_hw_dts_eagle" |
| /*#define LOG_NDEBUG 0*/ |
| |
| #include <errno.h> |
| #include <math.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| #include <cutils/str_parms.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sound/asound.h> |
| #include <sound/audio_effects.h> |
| #include <sound/devdep_params.h> |
| #include "audio_hw.h" |
| #include "platform.h" |
| #include "platform_api.h" |
| |
| #ifdef DTS_EAGLE |
| |
| #define AUDIO_PARAMETER_KEY_DTS_EAGLE "DTS_EAGLE" |
| #define STATE_NOTIFY_FILE "/data/misc/dts/stream" |
| #define FADE_NOTIFY_FILE "/data/misc/dts/fade" |
| #define DTS_EAGLE_KEY "DTS_EAGLE" |
| #define DEVICE_NODE "/dev/snd/hwC0D3" |
| #define MAX_LENGTH_OF_INTEGER_IN_STRING 13 |
| #define PARAM_GET_MAX_SIZE 512 |
| |
| struct dts_eagle_param_desc_alsa { |
| int alsa_effect_ID; |
| struct dts_eagle_param_desc d; |
| }; |
| |
| static struct dts_eagle_param_desc_alsa *fade_in_data = NULL; |
| static struct dts_eagle_param_desc_alsa *fade_out_data = NULL; |
| static int32_t mDevices = 0; |
| static int32_t mCurrDevice = 0; |
| static const char* DTS_EAGLE_STR = DTS_EAGLE_KEY; |
| |
| static int do_DTS_Eagle_params_stream(struct stream_out *out, struct dts_eagle_param_desc_alsa *t, bool get) { |
| char mixer_string[128]; |
| char mixer_str_query[128]; |
| struct mixer_ctl *ctl; |
| struct mixer_ctl *query_ctl; |
| int pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK); |
| |
| ALOGV("DTS_EAGLE_HAL (%s): enter", __func__); |
| snprintf(mixer_string, sizeof(mixer_string), "%s %d", "Audio Effects Config", pcm_device_id); |
| ctl = mixer_get_ctl_by_name(out->dev->mixer, mixer_string); |
| if (!ctl) { |
| ALOGE("DTS_EAGLE_HAL (%s): failed to open mixer %s", __func__, mixer_string); |
| } else if (t) { |
| int size = t->d.size + sizeof(struct dts_eagle_param_desc_alsa); |
| ALOGD("DTS_EAGLE_HAL (%s): opened mixer %s", __func__, mixer_string); |
| if (get) { |
| ALOGD("DTS_EAGLE_HAL (%s): get request", __func__); |
| snprintf(mixer_str_query, sizeof(mixer_str_query), "%s %d", "Query Audio Effect Param", pcm_device_id); |
| query_ctl = mixer_get_ctl_by_name(out->dev->mixer, mixer_str_query); |
| if (!query_ctl) { |
| ALOGE("DTS_EAGLE_HAL (%s): failed to open mixer %s", __func__, mixer_str_query); |
| return -EINVAL; |
| } |
| mixer_ctl_set_array(query_ctl, t, size); |
| return mixer_ctl_get_array(ctl, t, size); |
| } |
| ALOGD("DTS_EAGLE_HAL (%s): set request", __func__); |
| return mixer_ctl_set_array(ctl, t, size); |
| } else { |
| ALOGD("DTS_EAGLE_HAL (%s): parameter data NULL", __func__); |
| } |
| return -EINVAL; |
| } |
| |
| static int do_DTS_Eagle_params(const struct audio_device *adev, struct dts_eagle_param_desc_alsa *t, bool get, const struct stream_out *out) { |
| struct listnode *node; |
| struct audio_usecase *usecase; |
| int ret = 0, sent = 0, tret = 0; |
| |
| ALOGV("DTS_EAGLE_HAL (%s): enter", __func__); |
| |
| if (out) { |
| /* if valid out stream is given, then send params to this stream only */ |
| tret = do_DTS_Eagle_params_stream(out, t, get); |
| if (tret < 0) |
| ret = tret; |
| else |
| sent = 1; |
| } else { |
| list_for_each(node, &adev->usecase_list) { |
| usecase = node_to_item(node, struct audio_usecase, list); |
| /* set/get eagle params for offload usecases only */ |
| if ((usecase->type == PCM_PLAYBACK) && is_offload_usecase(usecase->id)) { |
| tret = do_DTS_Eagle_params_stream(usecase->stream.out, t, get); |
| if (tret < 0) |
| ret = tret; |
| else |
| sent = 1; |
| } |
| } |
| } |
| |
| if (!sent) { |
| int fd = open(DEVICE_NODE, O_RDWR); |
| |
| if (get) { |
| ALOGD("DTS_EAGLE_HAL (%s): no stream opened, attempting to retrieve directly from cache", __func__); |
| t->d.device &= ~DTS_EAGLE_FLAG_ALSA_GET; |
| } else { |
| ALOGD("DTS_EAGLE_HAL (%s): no stream opened, attempting to send directly to cache", __func__); |
| t->d.device |= DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE; |
| } |
| |
| if (fd > 0) { |
| int cmd = get ? DTS_EAGLE_IOCTL_GET_PARAM : DTS_EAGLE_IOCTL_SET_PARAM; |
| if (ioctl(fd, cmd, &t->d) < 0) { |
| ALOGE("DTS_EAGLE_HAL (%s): error sending/getting param\n", __func__); |
| ret = -EINVAL; |
| } else { |
| ALOGD("DTS_EAGLE_HAL (%s): sent/retrieved param\n", __func__); |
| } |
| close(fd); |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): couldn't open device %s\n", __func__, DEVICE_NODE); |
| ret = -EINVAL; |
| } |
| } |
| return ret; |
| } |
| |
| static void fade_node(bool need_data) { |
| char prop[PROPERTY_VALUE_MAX]; |
| property_get("use.dts_eagle", prop, "0"); |
| if (strncmp("true", prop, sizeof("true"))) |
| return; |
| int fd, n = 0; |
| if ((fd = open(FADE_NOTIFY_FILE, O_TRUNC|O_WRONLY)) < 0) { |
| ALOGV("No fade node, create one"); |
| fd = creat(FADE_NOTIFY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); |
| if (fd < 0) { |
| ALOGE("DTS_EAGLE_HAL (%s): Creating fade notifier node failed", __func__); |
| return; |
| } |
| chmod(FADE_NOTIFY_FILE, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH); |
| } |
| char *str = need_data ? "need" : "have"; |
| n = write(fd, str, strlen(str)); |
| close(fd); |
| if (n > 0) |
| ALOGI("DTS_EAGLE_HAL (%s): fade notifier node set to \"%s\", %i bytes written", __func__, str, n); |
| else |
| ALOGE("DTS_EAGLE_HAL (%s): error writing to fade notifier node", __func__); |
| } |
| |
| int audio_extn_dts_eagle_fade(const struct audio_device *adev, bool fade_in, const struct stream_out *out) { |
| char prop[PROPERTY_VALUE_MAX]; |
| |
| ALOGV("DTS_EAGLE_HAL (%s): enter with fade %s requested", __func__, fade_in ? "in" : "out"); |
| |
| property_get("use.dts_eagle", prop, "0"); |
| if (strncmp("true", prop, sizeof("true"))) |
| return 0; |
| |
| if (!fade_in_data || !fade_out_data) |
| fade_node(true); |
| |
| if (fade_in) { |
| if (fade_in_data) |
| return do_DTS_Eagle_params(adev, fade_in_data, false, out); |
| } else { |
| if (fade_out_data) |
| return do_DTS_Eagle_params(adev, fade_out_data, false, out); |
| } |
| return 0; |
| } |
| |
| void audio_extn_dts_eagle_send_lic() { |
| char prop[PROPERTY_VALUE_MAX] = {0}; |
| bool enabled; |
| property_get("use.dts_eagle", prop, "0"); |
| enabled = !strncmp("true", prop, sizeof("true")) || atoi(prop); |
| if (!enabled) |
| return; |
| int fd = open(DEVICE_NODE, O_RDWR); |
| int index = 1; |
| if (fd >= 0) { |
| if (ioctl(fd, DTS_EAGLE_IOCTL_SEND_LICENSE, &index) < 0) { |
| ALOGE("DTS_EAGLE_HAL: error sending license after adsp ssr"); |
| } else { |
| ALOGD("DTS_EAGLE_HAL: sent license after adsp ssr"); |
| } |
| close(fd); |
| } else { |
| ALOGE("DTS_EAGLE_HAL: error opening eagle"); |
| } |
| return; |
| } |
| |
| void audio_extn_dts_eagle_set_parameters(struct audio_device *adev, struct str_parms *parms) { |
| int ret, val; |
| char value[32] = { 0 }, prop[PROPERTY_VALUE_MAX]; |
| |
| ALOGV("DTS_EAGLE_HAL (%s): enter", __func__); |
| |
| property_get("use.dts_eagle", prop, "0"); |
| if (strncmp("true", prop, sizeof("true"))) |
| return; |
| |
| memset(value, 0, sizeof(value)); |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DTS_EAGLE, value, sizeof(value)); |
| if (ret >= 0) { |
| int *data = NULL, id, size, offset, count, dev, dts_found = 0, fade_in = 0; |
| struct dts_eagle_param_desc_alsa *t2 = NULL, **t = &t2; |
| |
| ret = str_parms_get_str(parms, "fade", value, sizeof(value)); |
| if (ret >= 0) { |
| fade_in = atoi(value); |
| if (fade_in > 0) { |
| t = (fade_in == 1) ? &fade_in_data : &fade_out_data; |
| } |
| } |
| |
| ret = str_parms_get_str(parms, "count", value, sizeof(value)); |
| if (ret >= 0) { |
| count = atoi(value); |
| if (count > 1) { |
| int tmp_size = count * 32; |
| char *tmp = malloc(tmp_size+1); |
| data = malloc(sizeof(int) * count); |
| ALOGV("DTS_EAGLE_HAL (%s): multi count param detected, count: %d", __func__, count); |
| if (data && tmp) { |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DTS_EAGLE, tmp, tmp_size); |
| if (ret >= 0) { |
| int idx = 0, tidx, tcnt = 0; |
| dts_found = 1; |
| do { |
| sscanf(&tmp[idx], "%i", &data[tcnt]); |
| tidx = strcspn(&tmp[idx], ","); |
| if (idx + tidx >= ret && tcnt < count-1) { |
| ALOGE("DTS_EAGLE_HAL (%s): malformed multi value string.", __func__); |
| dts_found = 0; |
| break; |
| } |
| ALOGD("DTS_EAGLE_HAL (%s): %i:%i (next %s)", __func__, tcnt, data[tcnt], &tmp[idx+tidx]); |
| idx += tidx + 1; |
| tidx = 0; |
| tcnt++; |
| } while (tcnt < count); |
| } |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): mem alloc for multi count param parse failed.", __func__); |
| } |
| free(tmp); |
| } |
| } |
| |
| if (!dts_found) { |
| data = malloc(sizeof(int)); |
| if (data) { |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DTS_EAGLE, value, sizeof(value)); |
| if (ret >= 0) { |
| *data = atoi(value); |
| dts_found = 1; |
| count = 1; |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): malformed value string.", __func__); |
| } |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): mem alloc for param parse failed.", __func__); |
| } |
| } |
| |
| if (dts_found) { |
| dts_found = 0; |
| ret = str_parms_get_str(parms, "id", value, sizeof(value)); |
| if (ret >= 0) { |
| if (sscanf(value, "%x", &id) == 1) { |
| ret = str_parms_get_str(parms, "size", value, sizeof(value)); |
| if (ret >= 0) { |
| size = atoi(value); |
| ret = str_parms_get_str(parms, "offset", value, sizeof(value)); |
| if (ret >= 0) { |
| offset = atoi(value); |
| ret = str_parms_get_str(parms, "device", value, sizeof(value)); |
| if (ret >= 0) { |
| dev = atoi(value); |
| dts_found = 1; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (dts_found && count > 1 && size != (int)(count * sizeof(int))) { |
| ALOGE("DTS_EAGLE_HAL (%s): size/count mismatch (size = %i bytes, count = %i integers / %u bytes).", __func__, size, count, count*sizeof(int)); |
| } else if (dts_found) { |
| ALOGI("DTS_EAGLE_HAL (%s): param detected: %s", __func__, str_parms_to_str(parms)); |
| if (!(*t)) |
| *t = (struct dts_eagle_param_desc_alsa*)malloc(sizeof(struct dts_eagle_param_desc_alsa) + size); |
| if (*t) { |
| (*t)->alsa_effect_ID = DTS_EAGLE_MODULE; |
| (*t)->d.id = id; |
| (*t)->d.size = size; |
| (*t)->d.offset = offset; |
| (*t)->d.device = dev; |
| memcpy((void*)((char*)*t + sizeof(struct dts_eagle_param_desc_alsa)), data, size); |
| ALOGD("DTS_EAGLE_HAL (%s): id: 0x%X, size: %d, offset: %d, device: %d", __func__, |
| (*t)->d.id, (*t)->d.size, (*t)->d.offset, (*t)->d.device); |
| if (!fade_in) { |
| ret = do_DTS_Eagle_params(adev, *t, false, NULL); |
| if (ret < 0) |
| ALOGE("DTS_EAGLE_HAL (%s): failed setting params in kernel with error %i", __func__, ret); |
| } |
| free(t2); |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): mem alloc for dsp structure failed.", __func__); |
| } |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): param detected but failed parse: %s", __func__, str_parms_to_str(parms)); |
| } |
| free(data); |
| |
| if (fade_in > 0 && fade_in_data && fade_out_data) |
| fade_node(false); |
| } |
| ALOGV("DTS_EAGLE_HAL (%s): exit", __func__); |
| } |
| |
| int audio_extn_dts_eagle_get_parameters(const struct audio_device *adev, |
| struct str_parms *query, struct str_parms *reply) { |
| int ret, val; |
| char value[32] = { 0 }, prop[PROPERTY_VALUE_MAX]; |
| char params[PARAM_GET_MAX_SIZE]; |
| |
| ALOGV("DTS_EAGLE_HAL (%s): enter", __func__); |
| |
| property_get("use.dts_eagle", prop, "0"); |
| if (strncmp("true", prop, sizeof("true"))) |
| return 0; |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_DTS_EAGLE, value, sizeof(value)); |
| if (ret >= 0) { |
| int *data = NULL, id = 0, size = 0, offset = 0, |
| count = 1, dev = 0, idx = 0, dts_found = 0, i = 0; |
| const size_t chars_4_int = 16; |
| ret = str_parms_get_str(query, "count", value, sizeof(value)); |
| if (ret >= 0) { |
| count = atoi(value); |
| if (count > 1) { |
| ALOGV("DTS_EAGLE_HAL (%s): multi count param detected, count: %d", __func__, count); |
| } else { |
| count = 1; |
| } |
| } |
| |
| ret = str_parms_get_str(query, "id", value, sizeof(value)); |
| if (ret >= 0) { |
| if (sscanf(value, "%x", &id) == 1) { |
| ret = str_parms_get_str(query, "size", value, sizeof(value)); |
| if (ret >= 0) { |
| size = atoi(value); |
| ret = str_parms_get_str(query, "offset", value, sizeof(value)); |
| if (ret >= 0) { |
| offset = atoi(value); |
| ret = str_parms_get_str(query, "device", value, sizeof(value)); |
| if (ret >= 0) { |
| dev = atoi(value); |
| dts_found = 1; |
| } |
| } |
| } |
| } |
| } |
| |
| if (dts_found) { |
| ALOGI("DTS_EAGLE_HAL (%s): param (get) detected: %s", __func__, str_parms_to_str(query)); |
| struct dts_eagle_param_desc_alsa *t = (struct dts_eagle_param_desc_alsa *)params; |
| if (t) { |
| char buf[chars_4_int*count]; |
| t->alsa_effect_ID = DTS_EAGLE_MODULE; |
| t->d.id = id; |
| t->d.size = size; |
| t->d.offset = offset; |
| t->d.device = dev; |
| ALOGV("DTS_EAGLE_HAL (%s): id (get): 0x%X, size: %d, offset: %d, device: %d", __func__, |
| t->d.id, t->d.size, t->d.offset, t->d.device & 0x7FFFFFFF); |
| if ((sizeof(struct dts_eagle_param_desc_alsa) + size) > PARAM_GET_MAX_SIZE) { |
| ALOGE("%s: requested data too large", __func__); |
| return -1; |
| } |
| ret = do_DTS_Eagle_params(adev, t, true, NULL); |
| if (ret >= 0) { |
| data = (int*)(params + sizeof(struct dts_eagle_param_desc_alsa)); |
| for (i = 0; i < count; i++) |
| idx += snprintf(&buf[idx], chars_4_int, "%i,", data[i]); |
| buf[idx > 0 ? idx-1 : 0] = 0; |
| ALOGD("DTS_EAGLE_HAL (%s): get result: %s", __func__, buf); |
| str_parms_add_int(reply, "size", size); |
| str_parms_add_str(reply, AUDIO_PARAMETER_KEY_DTS_EAGLE, buf); |
| str_parms_add_int(reply, "count", count); |
| snprintf(value, sizeof(value), "0x%x", id); |
| str_parms_add_str(reply, "id", value); |
| str_parms_add_int(reply, "device", dev); |
| str_parms_add_int(reply, "offset", offset); |
| ALOGV("DTS_EAGLE_HAL (%s): reply: %s", __func__, str_parms_to_str(reply)); |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): failed getting params from kernel with error %i", __func__, ret); |
| return -1; |
| } |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): mem alloc for (get) dsp structure failed.", __func__); |
| return -1; |
| } |
| } else { |
| ALOGE("DTS_EAGLE_HAL (%s): param (get) detected but failed parse: %s", __func__, str_parms_to_str(query)); |
| return -1; |
| } |
| } |
| |
| ALOGV("DTS_EAGLE_HAL (%s): exit", __func__); |
| return 0; |
| } |
| |
| void audio_extn_dts_create_state_notifier_node(int stream_out) |
| { |
| char prop[PROPERTY_VALUE_MAX]; |
| char path[PATH_MAX]; |
| char value[MAX_LENGTH_OF_INTEGER_IN_STRING]; |
| int fd; |
| property_get("use.dts_eagle", prop, "0"); |
| if ((!strncmp("true", prop, sizeof("true")) || atoi(prop))) { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): create_state_notifier_node - stream_out: %d", __func__, stream_out); |
| strlcpy(path, STATE_NOTIFY_FILE, sizeof(path)); |
| snprintf(value, sizeof(value), "%d", stream_out); |
| strlcat(path, value, sizeof(path)); |
| |
| if ((fd=open(path, O_RDONLY)) < 0) { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): no file exists", __func__); |
| } else { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): a file with the same name exists, removing it before creating it", __func__); |
| close(fd); |
| remove(path); |
| } |
| if ((fd=creat(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) { |
| ALOGE("DTS_EAGLE_NODE_STREAM (%s): opening state notifier node failed returned", __func__); |
| return; |
| } |
| chmod(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH); |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): opening state notifier node successful", __func__); |
| close(fd); |
| if (!fade_in_data || !fade_out_data) |
| fade_node(true); |
| } |
| } |
| |
| void audio_extn_dts_notify_playback_state(int stream_out, int has_video, int sample_rate, |
| int channels, int is_playing) { |
| char prop[PROPERTY_VALUE_MAX]; |
| char path[PATH_MAX]; |
| char value[MAX_LENGTH_OF_INTEGER_IN_STRING]; |
| char buf[1024]; |
| int fd; |
| property_get("use.dts_eagle", prop, "0"); |
| if ((!strncmp("true", prop, sizeof("true")) || atoi(prop))) { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): notify_playback_state - is_playing: %d", __func__, is_playing); |
| strlcpy(path, STATE_NOTIFY_FILE, sizeof(path)); |
| snprintf(value, sizeof(value), "%d", stream_out); |
| strlcat(path, value, sizeof(path)); |
| if ((fd=open(path, O_TRUNC|O_WRONLY)) < 0) { |
| ALOGE("DTS_EAGLE_NODE_STREAM (%s): open state notifier node failed", __func__); |
| } else { |
| snprintf(buf, sizeof(buf), "has_video=%d;sample_rate=%d;channel_mode=%d;playback_state=%d", |
| has_video, sample_rate, channels, is_playing); |
| int n = write(fd, buf, strlen(buf)); |
| if (n > 0) |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): write to state notifier node successful, bytes written: %d", __func__, n); |
| else |
| ALOGE("DTS_EAGLE_NODE_STREAM (%s): write state notifier node failed", __func__); |
| close(fd); |
| } |
| } |
| } |
| |
| void audio_extn_dts_remove_state_notifier_node(int stream_out) |
| { |
| char prop[PROPERTY_VALUE_MAX]; |
| char path[PATH_MAX]; |
| char value[MAX_LENGTH_OF_INTEGER_IN_STRING]; |
| int fd; |
| property_get("use.dts_eagle", prop, "0"); |
| if ((!strncmp("true", prop, sizeof("true")) || atoi(prop)) && (stream_out)) { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): remove_state_notifier_node: stream_out - %d", __func__, stream_out); |
| strlcpy(path, STATE_NOTIFY_FILE, sizeof(path)); |
| snprintf(value, sizeof(value), "%d", stream_out); |
| strlcat(path, value, sizeof(path)); |
| if ((fd=open(path, O_RDONLY)) < 0) { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): open state notifier node failed", __func__); |
| } else { |
| ALOGV("DTS_EAGLE_NODE_STREAM (%s): open state notifier node successful, removing the file", __func__); |
| close(fd); |
| remove(path); |
| } |
| } |
| } |
| |
| #endif /* DTS_EAGLE end */ |