/* * ALSA <-> ahal mixer control plugin * * Copyright (c) 2005 by Takashi Iwai * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * TODO: implement the pseudo poll with thread (and pipe as pollfd)? */ #define LOG_TAG "audio_ahal_ctl" #include #include #include #include #include #include #include "audio_if.h" #define DEBUG #ifdef DEBUG #define DBG(f, ...) \ fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__); #else #define DBG(f, ...) #endif #define AM_LOGI(fmt, ...) ALOGI("[%s:%d] " fmt, __func__,__LINE__, ##__VA_ARGS__) typedef struct aml_kctl_info_storage { int ahal_ctl_index; char ahal_ctl_name[256]; aml_ahal_ctl_elem_info_t kctl_value_info; } aml_kctl_info_storage_t; typedef struct aml_ahl_kctl { snd_ctl_ext_t ext; unsigned int total_count; audio_hw_device_t *device; aml_kctl_info_storage_t *kctl_info_cur_storage; } aml_ahl_kctl_t; static void ahal_close(snd_ctl_ext_t *ext) { aml_ahl_kctl_t *ahal = ext->private_data; if (!ahal) { return; } if (ahal->kctl_info_cur_storage && ahal->total_count > 0) { free(ahal->kctl_info_cur_storage); } if (ahal->device) { audio_hw_unload_interface(ahal->device); } free(ahal); } static int ahal_elem_count(snd_ctl_ext_t *ext) { aml_ahl_kctl_t *ahal = ext->private_data; return ahal->total_count; } static int ahal_elem_list(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) { //AM_LOGI("offest is %p id is %u", id, offset); aml_ahl_kctl_t *ahal = ext->private_data; if (offset >= ahal->total_count) { return -ENOENT; } const char *name = ahal->kctl_info_cur_storage[offset].ahal_ctl_name; snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_name(id, name); snd_ctl_elem_id_set_numid(id, offset + 1); return 0; } static snd_ctl_ext_key_t ahal_find_elem(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id) { aml_ahl_kctl_t *ahal = ext->private_data; const char *name = snd_ctl_elem_id_get_name(id); unsigned int numid = snd_ctl_elem_id_get_numid(id); //AM_LOGI("id is %d name is %s numid is %d", id, name, numid); if (numid > 0 && numid <= ahal->total_count) { for (numid = 0; numid < ahal->total_count; numid++) { //AM_LOGI("INDEX is %d", ahal->kctl_info_cur_storage[numid].ahal_ctl_index); if (strcmp(ahal->kctl_info_cur_storage[numid].ahal_ctl_name, name) == 0) { return ahal->kctl_info_cur_storage[numid].ahal_ctl_index; } } } return SND_CTL_EXT_KEY_NOT_FOUND; } static int ahal_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int *type, unsigned int *acc, unsigned int *count) { aml_ahl_kctl_t *ahal = ext->private_data; *type = ahal->kctl_info_cur_storage[key].kctl_value_info.type; *acc = ahal->kctl_info_cur_storage[key].kctl_value_info.access; *count = ahal->kctl_info_cur_storage[key].kctl_value_info.element_count; return 0; } static int ahal_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *imin, long *imax, long *istep) { aml_ahl_kctl_t *ahal = ext->private_data; *imin = ahal->kctl_info_cur_storage[key].kctl_value_info.value.integer.min; *imax = ahal->kctl_info_cur_storage[key].kctl_value_info.value.integer.max; *istep = ahal->kctl_info_cur_storage[key].kctl_value_info.value.integer.step; //AM_LOGI("min %d max %d step %d", *imin, *imax, *istep); return 0; } static int ahal_get_enumerated_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, unsigned int *items) { aml_ahl_kctl_t *ahal = ext->private_data; return 0; } static int ahal_get_enumerated_name(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, unsigned int item, char *name, size_t name_max_len) { aml_ahl_kctl_t *ahal = ext->private_data; return 0; } static int ahal_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { if (!ext || !value) { AM_LOGI("error: ext=%p or value=%p is NULL", ext, value); return -EINVAL; } aml_ahl_kctl_t *ahal = ext->private_data; if (!ahal || !ahal->device) { AM_LOGI("error: ahal=%p or device=%p is NULL", ahal, ahal->device); return -ENODEV; } // get param count aml_ahal_ioctl_info_t ahal_ioctl_tmp; ahal_ioctl_tmp.index = key; ahal_ioctl_tmp.project = AUDIO_IOCTL_PROJECT_L; int ret = ahal->device->ioctl(ahal->device, AUDIO_IOCTL_CMD_KCONTROL_READ, &ahal_ioctl_tmp, sizeof(ahal_ioctl_tmp)); if (ret != 0) { AM_LOGI("error: INFO_READ (count) ioctl failed, ret=%d", ret); return -EIO; } int elem_count = ahal->kctl_info_cur_storage[key].kctl_value_info.element_count; if (elem_count <= 0 || elem_count > 100) { AM_LOGI("error: invalid elem_count=%d (out of 1-100)", elem_count); return -EINVAL; } for (int i = 0; i < elem_count; i++) { value[i] = ahal_ioctl_tmp.value_cur.value.integer.value[i]; //AM_LOGI("read value[%d]:%ld", i, value[i]); } return 0; } static int ahal_read_bytes(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, void *buffer, size_t size) { aml_ahl_kctl_t *ahal = ext->private_data; unsigned int i, val; return 0; } static int ahal_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { AM_LOGI("*** ahal: ahal_pointer now used, key is %d value %d", key, value[0]); aml_ahl_kctl_t *ahal = ext->private_data; aml_ahal_ioctl_info_t ahal_ioctl_tmp; ahal_ioctl_tmp.project = AUDIO_IOCTL_PROJECT_L; int elem_count_write = ahal->kctl_info_cur_storage[key].kctl_value_info.write_count; if (elem_count_write <= 0 || elem_count_write > 100) { AM_LOGI("error: invalid elem_count=%d (out of 1-100)", elem_count_write); return -EINVAL; } ahal_ioctl_tmp.index = key; for (int i = 0; i < elem_count_write; i++) { ahal_ioctl_tmp.value_cur.value.integer.value[i] = value[i]; AM_LOGI("write value[%d]:%ld", i, value[i]); } int ret = ahal->device->ioctl(ahal->device, AUDIO_IOCTL_CMD_KCONTROL_WRITE, &ahal_ioctl_tmp, sizeof(ahal_ioctl_tmp)); if (ret != 0) { AM_LOGI("error: INFO_WRITE(count) ioctl failed, ret=%d", ret); return -EIO; } return 0; } static int ahal_write_bytes(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, const void *buffer, size_t size) { AM_LOGI("*** ahal: ahal_pointer now used buff is %s size is %d", (char*)buffer, size); aml_ahl_kctl_t *ahal = ext->private_data; char *buffer_string; buffer_string = malloc(size); memcpy(buffer_string, buffer, size); int val, oval; return 1; } static snd_ctl_ext_callback_t ahal_ext_callback = { .close = ahal_close, .elem_count = ahal_elem_count, .elem_list = ahal_elem_list, .find_elem = ahal_find_elem, .get_attribute = ahal_get_attribute, .get_integer_info = ahal_get_integer_info, .get_enumerated_info = ahal_get_enumerated_info, .get_enumerated_name = ahal_get_enumerated_name, .read_integer = ahal_read_integer, .read_bytes = ahal_read_bytes, .write_integer = ahal_write_integer, .write_bytes = ahal_write_bytes, }; SND_CTL_PLUGIN_DEFINE_FUNC(ahal) { aml_ahl_kctl_t *ahal; int err; ahal = calloc(1, sizeof(*ahal)); if (!ahal) return -ENOMEM; ahal->ext.version = SND_CTL_EXT_VERSION; ahal->ext.card_idx = 0; strncpy(ahal->ext.id, "ahal", sizeof(ahal->ext.id) - 1); strncpy(ahal->ext.name, "AHAL Test Control", sizeof(ahal->ext.name) - 1); strncpy(ahal->ext.longname, "AHAL Virtual Control Plugin", sizeof(ahal->ext.longname) - 1); strncpy(ahal->ext.mixername, "AHAL Mixer", sizeof(ahal->ext.mixername) - 1); ahal->ext.callback = &ahal_ext_callback; ahal->ext.private_data = ahal; err = audio_hw_load_interface(&ahal->device); if (err) { AM_LOGI("%s %d error:%d", __func__, __LINE__, err); return err; } AM_LOGI("*** ahal: ready to load adev"); int inited = ahal->device->init_check(ahal->device); if (inited) { AM_LOGI("device not inited, quit"); err = -1; return err; } aml_ahal_ioctl_info_t ahal_ioctl_tmp; ahal_ioctl_tmp.project = AUDIO_IOCTL_PROJECT_L; ahal->device->ioctl(ahal->device, AUDIO_IOCTL_CMD_KCONTROL_COUNT_READ, &ahal_ioctl_tmp, sizeof(ahal_ioctl_tmp)); ahal->total_count = ahal_ioctl_tmp.total_count; if (ahal->total_count <= 0) { AM_LOGI("error: total_count=%d is invalid", ahal->total_count); return -EINVAL; } aml_kctl_info_storage_t *kctl_info_tmp = (aml_kctl_info_storage_t *)malloc(ahal->total_count * sizeof(aml_kctl_info_storage_t)); if (!kctl_info_tmp) { AM_LOGI("error: malloc failed for kctl_info_tmp (size=%zu)", ahal->total_count * sizeof(aml_kctl_info_storage_t)); return -ENOMEM; } int i; for (i = 0; i < ahal->total_count; i++) { ahal_ioctl_tmp.index = i; int ret = ahal->device->ioctl(ahal->device, AUDIO_IOCTL_CMD_KCONTROL_INFO_READ, &ahal_ioctl_tmp, sizeof(ahal_ioctl_tmp)); if (ret != 0) { AM_LOGI("error: ioctl failed for index=%d, ret=%d", i, ret); free(kctl_info_tmp); return ret; } kctl_info_tmp[i].ahal_ctl_index = ahal_ioctl_tmp.index; kctl_info_tmp[i].kctl_value_info.type = ahal_ioctl_tmp.value_info.type; if (kctl_info_tmp[i].kctl_value_info.type == AML_CTL_ELEM_TYPE_INTEGER) { kctl_info_tmp[i].kctl_value_info.value.integer.min = ahal_ioctl_tmp.value_info.value.integer.min; kctl_info_tmp[i].kctl_value_info.value.integer.max = ahal_ioctl_tmp.value_info.value.integer.max; kctl_info_tmp[i].kctl_value_info.value.integer.step = ahal_ioctl_tmp.value_info.value.integer.step; } kctl_info_tmp[i].kctl_value_info.access = ahal_ioctl_tmp.value_info.access; kctl_info_tmp[i].kctl_value_info.element_count = ahal_ioctl_tmp.value_info.element_count; kctl_info_tmp[i].kctl_value_info.write_count = ahal_ioctl_tmp.value_info.write_count; strncpy(kctl_info_tmp[i].ahal_ctl_name, ahal_ioctl_tmp.value_info.name, sizeof(kctl_info_tmp[i].ahal_ctl_name) - 1); //AM_LOGI("debug: index=%d, name=%s stored at position %d step is %d ", // ahal_ioctl_tmp->index, ahal_ioctl_tmp->value_info.name, i, ahal_ioctl_tmp->value_info.value.integer.step); } ahal->kctl_info_cur_storage = kctl_info_tmp; err = snd_ctl_ext_create(&ahal->ext, name, mode); if (err < 0) { free(ahal); return err; } *handlep = ahal->ext.handle; AM_LOGI("*** ahal: end init start to write"); return 0; } SND_CTL_PLUGIN_SYMBOL(ahal);