From 070ccac33d65c651c972dfab9c6148e43d8d5d8e Mon Sep 17 00:00:00 2001 From: fulup Date: Thu, 3 Aug 2017 12:00:41 +0200 Subject: Initial Working Version on Alsa Policy-Policy-Hook --- .vscode/c_cpp_properties.json | 55 +-- ALSA-afb/Alsa-AddCtl.c | 444 +++++++++--------- ALSA-afb/Alsa-ApiHat.c | 36 +- ALSA-afb/Alsa-RegEvt.c | 327 +++++++------- ALSA-afb/Alsa-SetGet.c | 567 +++++++++++------------ ALSA-afb/Alsa-Ucm.c | 456 ++++++++++--------- Alsa-Plugin/Alsa-Policy-Hook/AlsaHookCb.c | 427 ++++++++++++++++++ Alsa-Plugin/Alsa-Policy-Hook/CMakeLists.txt | 38 ++ Alsa-Plugin/Alsa-Policy-Hook/README.md | 65 +++ Alsa-Plugin/CMakeLists.txt | 23 + Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlug.h | 91 ++++ Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCb.c | 66 +++ Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCtl.c | 423 ++++++++++++++++++ Alsa-Plugin/_Alsa-Hal-plugin/CMakeLists.txt | 55 +++ Alsa-Plugin/_Alsa-Hal-plugin/README.md | 44 ++ HAL-afb/HAL-interface/hal-interface.c | 619 +++++++++++++------------- HAL-afb/HAL-interface/hal-interface.h | 72 +-- HAL-afb/HAL-interface/hal-volramp.c | 161 ++++--- HAL-afb/HAL-interface/hal-volume.c | 126 +++--- HAL-afb/HAL-plugin/CMakeLists.txt | 55 --- HAL-afb/HAL-plugin/HalPlug.h | 92 ---- HAL-afb/HAL-plugin/HalPlugCb.c | 64 --- HAL-afb/HAL-plugin/HalPlugCtl.c | 423 ------------------ HAL-afb/HAL-plugin/README.md | 44 -- HAL-afb/HDA-intel/IntelHdaHAL.c | 60 +-- HAL-afb/Jabra-Solemate/CMakeLists.txt | 42 ++ HAL-afb/Jabra-Solemate/JabraUsbHAL.c | 123 +++++ HAL-afb/Scarlett-Focusrite/CMakeLists.txt | 2 +- HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c | 4 +- HAL-afb/Unicens-USB/CMakeLists.txt | 42 -- HAL-afb/Unicens-USB/UnicensHAL.c | 62 --- HAL-afb/Unicens-USB/UnicensVol.c | 37 -- README.md | 10 +- Shared-Interface/audio-interface.c | 37 +- Shared-Interface/audio-interface.h | 14 +- conf.d/alsa/asoundrc.sample | 142 ++++-- conf.d/cmake/config.cmake | 1 + htdocs/docs/AAAA-architecture.odg | Bin 0 -> 27998 bytes htdocs/docs/AAAA-architecture.pdf | Bin 0 -> 36392 bytes nbproject/configurations.xml | 603 +++++++++++++++++++++---- nbproject/project.xml | 4 + 41 files changed, 3546 insertions(+), 2410 deletions(-) create mode 100644 Alsa-Plugin/Alsa-Policy-Hook/AlsaHookCb.c create mode 100644 Alsa-Plugin/Alsa-Policy-Hook/CMakeLists.txt create mode 100644 Alsa-Plugin/Alsa-Policy-Hook/README.md create mode 100644 Alsa-Plugin/CMakeLists.txt create mode 100644 Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlug.h create mode 100644 Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCb.c create mode 100644 Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCtl.c create mode 100644 Alsa-Plugin/_Alsa-Hal-plugin/CMakeLists.txt create mode 100644 Alsa-Plugin/_Alsa-Hal-plugin/README.md delete mode 100644 HAL-afb/HAL-plugin/CMakeLists.txt delete mode 100644 HAL-afb/HAL-plugin/HalPlug.h delete mode 100644 HAL-afb/HAL-plugin/HalPlugCb.c delete mode 100644 HAL-afb/HAL-plugin/HalPlugCtl.c delete mode 100644 HAL-afb/HAL-plugin/README.md create mode 100644 HAL-afb/Jabra-Solemate/CMakeLists.txt create mode 100644 HAL-afb/Jabra-Solemate/JabraUsbHAL.c delete mode 100644 HAL-afb/Unicens-USB/CMakeLists.txt delete mode 100644 HAL-afb/Unicens-USB/UnicensHAL.c delete mode 100644 HAL-afb/Unicens-USB/UnicensVol.c create mode 100644 htdocs/docs/AAAA-architecture.odg create mode 100644 htdocs/docs/AAAA-architecture.pdf diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index cd13c94..5512cb3 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,64 +1,17 @@ { "configurations": [ { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], + "name": "null", + "includePath": [], "defines": [], - "intelliSenseMode": "clang-x64", "browse": { "path": [ - "/usr/include", - "/usr/local/include", "${workspaceRoot}" ], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" - } - }, - { - "name": "Linux", - "includePath": [ - "/usr/include/c++/5", - "/usr/local/include", - "/usr/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include/c++/5", - "/usr/local/include", - "/usr/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } + }, + "intelliSenseMode": "clang-x64" } ], "version": 2 diff --git a/ALSA-afb/Alsa-AddCtl.c b/ALSA-afb/Alsa-AddCtl.c index 7a68ab5..f7ef92e 100644 --- a/ALSA-afb/Alsa-AddCtl.c +++ b/ALSA-afb/Alsa-AddCtl.c @@ -18,7 +18,7 @@ * https://kernel.readthedocs.io/en/sphinx-samples/writing-an-alsa-driver.html#control-names * https://01.org/linuxgraphics/gfx-docs/drm/sound/designs/control-names.html -*/ + */ #define _GNU_SOURCE // needed for vasprintf #include @@ -29,270 +29,302 @@ #include "Alsa-ApiHat.h" // Performs like a toggle switch for attenuation, because they're bool (ref:user-ctl-element-set.c) -static const unsigned int *allocate_bool_fake_tlv (void) { - static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0); - unsigned int *tlv= malloc(sizeof(range)); - if (tlv == NULL) return NULL; - memcpy(tlv, range, sizeof(range)); - return tlv; + +static const unsigned int *allocate_bool_fake_tlv(void) { + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0); + unsigned int *tlv = malloc(sizeof (range)); + if (tlv == NULL) return NULL; + memcpy(tlv, range, sizeof (range)); + return tlv; } -static const unsigned int *allocate_int_dbscale_tlv (int min, int step, int mute) { - // SNDRV_CTL_TLVD_DECLARE_DB_SCALE(range, min, step, mute); - size_t tlvSize = sizeof(4*sizeof(int*)); - unsigned int *tlv= malloc(tlvSize); - tlv[0]=SNDRV_CTL_TLVT_DB_LINEAR; - tlv[1]=(int)tlvSize; - tlv[2]=min*100; - tlv[3] = ((step*100) & SNDRV_CTL_TLVD_DB_SCALE_MASK) | ((mute*100) ? SNDRV_CTL_TLVD_DB_SCALE_MUTE : 0); - return tlv; +static const unsigned int *allocate_int_dbscale_tlv(int min, int step, int mute) { + // SNDRV_CTL_TLVD_DECLARE_DB_SCALE(range, min, step, mute); + size_t tlvSize = sizeof (4 * sizeof (unsigned int)); + unsigned int *tlv = malloc(tlvSize); + tlv[0] = SNDRV_CTL_TLVT_DB_LINEAR; + tlv[1] = (int) tlvSize; + tlv[2] = min * 100; + tlv[3] = ((step * 100) & SNDRV_CTL_TLVD_DB_SCALE_MASK) | ((mute * 100) ? SNDRV_CTL_TLVD_DB_SCALE_MUTE : 0); + return tlv; } -static const unsigned int *allocate_int_linear_tlv (int max, int min) { - // SNDRV_CTL_TLVD_DECLARE_DB_LINEAR (range, min, max); - size_t tlvSize = sizeof(4*sizeof(int*)); - unsigned int *tlv= malloc(tlvSize); - tlv[0]=SNDRV_CTL_TLVT_DB_SCALE; - tlv[1]=(int)tlvSize; - tlv[2]=-min*100; - tlv[3]=max*100; - return tlv; +static const unsigned int *allocate_int_linear_tlv(int max, int min) { + // SNDRV_CTL_TLVD_DECLARE_DB_LINEAR (range, min, max); + size_t tlvSize = sizeof (4 * sizeof (unsigned int)); + unsigned int *tlv = malloc(tlvSize); + tlv[0] = SNDRV_CTL_TLVT_DB_LINEAR; + tlv[1] = (int) tlvSize; + tlv[2] = -min * 100; + tlv[3] = max * 100; + return tlv; } -STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_object *ctlJ, halQueryMode queryMode) { - int err, ctlNumid, ctlValue; +STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_object *ctlJ, halQueryMode queryMode) { + int err, done, ctlNumid, ctlValue, shouldCreate; json_object *tmpJ; const char *ctlName; - ctlRequestT ctlRequest; - int ctlMax, ctlMin, ctlStep, ctlCount, ctlSubDev, ctlSndDev; - snd_ctl_elem_type_t ctlType; - snd_ctl_elem_info_t *elemInfo; + ctlRequestT ctlRequest; + int ctlMax, ctlMin, ctlStep, ctlCount, ctlSubDev, ctlSndDev; + snd_ctl_elem_type_t ctlType; + snd_ctl_elem_info_t *elemInfo; snd_ctl_elem_id_t *elemId; snd_ctl_elem_value_t *elemValue; - const unsigned int *elemTlv=NULL; - + const unsigned int *elemTlv = NULL; + // parse json ctl object - json_object_object_get_ex (ctlJ, "name" , &tmpJ); - ctlName = json_object_get_string(tmpJ); - - json_object_object_get_ex (ctlJ, "ctl" , &tmpJ); - ctlNumid = json_object_get_int(tmpJ); - + json_object_object_get_ex(ctlJ, "name", &tmpJ); + ctlName = json_object_get_string(tmpJ); + + json_object_object_get_ex(ctlJ, "ctl", &tmpJ); + ctlNumid = json_object_get_int(tmpJ); + if (!ctlNumid && !ctlName) { - afb_req_fail_f (request, "ctl-invalid", "crl=%s name or numid missing", json_object_get_string(ctlJ)); + afb_req_fail_f(request, "ctl-invalid", "crl=%s name or numid missing", json_object_get_string(ctlJ)); goto OnErrorExit; } - + + + // We are facing a software ctl + if (ctlNumid == CTL_AUTO) { + done = json_object_object_get_ex(ctlJ, "type", &tmpJ); + if (done) ctlType = json_object_get_int(tmpJ); + else ctlType = SND_CTL_ELEM_TYPE_INTEGER; + + json_object_object_get_ex(ctlJ, "value", &tmpJ); + ctlValue = json_object_get_int(tmpJ); + + // default for json_object_get_int is zero + json_object_object_get_ex(ctlJ, "min", &tmpJ); + ctlMin = json_object_get_int(tmpJ); + + done = json_object_object_get_ex(ctlJ, "max", &tmpJ); + if (done) ctlMax = json_object_get_int(tmpJ); + else { + if (ctlType == SND_CTL_ELEM_TYPE_BOOLEAN) ctlMax = 1; + else ctlMax = 100; + } + + done = json_object_object_get_ex(ctlJ, "step", &tmpJ); + if (!done) ctlStep = 1; + else ctlStep = json_object_get_int(tmpJ); + + done = json_object_object_get_ex(ctlJ, "count", &tmpJ); + if (!done) ctlCount = 1; + else ctlCount = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "snddev", &tmpJ); + ctlSndDev = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "subdev", &tmpJ); + ctlSubDev = json_object_get_int(tmpJ); + } + // Assert that this ctls is not used snd_ctl_elem_info_alloca(&elemInfo); - if (ctlName) snd_ctl_elem_info_set_name (elemInfo, ctlName); - if (ctlNumid)snd_ctl_elem_info_set_numid(elemInfo, ctlNumid); - snd_ctl_elem_info_set_interface (elemInfo, SND_CTL_ELEM_IFACE_MIXER); + if (ctlName) snd_ctl_elem_info_set_name(elemInfo, ctlName); + if (ctlNumid > 0)snd_ctl_elem_info_set_numid(elemInfo, ctlNumid); + snd_ctl_elem_info_set_interface(elemInfo, SND_CTL_ELEM_IFACE_MIXER); + + // If control exist try to reuse it err = snd_ctl_elem_info(ctlDev, elemInfo); - if (!err) { - snd_ctl_elem_id_alloca(&elemId); + if (err) { + shouldCreate = 1; + } else { + int count, min, max, sndDev, subDev; + snd_ctl_elem_type_t type; + + // ctl exit let's get associated elemID + snd_ctl_elem_id_alloca(&elemId); snd_ctl_elem_info_get_id(elemInfo, elemId); - if (snd_ctl_elem_info_get_numid(elemInfo)) goto OnSucessExit; // hardware control nothing todo - else { // user created kcontrol should be removable + + // If this is a hardware ctl only update value + if (ctlNumid != CTL_AUTO) goto UpdateDefaultVal; + + count = snd_ctl_elem_info_get_count(elemInfo); + min = (int) snd_ctl_elem_info_get_min(elemInfo); + max = (int) snd_ctl_elem_info_get_max(elemInfo); + type = snd_ctl_elem_info_get_type(elemInfo); + sndDev = (int) snd_ctl_elem_info_get_device(elemInfo); + subDev = (int) snd_ctl_elem_info_get_subdevice(elemInfo); + + if (count == ctlCount && min == ctlMin && max == ctlMax && type == ctlType && sndDev == ctlSndDev && subDev == ctlSubDev) { + // Adjust value to best fit request + shouldCreate = 0; + } else { err = snd_ctl_elem_remove(ctlDev, elemId); + shouldCreate = 1; if (err < 0) { - afb_req_fail_f (request, "ctl-reset-fail", "ctlName=%s numid=%d fail to reset", snd_ctl_elem_info_get_name(elemInfo), snd_ctl_elem_info_get_numid(elemInfo)); - goto OnErrorExit; - } else elemInfo=NULL; // we remove control elemInfo is not valid anymore + AFB_ERROR("addOneSndCtl: ctlName=%s numid=%d fail to reset", snd_ctl_elem_info_get_name(elemInfo), snd_ctl_elem_info_get_numid(elemInfo)); + goto DoNotUpdate; + }; + + // Control was deleted need to refresh elemInfo + snd_ctl_elem_info_alloca(&elemInfo); + if (ctlName) snd_ctl_elem_info_set_name(elemInfo, ctlName); + snd_ctl_elem_info_set_interface(elemInfo, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_info(ctlDev, elemInfo); } } - // Control was deleted need to refresh elemInfo - if (!elemInfo) { - snd_ctl_elem_info_alloca(&elemInfo); - if (ctlName) snd_ctl_elem_info_set_name (elemInfo, ctlName); - snd_ctl_elem_info_set_interface (elemInfo, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_info(ctlDev, elemInfo); - } - - // default for json_object_get_int is zero - json_object_object_get_ex (ctlJ, "min" , &tmpJ); - ctlMin = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "value" , &tmpJ); - ctlValue = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "max" , &tmpJ); - if (!tmpJ) ctlMax=1; - else ctlMax = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "step" , &tmpJ); - if (!tmpJ) ctlStep=1; - else ctlStep = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "count" , &tmpJ); - if (!tmpJ) ctlCount=1; - else ctlCount = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "snddev" , &tmpJ); - ctlSndDev = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "subdev" , &tmpJ); - ctlSubDev = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "type" , &tmpJ); - if (!tmpJ) ctlType=SND_CTL_ELEM_TYPE_BOOLEAN; - else ctlType = json_object_get_int(tmpJ); - // Add requested ID into elemInfo snd_ctl_elem_info_set_device(elemInfo, ctlSndDev); snd_ctl_elem_info_set_subdevice(elemInfo, ctlSubDev); - // prepare value set - snd_ctl_elem_value_alloca(&elemValue); - switch (ctlType) { case SND_CTL_ELEM_TYPE_BOOLEAN: - err = snd_ctl_add_boolean_elem_set(ctlDev, elemInfo, ctlCount, ctlCount); - if (err) { - afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid boolean data", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); - goto OnErrorExit; - } + if (shouldCreate) { + err = snd_ctl_add_boolean_elem_set(ctlDev, elemInfo, 1, ctlCount); + if (err) { + AFB_ERROR("AddOntSndCtl Boolean: devid='%s' name='%s' count=%d", snd_ctl_name(ctlDev), ctlName, ctlCount); + afb_req_fail_f(request, "ctl-invalid-bool", "devid='%s' name='%s' count=%d", snd_ctl_name(ctlDev), ctlName, ctlCount); + goto OnErrorExit; + } + } + // Create a dummy TLV elemTlv = allocate_bool_fake_tlv(); - - // Provide FALSE as default value - for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_boolean (elemValue, idx, ctlValue); - } + break; - + case SND_CTL_ELEM_TYPE_INTEGER: - err = snd_ctl_add_integer_elem_set (ctlDev, elemInfo, 1, ctlCount, ctlMin, ctlMax, ctlStep); - if (err) { - afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid boolean data", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); - goto OnErrorExit; - } - - // Provide 0 as default value - for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_integer (elemValue, idx, ctlValue); + if (shouldCreate) { + err = snd_ctl_add_integer_elem_set(ctlDev, elemInfo, 1, ctlCount, ctlMin, ctlMax, ctlStep); + if (err) { + AFB_ERROR("AddOntSndCtl Integer: devid='%s' name='%s' count=%d min=%d max=%d step=%d", snd_ctl_name(ctlDev), ctlName, ctlCount, ctlMin, ctlMax, ctlStep); + afb_req_fail_f(request, "ctl-invalid-bool", "devid='%s' name='%s' count=%d min=%d max=%d step=%d", snd_ctl_name(ctlDev), ctlName, ctlCount, ctlMin, ctlMax, ctlStep); + goto OnErrorExit; + } } // Fulup needed to be tested with some dB expert !!! json_object *dbscaleJ; - if (json_object_object_get_ex (ctlJ, "dbscale" , &dbscaleJ)) { + if (json_object_object_get_ex(ctlJ, "dbscale", &dbscaleJ)) { + int min, max; + if (json_object_get_type(dbscaleJ) != json_type_object) { - afb_req_fail_f (request, "ctl-invalid-dbscale", "devid=%s crl=%s invalid json in integer control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + afb_req_fail_f(request, "ctl-invalid-dbscale", "devid=%s crl=%s invalid json in integer control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); goto OnErrorExit; - - json_object_object_get_ex (ctlJ, "min" , &tmpJ); - int min = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "min", &tmpJ); + min = json_object_get_int(tmpJ); if (min >= 0) { - afb_req_fail_f (request, "ctl-invalid-dbscale", "devid=%s crl=%s min should be a negative number", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); - goto OnErrorExit; + afb_req_fail_f(request, "ctl-invalid-dbscale", "devid=%s crl=%s min should be a negative number", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; } - + // default value 0=mute - json_object_object_get_ex (ctlJ, "max" , &tmpJ); - int max = json_object_get_int(tmpJ); - + json_object_object_get_ex(ctlJ, "max", &tmpJ); + max = json_object_get_int(tmpJ); + // default value 1dB - json_object_object_get_ex (ctlJ, "step" , &tmpJ); + json_object_object_get_ex(ctlJ, "step", &tmpJ); int step = json_object_get_int(tmpJ); - if (step <= 0) step=1; - - elemTlv = allocate_int_dbscale_tlv (min, max, step); - + if (step <= 0) step = 1; + + elemTlv = allocate_int_dbscale_tlv(min, max, step); + } } else { // provide a fake linear TLV - elemTlv = allocate_int_linear_tlv (ctlMin, ctlMax); + elemTlv = allocate_int_linear_tlv(ctlMin, ctlMax); } break; - - - case SND_CTL_ELEM_TYPE_ENUMERATED: { - json_object *enumsJ; - json_object_object_get_ex (ctlJ, "enums" , &enumsJ); - if (json_object_get_type(enumsJ) != json_type_array) { - afb_req_fail_f (request, "ctl-missing-enums", "devid=%s crl=%s mandatory enum=xxx missing in enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); - goto OnErrorExit; - } - - int length = json_object_array_length(enumsJ); - const char **enumlabels = malloc(length*sizeof(char*)); - - for (int jdx=0; jdx < length; jdx++) { - tmpJ = json_object_array_get_idx(enumsJ, jdx); - enumlabels[jdx] = json_object_get_string(tmpJ); - } - - err = snd_ctl_add_enumerated_elem_set (ctlDev, elemInfo, 1, ctlCount, length, enumlabels); - if (err) { - afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); - goto OnErrorExit; - } - - // Provide 0 as default value - for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_enumerated (elemValue, idx, ctlValue); - } - elemTlv = allocate_bool_fake_tlv(); - + case SND_CTL_ELEM_TYPE_ENUMERATED: + + if (shouldCreate) { + json_object *enumsJ; + json_object_object_get_ex(ctlJ, "enums", &enumsJ); + if (json_object_get_type(enumsJ) != json_type_array) { + afb_req_fail_f(request, "ctl-missing-enums", "devid=%s crl=%s mandatory enum=xxx missing in enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; + } + + int length = json_object_array_length(enumsJ); + const char **enumlabels = malloc(length * sizeof (char*)); + + for (int jdx = 0; jdx < length; jdx++) { + tmpJ = json_object_array_get_idx(enumsJ, jdx); + enumlabels[jdx] = json_object_get_string(tmpJ); + } + + err = snd_ctl_add_enumerated_elem_set(ctlDev, elemInfo, 1, ctlCount, length, enumlabels); + if (err) { + afb_req_fail_f(request, "ctl-invalid-bool", "devid=%s crl=%s invalid enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; + } + + // Fulup should be an empty container + elemTlv = allocate_bool_fake_tlv(); + } break; - } - - // Long Term Waiting ToDoList + + // Long Term Waiting ToDoList case SND_CTL_ELEM_TYPE_INTEGER64: case SND_CTL_ELEM_TYPE_BYTES: default: - afb_req_fail_f (request, "ctl-invalid-type", "crl=%s unsupported/unknown element type", json_object_get_string(ctlJ)); - goto OnErrorExit; + afb_req_fail_f(request, "ctl-invalid-type", "crl=%s unsupported type type=%d numid=%d", json_object_get_string(ctlJ), ctlType, ctlNumid); + goto OnErrorExit; + } + +UpdateDefaultVal: + + // Set Value to default + snd_ctl_elem_value_alloca(&elemValue); + for (int idx = 0; idx < snd_ctl_elem_info_get_count(elemInfo); idx++) { + snd_ctl_elem_value_set_integer(elemValue, idx, ctlValue); } // write default values in newly created control - snd_ctl_elem_id_alloca(&elemId); + snd_ctl_elem_id_alloca(&elemId); + snd_ctl_elem_info(ctlDev, elemInfo); snd_ctl_elem_info_get_id(elemInfo, elemId); snd_ctl_elem_value_set_id(elemValue, elemId); - err = snd_ctl_elem_write (ctlDev, elemValue); + err = snd_ctl_elem_write(ctlDev, elemValue); if (err < 0) { - afb_req_fail_f (request, "ctl-write-fail", "crl=%s numid=%d fail to write data error=%s", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo), snd_strerror(err)); - goto OnErrorExit; - } - + AFB_WARNING("addOneSndCtl: crl=%s numid=%d fail to write Values error=%s", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo), snd_strerror(err)); + goto DoNotUpdate; + } + // write a default null TLV (if usefull should be implemented for every ctl type) if (elemTlv) { - err=snd_ctl_elem_tlv_write (ctlDev, elemId, elemTlv); + err = snd_ctl_elem_tlv_write(ctlDev, elemId, elemTlv); if (err < 0) { - afb_req_fail_f (request, "TLV-write-fail", "crl=%s numid=%d fail to write data error=%s", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo), snd_strerror(err)); - goto OnErrorExit; + AFB_WARNING("addOneSndCtl: crl=%s numid=%d fail to write TLV error=%s", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo), snd_strerror(err)); + goto DoNotUpdate; } - } + } +DoNotUpdate: // return newly created as a JSON object - OnSucessExit: - alsaGetSingleCtl (ctlDev, elemId, &ctlRequest, queryMode); - if (ctlRequest.used < 0) goto OnErrorExit; - return ctlRequest.valuesJ; - - OnErrorExit: - return NULL; + alsaGetSingleCtl(ctlDev, elemId, &ctlRequest, queryMode); + if (ctlRequest.used < 0) { + AFB_WARNING("addOneSndCtl: crl=%s numid=%d Fail to get value", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo)); + } + return ctlRequest.valuesJ; + +OnErrorExit: + return NULL; } PUBLIC void alsaAddCustomCtls(afb_req request) { int err; json_object *ctlsJ, *ctlsValues, *ctlValues; enum json_type; - snd_ctl_t *ctlDev=NULL; + snd_ctl_t *ctlDev = NULL; const char *devid, *mode; devid = afb_req_value(request, "devid"); if (devid == NULL) { - afb_req_fail_f (request, "devid-missing", "devid MUST be defined for alsaAddCustomCtls"); + afb_req_fail_f(request, "devid-missing", "devid MUST be defined for alsaAddCustomCtls"); goto OnErrorExit; } - + // open control interface for devid err = snd_ctl_open(&ctlDev, devid, 0); if (err < 0) { - afb_req_fail_f (request, "devid-unknown", "SndCard devid=[%s] Not Found err=%s", devid, snd_strerror(err)); + afb_req_fail_f(request, "devid-unknown", "SndCard devid=[%s] Not Found err=%s", devid, snd_strerror(err)); goto OnErrorExit; } @@ -300,41 +332,41 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { halQueryMode queryMode = QUERY_QUIET; mode = afb_req_value(request, "mode"); if (mode != NULL) { - sscanf(mode,"%i", (int*)&queryMode); + sscanf(mode, "%i", (int*) &queryMode); } - + // extract sound controls and parse json - ctlsJ = json_tokener_parse (afb_req_value(request, "ctl")); + ctlsJ = json_tokener_parse(afb_req_value(request, "ctl")); if (!ctlsJ) { - afb_req_fail_f (request, "ctls-missing", "ctls MUST be defined as a JSON array for alsaAddCustomCtls"); + afb_req_fail_f(request, "ctls-missing", "ctls MUST be defined as a JSON array for alsaAddCustomCtls"); goto OnErrorExit; } - - switch (json_object_get_type(ctlsJ)) { + + switch (json_object_get_type(ctlsJ)) { case json_type_object: - ctlsValues= addOneSndCtl(request, ctlDev, ctlsJ, queryMode); - if (!ctlsValues) goto OnErrorExit; + ctlsValues = addOneSndCtl(request, ctlDev, ctlsJ, queryMode); + if (!ctlsValues) goto OnErrorExit; break; - + case json_type_array: - ctlsValues= json_object_new_array(); - for (int idx= 0; idx < json_object_array_length (ctlsJ); idx++) { - json_object *ctlJ = json_object_array_get_idx (ctlsJ, idx); - ctlValues= addOneSndCtl(request, ctlDev, ctlJ, queryMode) ; - if (ctlValues) json_object_array_add (ctlsValues, ctlValues); - else goto OnErrorExit; + ctlsValues = json_object_new_array(); + for (int idx = 0; idx < json_object_array_length(ctlsJ); idx++) { + json_object *ctlJ = json_object_array_get_idx(ctlsJ, idx); + ctlValues = addOneSndCtl(request, ctlDev, ctlJ, queryMode); + if (ctlValues) json_object_array_add(ctlsValues, ctlValues); + else goto OnErrorExit; } break; - + default: - afb_req_fail_f (request, "ctls-invalid","ctls=%s not valid JSON array", json_object_get_string(ctlsJ)); + afb_req_fail_f(request, "ctls-invalid", "ctls=%s not valid JSON array", json_object_get_string(ctlsJ)); goto OnErrorExit; } - + // get ctl as a json response afb_req_success(request, ctlsValues, NULL); - - OnErrorExit: - if (ctlDev) snd_ctl_close(ctlDev); - return; + +OnErrorExit: + if (ctlDev) snd_ctl_close(ctlDev); + return; } \ No newline at end of file diff --git a/ALSA-afb/Alsa-ApiHat.c b/ALSA-afb/Alsa-ApiHat.c index 9797d3c..2c3c5c1 100644 --- a/ALSA-afb/Alsa-ApiHat.c +++ b/ALSA-afb/Alsa-ApiHat.c @@ -32,28 +32,28 @@ * array of the verbs exported to afb-daemon */ static const struct afb_verb_v2 api_verbs[] = { - /* VERB'S NAME FUNCTION TO CALL */ - { .verb= "ping" , .callback= pingtest }, - { .verb= "getinfo", .callback= alsaGetInfo}, - { .verb= "getctl", .callback= alsaGetCtls}, - { .verb= "setctl", .callback= alsaSetCtls}, - { .verb= "subscribe", .callback= alsaEvtSubcribe}, - { .verb= "getcardid", .callback= alsaGetCardId}, - { .verb= "halregister", .callback= alsaRegisterHal}, - { .verb= "hallist", .callback= alsaActiveHal}, - { .verb= "ucmquery", .callback= alsaUseCaseQuery}, - { .verb= "ucmset", .callback= alsaUseCaseSet}, - { .verb= "ucmget", .callback= alsaUseCaseGet}, - { .verb= "ucmreset", .callback= alsaUseCaseReset}, - { .verb= "ucmclose", .callback= alsaUseCaseClose}, - { .verb= "addcustomctl",.callback= alsaAddCustomCtls}, - { .verb= NULL } /* marker for end of the array */ + /* VERB'S NAME FUNCTION TO CALL */ + { .verb = "ping", .callback = pingtest}, + { .verb = "getinfo", .callback = alsaGetInfo}, + { .verb = "getctl", .callback = alsaGetCtls}, + { .verb = "setctl", .callback = alsaSetCtls}, + { .verb = "subscribe", .callback = alsaEvtSubcribe}, + { .verb = "getcardid", .callback = alsaGetCardId}, + { .verb = "halregister", .callback = alsaRegisterHal}, + { .verb = "hallist", .callback = alsaActiveHal}, + { .verb = "ucmquery", .callback = alsaUseCaseQuery}, + { .verb = "ucmset", .callback = alsaUseCaseSet}, + { .verb = "ucmget", .callback = alsaUseCaseGet}, + { .verb = "ucmreset", .callback = alsaUseCaseReset}, + { .verb = "ucmclose", .callback = alsaUseCaseClose}, + { .verb = "addcustomctl", .callback = alsaAddCustomCtls}, + { .verb = NULL} /* marker for end of the array */ }; /* * description of the binding for afb-daemon */ const struct afb_binding_v2 afbBindingV2 = { - .api = "alsacore", - .verbs = api_verbs, + .api = "alsacore", + .verbs = api_verbs, }; diff --git a/ALSA-afb/Alsa-RegEvt.c b/ALSA-afb/Alsa-RegEvt.c index add3944..080b6cc 100644 --- a/ALSA-afb/Alsa-RegEvt.c +++ b/ALSA-afb/Alsa-RegEvt.c @@ -14,17 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. -*/ + */ #define _GNU_SOURCE // needed for vasprintf #include "Alsa-ApiHat.h" // generic sndctrl event handle hook to event callback when pooling + typedef struct { struct pollfd pfds; sd_event_source *src; - snd_ctl_t *ctlDev; + snd_ctl_t *ctlDev; int mode; struct afb_event afbevt; } evtHandleT; @@ -40,29 +41,29 @@ typedef struct { char *apiprefix; char *shortname; char *longname; -}cardRegistryT; +} cardRegistryT; -cardRegistryT *cardRegistry[MAX_SND_CARD+1]; +cardRegistryT *cardRegistry[MAX_SND_CARD + 1]; -PUBLIC json_object *alsaCheckQuery (afb_req request, queryValuesT *queryValues) { +PUBLIC json_object *alsaCheckQuery(afb_req request, queryValuesT *queryValues) { json_object *tmpJ; int done; // get query from request json_object *queryInJ = afb_req_json(request); - - done= json_object_object_get_ex (queryInJ, "devid" , &tmpJ); + + done = json_object_object_get_ex(queryInJ, "devid", &tmpJ); if (!done) { afb_req_fail_f(request, "devid-missing", "Invalid query='%s'", json_object_get_string(queryInJ)); - goto OnErrorExit; + goto OnErrorExit; } queryValues->devid = json_object_get_string(tmpJ); - done= json_object_object_get_ex (queryInJ, "mode" , &tmpJ); - if (!done) queryValues->mode=QUERY_QUIET; // default quiet - else queryValues->mode = json_object_get_int (tmpJ); - + done = json_object_object_get_ex(queryInJ, "mode", &tmpJ); + if (!done) queryValues->mode = QUERY_QUIET; // default quiet + else queryValues->mode = json_object_get_int(tmpJ); + return queryInJ; OnErrorExit: @@ -70,9 +71,10 @@ OnErrorExit: } // This routine is called when ALSA event are fired -STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* userData) { + +STATIC int sndCtlEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) { int err, ctlNumid; - evtHandleT *evtHandle = (evtHandleT*)userData; + evtHandleT *evtHandle = (evtHandleT*) userData; snd_ctl_event_t *eventId; json_object *ctlEventJ; unsigned int mask; @@ -82,307 +84,312 @@ STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* const char*ctlName; ctlRequestT ctlRequest; snd_ctl_elem_id_t *elemId; - + if ((revents & EPOLLHUP) != 0) { AFB_NOTICE("SndCtl hanghup [car disconnected]"); goto ExitOnSucess; } - - if ((revents & EPOLLIN) != 0) { - + + if ((revents & EPOLLIN) != 0) { + // initialise event structure on stack - snd_ctl_event_alloca(&eventId); + snd_ctl_event_alloca(&eventId); snd_ctl_elem_id_alloca(&elemId); - + err = snd_ctl_read(evtHandle->ctlDev, eventId); if (err < 0) goto OnErrorExit; - + // we only process sndctrl element if (snd_ctl_event_get_type(eventId) != SND_CTL_EVENT_ELEM) goto ExitOnSucess; - + // we only process value changed events mask = snd_ctl_event_elem_get_mask(eventId); if (!(mask & SND_CTL_EVENT_MASK_VALUE)) goto ExitOnSucess; - - snd_ctl_event_elem_get_id (eventId, elemId); - - err = alsaGetSingleCtl (evtHandle->ctlDev, elemId, &ctlRequest, evtHandle->mode); + + snd_ctl_event_elem_get_id(eventId, elemId); + + err = alsaGetSingleCtl(evtHandle->ctlDev, elemId, &ctlRequest, evtHandle->mode); if (err) goto OnErrorExit; - + // If CTL as a value use it as container for response - if (ctlRequest.valuesJ) ctlEventJ= ctlRequest.valuesJ; + if (ctlRequest.valuesJ) ctlEventJ = ctlRequest.valuesJ; else { ctlEventJ = json_object_new_object(); ctlNumid = snd_ctl_event_elem_get_numid(eventId); - json_object_object_add(ctlEventJ, "id",json_object_new_int (ctlNumid)); + json_object_object_add(ctlEventJ, "id", json_object_new_int(ctlNumid)); } if (evtHandle->mode >= QUERY_COMPACT) { ctlName = snd_ctl_event_elem_get_name(eventId); - json_object_object_add(ctlEventJ, "name",json_object_new_string (ctlName)); + json_object_object_add(ctlEventJ, "name", json_object_new_string(ctlName)); } if (evtHandle->mode >= QUERY_VERBOSE) { - iface = snd_ctl_event_elem_get_interface(eventId); - device = snd_ctl_event_elem_get_device(eventId); - subdev = snd_ctl_event_elem_get_subdevice(eventId); - json_object_object_add(ctlEventJ, "ifc" ,json_object_new_int (iface)); - json_object_object_add(ctlEventJ, "dev" ,json_object_new_int (device)); - json_object_object_add(ctlEventJ, "sub" ,json_object_new_int (subdev)); + iface = snd_ctl_event_elem_get_interface(eventId); + device = snd_ctl_event_elem_get_device(eventId); + subdev = snd_ctl_event_elem_get_subdevice(eventId); + json_object_object_add(ctlEventJ, "ifc", json_object_new_int(iface)); + json_object_object_add(ctlEventJ, "dev", json_object_new_int(device)); + json_object_object_add(ctlEventJ, "sub", json_object_new_int(subdev)); } - - AFB_DEBUG( "sndCtlEventCB=%s", json_object_get_string(ctlEventJ)); + + AFB_DEBUG("sndCtlEventCB=%s", json_object_get_string(ctlEventJ)); afb_event_push(evtHandle->afbevt, ctlEventJ); } - ExitOnSucess: - return 0; - - OnErrorExit: - AFB_WARNING ("sndCtlEventCB: ignored unsupported event type"); - return (0); +ExitOnSucess: + return 0; + +OnErrorExit: + AFB_WARNING("sndCtlEventCB: ignored unsupported event type"); + return (0); } // Subscribe to every Alsa CtlEvent send by a given board -PUBLIC void alsaEvtSubcribe (afb_req request) { + +PUBLIC void alsaEvtSubcribe(afb_req request) { static sndHandleT sndHandles[MAX_SND_CARD]; - evtHandleT *evtHandle=NULL; - snd_ctl_t *ctlDev=NULL; - int err, idx, cardId, idxFree=-1; + evtHandleT *evtHandle = NULL; + snd_ctl_t *ctlDev = NULL; + int err, idx, cardId, idxFree = -1; snd_ctl_card_info_t *cardinfo; queryValuesT queryValues; - json_object *queryJ = alsaCheckQuery (request, &queryValues); + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; // open control interface for devid err = snd_ctl_open(&ctlDev, queryValues.devid, SND_CTL_READONLY); if (err < 0) { - afb_req_fail_f (request, "devid-unknown", "SndCard devid=%s Not Found err=%s", queryValues.devid, snd_strerror(err)); + afb_req_fail_f(request, "devid-unknown", "SndCard devid=%s Not Found err=%s", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } - - snd_ctl_card_info_alloca(&cardinfo); - if ((err = snd_ctl_card_info(ctlDev, cardinfo)) < 0) { - afb_req_fail_f (request, "devid-invalid", "SndCard devid=%s Not Found err=%s", queryValues.devid, snd_strerror(err)); + + snd_ctl_card_info_alloca(&cardinfo); + if ((err = snd_ctl_card_info(ctlDev, cardinfo)) < 0) { + afb_req_fail_f(request, "devid-invalid", "SndCard devid=%s Not Found err=%s", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } - - cardId=snd_ctl_card_info_get_card(cardinfo); - + + cardId = snd_ctl_card_info_get_card(cardinfo); + // search for an existing subscription and mark 1st free slot - for (idx= 0; idx < MAX_SND_CARD; idx ++) { - if (sndHandles[idx].ucount > 0 && cardId == sndHandles[idx].cardId) { - evtHandle= sndHandles[idx].evtHandle; - break; - } else if (idxFree == -1) idxFree= idx; + for (idx = 0; idx < MAX_SND_CARD; idx++) { + if (sndHandles[idx].ucount > 0 && cardId == sndHandles[idx].cardId) { + evtHandle = sndHandles[idx].evtHandle; + break; + } else if (idxFree == -1) idxFree = idx; }; - + // if not subscription exist for the event let's create one if (idx == MAX_SND_CARD) { - + // reach MAX_SND_CARD event registration if (idxFree == -1) { - afb_req_fail_f (request, "register-toomany", "Cannot register new event Maxcard==%d", idx); - goto OnErrorExit; - } - - evtHandle = malloc (sizeof(evtHandleT)); + afb_req_fail_f(request, "register-toomany", "Cannot register new event Maxcard==%d", idx); + goto OnErrorExit; + } + + evtHandle = malloc(sizeof (evtHandleT)); evtHandle->ctlDev = ctlDev; - evtHandle->mode = queryValues.mode; + evtHandle->mode = queryValues.mode; sndHandles[idxFree].ucount = 0; sndHandles[idxFree].cardId = cardId; sndHandles[idxFree].evtHandle = evtHandle; - + // subscribe for sndctl events attached to devid err = snd_ctl_subscribe_events(evtHandle->ctlDev, 1); if (err < 0) { - afb_req_fail_f (request, "subscribe-fail", "Cannot subscribe events from devid=%s err=%d", queryValues.devid, err); + afb_req_fail_f(request, "subscribe-fail", "Cannot subscribe events from devid=%s err=%d", queryValues.devid, err); goto OnErrorExit; } - + // get pollfd attach to this sound board snd_ctl_poll_descriptors(evtHandle->ctlDev, &evtHandle->pfds, 1); // register sound event to binder main loop err = sd_event_add_io(afb_daemon_get_event_loop(), &evtHandle->src, evtHandle->pfds.fd, EPOLLIN, sndCtlEventCB, evtHandle); if (err < 0) { - afb_req_fail_f (request, "register-mainloop", "Cannot hook events to mainloop devid=%s err=%d", queryValues.devid, err); + afb_req_fail_f(request, "register-mainloop", "Cannot hook events to mainloop devid=%s err=%d", queryValues.devid, err); goto OnErrorExit; } // create binder event attached to devid name - evtHandle->afbevt = afb_daemon_make_event (queryValues.devid); - if (!afb_event_is_valid (evtHandle->afbevt)) { - afb_req_fail_f (request, "register-event", "Cannot register new binder event name=%s", queryValues.devid); - goto OnErrorExit; + evtHandle->afbevt = afb_daemon_make_event(queryValues.devid); + if (!afb_event_is_valid(evtHandle->afbevt)) { + afb_req_fail_f(request, "register-event", "Cannot register new binder event name=%s", queryValues.devid); + goto OnErrorExit; } // everything looks OK let's move forward - idx=idxFree; + idx = idxFree; } - + // subscribe to binder event err = afb_req_subscribe(request, evtHandle->afbevt); if (err != 0) { - afb_req_fail_f (request, "register-eventname", "Cannot subscribe binder event name=%s [invalid channel]", queryValues.devid); + afb_req_fail_f(request, "register-eventname", "Cannot subscribe binder event name=%s [invalid channel]", queryValues.devid); goto OnErrorExit; } // increase usage count and return success - sndHandles[idx].ucount ++; + sndHandles[idx].ucount++; afb_req_success(request, NULL, NULL); return; - - OnErrorExit: - if (ctlDev) snd_ctl_close(ctlDev); - return; + +OnErrorExit: + if (ctlDev) snd_ctl_close(ctlDev); + return; } // Subscribe to every Alsa CtlEvent send by a given board -STATIC json_object *alsaProbeCardId (afb_req request) { + +STATIC json_object *alsaProbeCardId(afb_req request) { char devid [10]; const char *ctlName, *shortname, *longname; int card, err, index, idx; json_object *responseJ; - snd_ctl_t *ctlDev; + snd_ctl_t *ctlDev; snd_ctl_card_info_t *cardinfo; - + const char *sndname = afb_req_value(request, "sndname"); if (sndname == NULL) { - afb_req_fail_f (request, "argument-missing", "sndname=SndCardName missing"); + afb_req_fail_f(request, "argument-missing", "sndname=SndCardName missing"); goto OnErrorExit; } - + // loop on potential card number - snd_ctl_card_info_alloca(&cardinfo); - for (card =0; card < MAX_SND_CARD; card++) { + snd_ctl_card_info_alloca(&cardinfo); + for (card = 0; card < MAX_SND_CARD; card++) { // build card devid and probe it - snprintf (devid, sizeof(devid), "hw:%i", card); - + snprintf(devid, sizeof (devid), "hw:%i", card); + // open control interface for devid err = snd_ctl_open(&ctlDev, devid, SND_CTL_READONLY); - if (err < 0) continue; - + if (err < 0) continue; + // extract sound card information snd_ctl_card_info(ctlDev, cardinfo); - index = snd_ctl_card_info_get_card(cardinfo); - ctlName = snd_ctl_card_info_get_id(cardinfo); - shortname= snd_ctl_card_info_get_name(cardinfo); + index = snd_ctl_card_info_get_card(cardinfo); + ctlName = snd_ctl_card_info_get_id(cardinfo); + shortname = snd_ctl_card_info_get_name(cardinfo); longname = snd_ctl_card_info_get_longname(cardinfo); - + // check if short|long name match - if (!strcmp (sndname, ctlName)) break; - if (!strcmp (sndname, shortname)) break; - if (!strcmp (sndname, longname)) break; + if (!strcmp(sndname, ctlName)) break; + if (!strcmp(sndname, shortname)) break; + if (!strcmp(sndname, longname)) break; } - + if (card == MAX_SND_CARD) { - afb_req_fail_f (request, "ctlDev-notfound", "Fail to find card with name=%s", sndname); + afb_req_fail_f(request, "ctlDev-notfound", "Fail to find card with name=%s", sndname); goto OnErrorExit; } - + // proxy ctlevent as a binder event responseJ = json_object_new_object(); - json_object_object_add(responseJ, "index" ,json_object_new_int (index)); - json_object_object_add(responseJ, "devid" ,json_object_new_string (devid)); - json_object_object_add(responseJ, "shortname" ,json_object_new_string (shortname)); - json_object_object_add(responseJ, "longname" ,json_object_new_string (longname)); - + json_object_object_add(responseJ, "index", json_object_new_int(index)); + json_object_object_add(responseJ, "devid", json_object_new_string(devid)); + json_object_object_add(responseJ, "shortname", json_object_new_string(shortname)); + json_object_object_add(responseJ, "longname", json_object_new_string(longname)); + // search for a HAL binder card mapping name to api prefix - for (idx=0; (idx < MAX_SND_CARD && cardRegistry[idx]); idx++) { - if (!strcmp (cardRegistry[idx]->shortname, shortname)) { - json_object_object_add(responseJ, "halapi",json_object_new_string (cardRegistry[idx]->apiprefix)); + for (idx = 0; (idx < MAX_SND_CARD && cardRegistry[idx]); idx++) { + if (!strcmp(cardRegistry[idx]->shortname, shortname)) { + json_object_object_add(responseJ, "halapi", json_object_new_string(cardRegistry[idx]->apiprefix)); break; } } - + return responseJ; - - OnErrorExit: - return NULL; + +OnErrorExit: + return NULL; } // Make alsaProbeCardId compatible with AFB request -PUBLIC void alsaGetCardId (afb_req request) { - - json_object *responseJ = alsaProbeCardId (request); + +PUBLIC void alsaGetCardId(afb_req request) { + + json_object *responseJ = alsaProbeCardId(request); if (responseJ) afb_req_success(request, responseJ, NULL); } // Return list of active resgistrated HAL with corresponding sndcard -PUBLIC void alsaActiveHal (afb_req request) { + +PUBLIC void alsaActiveHal(afb_req request) { json_object *responseJ = json_object_new_array(); - - for (int idx=0; idx < MAX_SND_CARD; idx++) { + + for (int idx = 0; idx < MAX_SND_CARD; idx++) { if (!cardRegistry[idx]) break; - + json_object *haldevJ = json_object_new_object(); json_object_object_add(haldevJ, "api", json_object_new_string(cardRegistry[idx]->apiprefix)); - if (cardRegistry[idx]->devid) json_object_object_add(haldevJ, "devid", json_object_new_string(cardRegistry[idx]->devid)); + if (cardRegistry[idx]->devid) json_object_object_add(haldevJ, "devid", json_object_new_string(cardRegistry[idx]->devid)); if (cardRegistry[idx]->shortname)json_object_object_add(haldevJ, "shortname", json_object_new_string(cardRegistry[idx]->shortname)); if (cardRegistry[idx]->longname) json_object_object_add(haldevJ, "longname", json_object_new_string(cardRegistry[idx]->longname)); - json_object_array_add (responseJ, haldevJ); + json_object_array_add(responseJ, haldevJ); } - + afb_req_success(request, responseJ, NULL); } // Register loaded HAL with board Name and API prefix -PUBLIC void alsaRegisterHal (afb_req request) { - static int index=0; + +PUBLIC void alsaRegisterHal(afb_req request) { + static int index = 0; json_object *responseJ; const char *shortname, *apiPrefix; - + apiPrefix = afb_req_value(request, "prefix"); if (apiPrefix == NULL) { - afb_req_fail_f (request, "argument-missing", "prefix=BindingApiPrefix missing"); + afb_req_fail_f(request, "argument-missing", "prefix=BindingApiPrefix missing"); goto OnErrorExit; } - + shortname = afb_req_value(request, "sndname"); if (shortname == NULL) { - afb_req_fail_f (request, "argument-missing", "sndname=SndCardName missing"); + afb_req_fail_f(request, "argument-missing", "sndname=SndCardName missing"); goto OnErrorExit; } - + if (index == MAX_SND_CARD) { - afb_req_fail_f (request, "alsahal-toomany", "Fail to register sndname=[%s]", shortname); - goto OnErrorExit; + afb_req_fail_f(request, "alsahal-toomany", "Fail to register sndname=[%s]", shortname); + goto OnErrorExit; } - + // alsaGetCardId should be check to register only valid card - responseJ= alsaProbeCardId(request); + responseJ = alsaProbeCardId(request); if (responseJ) { json_object *tmpJ; int done; - - cardRegistry[index]= malloc (sizeof(cardRegistry)); - cardRegistry[index]->apiprefix=strdup(apiPrefix); - cardRegistry[index]->shortname=strdup(shortname); - - done= json_object_object_get_ex (responseJ, "devid" , &tmpJ); - if (done) cardRegistry[index]->devid = strdup (json_object_get_string(tmpJ)); - else cardRegistry[index]->devid=NULL; - - done = json_object_object_get_ex (responseJ, "longname" , &tmpJ); - if (done) cardRegistry[index]->longname = strdup (json_object_get_string(tmpJ)); - else cardRegistry[index]->longname=NULL; - + + cardRegistry[index] = malloc(sizeof (cardRegistry)); + cardRegistry[index]->apiprefix = strdup(apiPrefix); + cardRegistry[index]->shortname = strdup(shortname); + + done = json_object_object_get_ex(responseJ, "devid", &tmpJ); + if (done) cardRegistry[index]->devid = strdup(json_object_get_string(tmpJ)); + else cardRegistry[index]->devid = NULL; + + done = json_object_object_get_ex(responseJ, "longname", &tmpJ); + if (done) cardRegistry[index]->longname = strdup(json_object_get_string(tmpJ)); + else cardRegistry[index]->longname = NULL; + // make sure register close with a null value index++; - cardRegistry[index]=NULL; - - afb_req_success(request, responseJ, NULL); + cardRegistry[index] = NULL; + + afb_req_success(request, responseJ, NULL); } - + // If OK return sound card Alsa ID+Info return; - - OnErrorExit: - return; + +OnErrorExit: + return; } diff --git a/ALSA-afb/Alsa-SetGet.c b/ALSA-afb/Alsa-SetGet.c index 5a41ec1..04f2929 100644 --- a/ALSA-afb/Alsa-SetGet.c +++ b/ALSA-afb/Alsa-SetGet.c @@ -23,84 +23,84 @@ https://github.com/DongheonKim/android_hardware_alsa-sound/blob/master/ALSAControl.cpp (ALSA low level API) https://www.kernel.org/doc/html/v4.11/sound/index.html -*/ + */ #define _GNU_SOURCE // needed for vasprintf #include "Alsa-ApiHat.h" -PUBLIC void NumidsListParse (ActionSetGetT action, queryValuesT *queryValues, ctlRequestT *ctlRequest) { +PUBLIC void NumidsListParse(ActionSetGetT action, queryValuesT *queryValues, ctlRequestT *ctlRequest) { int length; - for (int idx=0; idx < queryValues->count; idx ++) { + for (int idx = 0; idx < queryValues->count; idx++) { json_object *jId, *valuesJ; - ctlRequest[idx].used=0; + ctlRequest[idx].used = 0; ctlRequest[idx].valuesJ = NULL; - + // when only one NUMID is provided it might not be encapsulated in a JSON array - if (json_type_array == json_object_get_type(queryValues->numidsJ)) ctlRequest[idx].jToken = json_object_array_get_idx (queryValues->numidsJ, idx); - else ctlRequest[idx].jToken=queryValues->numidsJ; - - enum json_type jtype=json_object_get_type(ctlRequest[idx].jToken); + if (json_type_array == json_object_get_type(queryValues->numidsJ)) ctlRequest[idx].jToken = json_object_array_get_idx(queryValues->numidsJ, idx); + else ctlRequest[idx].jToken = queryValues->numidsJ; + + enum json_type jtype = json_object_get_type(ctlRequest[idx].jToken); switch (jtype) { - + case json_type_int: // if NUMID is not an array then it should be an integer numid with no value - ctlRequest[idx].numId = json_object_get_int (ctlRequest[idx].jToken); + ctlRequest[idx].numId = json_object_get_int(ctlRequest[idx].jToken); // Special SET simple short numid form [numid, [VAL1...VALX]] if (action == ACTION_SET && queryValues->count == 2) { - ctlRequest[idx].valuesJ = json_object_array_get_idx (queryValues->numidsJ, 1); - queryValues->count =1; //In this form count==2 , when only one numid is to set - idx ++; - continue; + ctlRequest[idx].valuesJ = json_object_array_get_idx(queryValues->numidsJ, 1); + queryValues->count = 1; //In this form count==2 , when only one numid is to set + idx++; + continue; } else - break; + break; - case json_type_array: + case json_type_array: // NUMID is an array 1st slot should be numid, optionally values may come after - length=json_object_array_length (ctlRequest[idx].jToken); + length = json_object_array_length(ctlRequest[idx].jToken); // numid must be in 1st slot of numid json array - ctlRequest[idx].numId =json_object_get_int(json_object_array_get_idx (ctlRequest[idx].jToken, 0)); - if (action == ACTION_GET) continue; + ctlRequest[idx].numId = json_object_get_int(json_object_array_get_idx(ctlRequest[idx].jToken, 0)); + if (action == ACTION_GET) continue; // In Write mode second value should be the value if (action == ACTION_SET && length == 2) { - ctlRequest[idx].valuesJ = json_object_array_get_idx (ctlRequest[idx].jToken, 1); - continue; + ctlRequest[idx].valuesJ = json_object_array_get_idx(ctlRequest[idx].jToken, 1); + continue; } // no numid value - ctlRequest[idx].used=-1; + ctlRequest[idx].used = -1; break; - + case json_type_object: // numid+values formated as {id:xxx, val:[aa,bb...,nn]} - if (!json_object_object_get_ex (ctlRequest[idx].jToken,"id", &jId) || !json_object_object_get_ex (ctlRequest[idx].jToken,"val",&valuesJ)) { + if (!json_object_object_get_ex(ctlRequest[idx].jToken, "id", &jId) || !json_object_object_get_ex(ctlRequest[idx].jToken, "val", &valuesJ)) { AFB_NOTICE("Invalid Json=%s missing 'id'|'val'", json_object_get_string(ctlRequest[idx].jToken)); - ctlRequest[idx].used=-1; + ctlRequest[idx].used = -1; } else { - ctlRequest[idx].numId =json_object_get_int(jId); - if (action == ACTION_SET) ctlRequest[idx].valuesJ=valuesJ; + ctlRequest[idx].numId = json_object_get_int(jId); + if (action == ACTION_SET) ctlRequest[idx].valuesJ = valuesJ; } break; - - default: - ctlRequest[idx].used=-1; + + default: + ctlRequest[idx].used = -1; } } } -STATIC json_object *DB2StringJsonOject (long dB) { +STATIC json_object *DB2StringJsonOject(long dB) { char label [20]; - if (dB < 0) { - snprintf(label, sizeof(label), "-%li.%02lidB", -dB / 100, -dB % 100); - } else { - snprintf(label, sizeof(label), "%li.%02lidB", dB / 100, dB % 100); - } + if (dB < 0) { + snprintf(label, sizeof (label), "-%li.%02lidB", -dB / 100, -dB % 100); + } else { + snprintf(label, sizeof (label), "%li.%02lidB", dB / 100, dB % 100); + } // json function takes care of string copy - return (json_object_new_string (label)); + return (json_object_new_string(label)); } @@ -160,8 +160,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, json_object_new_string(label)); } else { - json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); - } + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbscaleJson, "array", arrayJson); @@ -171,10 +171,10 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode json_object_object_add(dbscaleJson, "step", DB2StringJsonOject(tlv[3] & 0xffff)); json_object_object_add(dbscaleJson, "mute", DB2StringJsonOject((tlv[3] >> 16) & 1)); } else { - json_object_object_add(dbscaleJson, "min", json_object_new_int((int) tlv[2])); + json_object_object_add(dbscaleJson, "min", json_object_new_int((int) tlv[2])); json_object_object_add(dbscaleJson, "step", json_object_new_int(tlv[3] & 0xffff)); - json_object_object_add(dbscaleJson, "mute", json_object_new_int((tlv[3] >> 16) & 1)); - } + json_object_object_add(dbscaleJson, "mute", json_object_new_int((tlv[3] >> 16) & 1)); + } } json_object_object_add(decodeTlvJson, "dbscale", dbscaleJson); break; @@ -192,8 +192,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, json_object_new_string(label)); } else { - json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); - } + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbLinearJson, "offset", arrayJson); @@ -203,8 +203,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode json_object_object_add(dbLinearJson, "max", DB2StringJsonOject((int) tlv[3])); } else { json_object_object_add(dbLinearJson, "min", json_object_new_int((int) tlv[2])); - json_object_object_add(dbLinearJson, "max", json_object_new_int((int) tlv[3])); - } + json_object_object_add(dbLinearJson, "max", json_object_new_int((int) tlv[3])); + } } json_object_object_add(decodeTlvJson, "dblinear", dbLinearJson); break; @@ -223,8 +223,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, json_object_new_string(label)); } else { - json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); - } + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbRangeJson, "dbrange", arrayJson); @@ -257,8 +257,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, json_object_new_string(label)); } else { - json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); - } + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbMinMaxJson, "array", arrayJson); @@ -269,8 +269,8 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode json_object_object_add(dbMinMaxJson, "max", DB2StringJsonOject((int) tlv[3])); } else { json_object_object_add(dbMinMaxJson, "min", json_object_new_int((int) tlv[2])); - json_object_object_add(dbMinMaxJson, "max", json_object_new_int((int) tlv[3])); - } + json_object_object_add(dbMinMaxJson, "max", json_object_new_int((int) tlv[3])); + } } if (type == SND_CTL_TLVT_DB_MINMAX_MUTE) { @@ -325,144 +325,148 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode // retreive info for one given card -STATIC json_object* alsaCardProbe (const char *rqtSndId) { - const char *info, *name; + +STATIC json_object* alsaCardProbe(const char *rqtSndId) { + const char *info, *name; const char *devid, *driver; json_object *ctlDev; - snd_ctl_t *handle; + snd_ctl_t *handle; snd_ctl_card_info_t *cardinfo; int err; if ((err = snd_ctl_open(&handle, rqtSndId, 0)) < 0) { - AFB_INFO ("alsaCardProbe '%s' Not Found", rqtSndId); + AFB_INFO("alsaCardProbe '%s' Not Found", rqtSndId); return NULL; } snd_ctl_card_info_alloca(&cardinfo); if ((err = snd_ctl_card_info(handle, cardinfo)) < 0) { snd_ctl_close(handle); - AFB_WARNING ("SndCard '%s' info error: %s", rqtSndId, snd_strerror(err)); + AFB_WARNING("SndCard '%s' info error: %s", rqtSndId, snd_strerror(err)); return NULL; } // start a new json object to store card info ctlDev = json_object_new_object(); - devid= snd_ctl_card_info_get_id(cardinfo); - json_object_object_add (ctlDev, "devid" , json_object_new_string(devid)); - name = snd_ctl_card_info_get_name(cardinfo); - json_object_object_add (ctlDev, "name", json_object_new_string (name)); + devid = snd_ctl_card_info_get_id(cardinfo); + json_object_object_add(ctlDev, "devid", json_object_new_string(devid)); + name = snd_ctl_card_info_get_name(cardinfo); + json_object_object_add(ctlDev, "name", json_object_new_string(name)); if (AFB_GET_VERBOSITY > 1) { - json_object_object_add (ctlDev, "devid", json_object_new_string(rqtSndId)); - driver= snd_ctl_card_info_get_driver(cardinfo); - json_object_object_add (ctlDev, "driver" , json_object_new_string(driver)); - info = strdup(snd_ctl_card_info_get_longname (cardinfo)); - json_object_object_add (ctlDev, "info" , json_object_new_string (info)); - AFB_INFO ("AJG: Soundcard Devid=%-5s devid=%-7s Name=%s\n", rqtSndId, devid, info); + json_object_object_add(ctlDev, "devid", json_object_new_string(rqtSndId)); + driver = snd_ctl_card_info_get_driver(cardinfo); + json_object_object_add(ctlDev, "driver", json_object_new_string(driver)); + info = strdup(snd_ctl_card_info_get_longname(cardinfo)); + json_object_object_add(ctlDev, "info", json_object_new_string(info)); + AFB_INFO("AJG: Soundcard Devid=%-5s devid=%-7s Name=%s\n", rqtSndId, devid, info); } // free card handle and return info - snd_ctl_close(handle); + snd_ctl_close(handle); return (ctlDev); } // Loop on every potential Sound card and register active one -PUBLIC void alsaGetInfo (afb_req request) { - int card; + +PUBLIC void alsaGetInfo(afb_req request) { + int card; json_object *ctlDev, *ctlDevs; char devid[32]; - + const char *rqtSndId = afb_req_value(request, "devid"); // if no specific card requested loop on all if (rqtSndId != NULL) { // only one card was requested let's probe it - ctlDev = alsaCardProbe (rqtSndId); + ctlDev = alsaCardProbe(rqtSndId); if (ctlDev != NULL) afb_req_success(request, ctlDev, NULL); - else afb_req_fail_f (request, "sndscard-notfound", "SndCard '%s' Not Found", rqtSndId); - + else afb_req_fail_f(request, "sndscard-notfound", "SndCard '%s' Not Found", rqtSndId); + } else { // return an array of ctlDev - ctlDevs =json_object_new_array(); + ctlDevs = json_object_new_array(); // loop on potential card number - for (card =0; card < MAX_SND_CARD; card++) { + for (card = 0; card < MAX_SND_CARD; card++) { // build card devid and probe it - snprintf (devid, sizeof(devid), "hw:%i", card); - ctlDev = alsaCardProbe (devid); + snprintf(devid, sizeof (devid), "hw:%i", card); + ctlDev = alsaCardProbe(devid); // Alsa has hole within card list [ignore them] if (ctlDev != NULL) { // add current ctlDev to ctlDevs object - json_object_array_add (ctlDevs, ctlDev); - } + json_object_array_add(ctlDevs, ctlDev); + } } - afb_req_success (request, ctlDevs, NULL); + afb_req_success(request, ctlDevs, NULL); } } // pack Alsa element's ACL into a JSON object -STATIC json_object *getControlAcl (snd_ctl_elem_info_t *info) { + +STATIC json_object *getControlAcl(snd_ctl_elem_info_t *info) { json_object * jsonAclCtl = json_object_new_object(); - json_object_object_add (jsonAclCtl, "read" , json_object_new_boolean(snd_ctl_elem_info_is_readable(info))); - json_object_object_add (jsonAclCtl, "write" , json_object_new_boolean(snd_ctl_elem_info_is_writable(info))); - json_object_object_add (jsonAclCtl, "inact" , json_object_new_boolean(snd_ctl_elem_info_is_inactive(info))); - json_object_object_add (jsonAclCtl, "volat" , json_object_new_boolean(snd_ctl_elem_info_is_volatile(info))); - json_object_object_add (jsonAclCtl, "lock" , json_object_new_boolean(snd_ctl_elem_info_is_locked(info))); + json_object_object_add(jsonAclCtl, "read", json_object_new_boolean(snd_ctl_elem_info_is_readable(info))); + json_object_object_add(jsonAclCtl, "write", json_object_new_boolean(snd_ctl_elem_info_is_writable(info))); + json_object_object_add(jsonAclCtl, "inact", json_object_new_boolean(snd_ctl_elem_info_is_inactive(info))); + json_object_object_add(jsonAclCtl, "volat", json_object_new_boolean(snd_ctl_elem_info_is_volatile(info))); + json_object_object_add(jsonAclCtl, "lock", json_object_new_boolean(snd_ctl_elem_info_is_locked(info))); // if TLV is readable we insert its ACL if (!snd_ctl_elem_info_is_tlv_readable(info)) { json_object * jsonTlv = json_object_new_object(); - json_object_object_add (jsonTlv, "read" , json_object_new_boolean(snd_ctl_elem_info_is_tlv_readable(info))); - json_object_object_add (jsonTlv, "write" , json_object_new_boolean(snd_ctl_elem_info_is_tlv_writable(info))); - json_object_object_add (jsonTlv, "command", json_object_new_boolean(snd_ctl_elem_info_is_tlv_commandable(info))); + json_object_object_add(jsonTlv, "read", json_object_new_boolean(snd_ctl_elem_info_is_tlv_readable(info))); + json_object_object_add(jsonTlv, "write", json_object_new_boolean(snd_ctl_elem_info_is_tlv_writable(info))); + json_object_object_add(jsonTlv, "command", json_object_new_boolean(snd_ctl_elem_info_is_tlv_commandable(info))); - json_object_object_add (jsonAclCtl, "tlv", jsonTlv); + json_object_object_add(jsonAclCtl, "tlv", jsonTlv); } return (jsonAclCtl); } // process ALSA control and store resulting value into ctlRequest -PUBLIC int alsaSetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest) { + +PUBLIC int alsaSetSingleCtl(snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest) { snd_ctl_elem_value_t *elemData; - snd_ctl_elem_info_t *elemInfo; - int count, length, err, valueIsArray=0; + snd_ctl_elem_info_t *elemInfo; + int count, length, err, valueIsArray = 0; // let's make sure we are processing the right control - if (ctlRequest->numId != snd_ctl_elem_id_get_numid (elemId)) goto OnErrorExit; - + if (ctlRequest->numId != snd_ctl_elem_id_get_numid(elemId)) goto OnErrorExit; + // set info event ID and get value snd_ctl_elem_info_alloca(&elemInfo); - snd_ctl_elem_info_set_id(elemInfo, elemId); // map ctlInfo to ctlId elemInfo is updated !!! + snd_ctl_elem_info_set_id(elemInfo, elemId); // map ctlInfo to ctlId elemInfo is updated !!! if (snd_ctl_elem_info(ctlDev, elemInfo) < 0) { - AFB_NOTICE( "Fail to load ALSA NUMID=%d Values='%s'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ)); + AFB_NOTICE("Fail to load ALSA NUMID=%d Values='%s'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ)); goto OnErrorExit; } - + if (!snd_ctl_elem_info_is_writable(elemInfo)) { - AFB_NOTICE( "Not Writable ALSA NUMID=%d Values='%s'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ)); + AFB_NOTICE("Not Writable ALSA NUMID=%d Values='%s'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ)); goto OnErrorExit; } - - count = snd_ctl_elem_info_get_count (elemInfo); + + count = snd_ctl_elem_info_get_count(elemInfo); if (count == 0) goto OnErrorExit; - - enum json_type jtype= json_object_get_type(ctlRequest->valuesJ); + + enum json_type jtype = json_object_get_type(ctlRequest->valuesJ); switch (jtype) { case json_type_array: - length = json_object_array_length (ctlRequest->valuesJ); - valueIsArray=1; + length = json_object_array_length(ctlRequest->valuesJ); + valueIsArray = 1; break; case json_type_int: - length=1; - valueIsArray=0; - break; + length = 1; + valueIsArray = 0; + break; default: length = 0; break; @@ -470,193 +474,198 @@ PUBLIC int alsaSetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRe if (length == 0) { - AFB_NOTICE( "Invalid values NUMID='%d' Values='%s' count='%d' wanted='%d'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ), length, count); + AFB_NOTICE("Invalid values NUMID='%d' Values='%s' count='%d' wanted='%d'", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ), length, count); goto OnErrorExit; } - snd_ctl_elem_value_alloca(&elemData); - snd_ctl_elem_value_set_id(elemData, elemId); // map ctlInfo to ctlId elemInfo is updated !!! - if (snd_ctl_elem_read(ctlDev, elemData) < 0) goto OnErrorExit; + snd_ctl_elem_value_alloca(&elemData); + snd_ctl_elem_value_set_id(elemData, elemId); // map ctlInfo to ctlId elemInfo is updated !!! + if (snd_ctl_elem_read(ctlDev, elemData) < 0) goto OnErrorExit; // Loop on every control value and push to sndcard - for (int index=0; index < count; index++) { + for (int index = 0; index < count; index++) { json_object *element; int value; // when not enough value duplicate last provided one - if (!valueIsArray) element= ctlRequest->valuesJ; + if (!valueIsArray) element = ctlRequest->valuesJ; else { - if (index < length) element= json_object_array_get_idx(ctlRequest->valuesJ, index); - else element= json_object_array_get_idx(ctlRequest->valuesJ, length-1); + if (index < length) element = json_object_array_get_idx(ctlRequest->valuesJ, index); + else element = json_object_array_get_idx(ctlRequest->valuesJ, length - 1); } - - value= json_object_get_int (element); + + value = json_object_get_int(element); snd_ctl_elem_value_set_integer(elemData, index, value); } - + err = snd_ctl_elem_write(ctlDev, elemData); if (err < 0) { - AFB_NOTICE( "Fail to write ALSA NUMID=%d Values='%s' Error=%s", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ), snd_strerror(err)); + AFB_NOTICE("Fail to write ALSA NUMID=%d Values='%s' Error=%s", ctlRequest->numId, json_object_get_string(ctlRequest->valuesJ), snd_strerror(err)); goto OnErrorExit; } - - ctlRequest->used=1; + + ctlRequest->used = 1; return 0; - - OnErrorExit: - ctlRequest->used=-1; - return -1; + +OnErrorExit: + ctlRequest->used = -1; + return -1; } // process ALSA control and store then into ctlRequest -PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, halQueryMode queryMode) { - snd_ctl_elem_type_t elemType; + +PUBLIC int alsaGetSingleCtl(snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, halQueryMode queryMode) { + snd_ctl_elem_type_t elemType; snd_ctl_elem_value_t *elemData; - snd_ctl_elem_info_t *elemInfo; + snd_ctl_elem_info_t *elemInfo; int count, idx, err; - + // set info event ID and get value snd_ctl_elem_info_alloca(&elemInfo); - snd_ctl_elem_info_set_id(elemInfo, elemId); + snd_ctl_elem_info_set_id(elemInfo, elemId); if (snd_ctl_elem_info(ctlDev, elemInfo) < 0) goto OnErrorExit; - count = snd_ctl_elem_info_get_count (elemInfo); + count = snd_ctl_elem_info_get_count(elemInfo); if (count == 0) goto OnErrorExit; - + if (!snd_ctl_elem_info_is_readable(elemInfo)) goto OnErrorExit; - elemType = snd_ctl_elem_info_get_type(elemInfo); + elemType = snd_ctl_elem_info_get_type(elemInfo); - snd_ctl_elem_value_alloca(&elemData); - snd_ctl_elem_value_set_id(elemData, elemId); + snd_ctl_elem_value_alloca(&elemData); + snd_ctl_elem_value_set_id(elemData, elemId); if (snd_ctl_elem_read(ctlDev, elemData) < 0) goto OnErrorExit; - - int numid= snd_ctl_elem_info_get_numid(elemInfo); - ctlRequest->valuesJ= json_object_new_object(); - json_object_object_add (ctlRequest->valuesJ,"id" , json_object_new_int(numid)); - if (queryMode >= 1) json_object_object_add (ctlRequest->valuesJ,"name" , json_object_new_string(snd_ctl_elem_id_get_name (elemId))); - if (queryMode >= 2) json_object_object_add (ctlRequest->valuesJ,"iface" , json_object_new_string(snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(elemId)))); - if (queryMode >= 3) json_object_object_add (ctlRequest->valuesJ,"actif", json_object_new_boolean(!snd_ctl_elem_info_is_inactive(elemInfo))); + int numid = snd_ctl_elem_info_get_numid(elemInfo); + + ctlRequest->valuesJ = json_object_new_object(); + json_object_object_add(ctlRequest->valuesJ, "id", json_object_new_int(numid)); + if (queryMode >= 1) json_object_object_add(ctlRequest->valuesJ, "name", json_object_new_string(snd_ctl_elem_id_get_name(elemId))); + if (queryMode >= 2) json_object_object_add(ctlRequest->valuesJ, "iface", json_object_new_string(snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(elemId)))); + if (queryMode >= 3) json_object_object_add(ctlRequest->valuesJ, "actif", json_object_new_boolean(!snd_ctl_elem_info_is_inactive(elemInfo))); json_object *jsonValuesCtl = json_object_new_array(); for (idx = 0; idx < count; idx++) { // start from one in amixer.c !!! switch (elemType) { - case SND_CTL_ELEM_TYPE_BOOLEAN: { - json_object_array_add (jsonValuesCtl, json_object_new_boolean (snd_ctl_elem_value_get_boolean(elemData, idx))); + case SND_CTL_ELEM_TYPE_BOOLEAN: + { + json_object_array_add(jsonValuesCtl, json_object_new_boolean(snd_ctl_elem_value_get_boolean(elemData, idx))); break; - } - case SND_CTL_ELEM_TYPE_INTEGER: - json_object_array_add (jsonValuesCtl, json_object_new_int ((int)snd_ctl_elem_value_get_integer(elemData, idx))); + } + case SND_CTL_ELEM_TYPE_INTEGER: + json_object_array_add(jsonValuesCtl, json_object_new_int((int) snd_ctl_elem_value_get_integer(elemData, idx))); break; - case SND_CTL_ELEM_TYPE_INTEGER64: - json_object_array_add (jsonValuesCtl, json_object_new_int64 (snd_ctl_elem_value_get_integer64(elemData, idx))); + case SND_CTL_ELEM_TYPE_INTEGER64: + json_object_array_add(jsonValuesCtl, json_object_new_int64(snd_ctl_elem_value_get_integer64(elemData, idx))); break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - json_object_array_add (jsonValuesCtl, json_object_new_int (snd_ctl_elem_value_get_enumerated(elemData, idx))); + case SND_CTL_ELEM_TYPE_ENUMERATED: + json_object_array_add(jsonValuesCtl, json_object_new_int(snd_ctl_elem_value_get_enumerated(elemData, idx))); break; - case SND_CTL_ELEM_TYPE_BYTES: - json_object_array_add (jsonValuesCtl, json_object_new_int ((int)snd_ctl_elem_value_get_byte(elemData, idx))); + case SND_CTL_ELEM_TYPE_BYTES: + json_object_array_add(jsonValuesCtl, json_object_new_int((int) snd_ctl_elem_value_get_byte(elemData, idx))); break; - case SND_CTL_ELEM_TYPE_IEC958: { + case SND_CTL_ELEM_TYPE_IEC958: + { json_object *jsonIec958Ctl = json_object_new_object(); snd_aes_iec958_t iec958; snd_ctl_elem_value_get_iec958(elemData, &iec958); - json_object_object_add (jsonIec958Ctl,"AES0",json_object_new_int(iec958.status[0])); - json_object_object_add (jsonIec958Ctl,"AES1",json_object_new_int(iec958.status[1])); - json_object_object_add (jsonIec958Ctl,"AES2",json_object_new_int(iec958.status[2])); - json_object_object_add (jsonIec958Ctl,"AES3",json_object_new_int(iec958.status[3])); - json_object_array_add (jsonValuesCtl, jsonIec958Ctl); + json_object_object_add(jsonIec958Ctl, "AES0", json_object_new_int(iec958.status[0])); + json_object_object_add(jsonIec958Ctl, "AES1", json_object_new_int(iec958.status[1])); + json_object_object_add(jsonIec958Ctl, "AES2", json_object_new_int(iec958.status[2])); + json_object_object_add(jsonIec958Ctl, "AES3", json_object_new_int(iec958.status[3])); + json_object_array_add(jsonValuesCtl, jsonIec958Ctl); break; - } - default: - json_object_array_add (jsonValuesCtl, json_object_new_string ("?unknown?")); + } + default: + json_object_array_add(jsonValuesCtl, json_object_new_string("?unknown?")); break; } } - json_object_object_add (ctlRequest->valuesJ,"val",jsonValuesCtl); + json_object_object_add(ctlRequest->valuesJ, "val", jsonValuesCtl); - if (queryMode >= 1) { // in simple mode do not print usable values + if (queryMode >= 1) { // in simple mode do not print usable values json_object *jsonClassCtl = json_object_new_object(); - json_object_object_add (jsonClassCtl,"type" , json_object_new_int(elemType)); - json_object_object_add (jsonClassCtl,"count", json_object_new_int(count)); + json_object_object_add(jsonClassCtl, "type", json_object_new_int(elemType)); + json_object_object_add(jsonClassCtl, "count", json_object_new_int(count)); switch (elemType) { - case SND_CTL_ELEM_TYPE_INTEGER: - json_object_object_add (jsonClassCtl,"min", json_object_new_int((int)snd_ctl_elem_info_get_min(elemInfo))); - json_object_object_add (jsonClassCtl,"max", json_object_new_int((int)snd_ctl_elem_info_get_max(elemInfo))); - json_object_object_add (jsonClassCtl,"step", json_object_new_int((int)snd_ctl_elem_info_get_step(elemInfo))); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - json_object_object_add (jsonClassCtl,"min", json_object_new_int64(snd_ctl_elem_info_get_min64(elemInfo))); - json_object_object_add (jsonClassCtl,"max", json_object_new_int64(snd_ctl_elem_info_get_max64(elemInfo))); - json_object_object_add (jsonClassCtl,"step", json_object_new_int64(snd_ctl_elem_info_get_step64(elemInfo))); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: { - unsigned int item, items = snd_ctl_elem_info_get_items(elemInfo); - json_object *jsonEnum = json_object_new_array(); - - for (item = 0; item < items; item++) { - snd_ctl_elem_info_set_item(elemInfo, item); - if ((err = snd_ctl_elem_info(ctlDev, elemInfo)) >= 0) { - json_object_array_add (jsonEnum, json_object_new_string(snd_ctl_elem_info_get_item_name(elemInfo))); - } - } - json_object_object_add (jsonClassCtl, "enums",jsonEnum); - break; + case SND_CTL_ELEM_TYPE_INTEGER: + json_object_object_add(jsonClassCtl, "min", json_object_new_int((int) snd_ctl_elem_info_get_min(elemInfo))); + json_object_object_add(jsonClassCtl, "max", json_object_new_int((int) snd_ctl_elem_info_get_max(elemInfo))); + json_object_object_add(jsonClassCtl, "step", json_object_new_int((int) snd_ctl_elem_info_get_step(elemInfo))); + break; + case SND_CTL_ELEM_TYPE_INTEGER64: + json_object_object_add(jsonClassCtl, "min", json_object_new_int64(snd_ctl_elem_info_get_min64(elemInfo))); + json_object_object_add(jsonClassCtl, "max", json_object_new_int64(snd_ctl_elem_info_get_max64(elemInfo))); + json_object_object_add(jsonClassCtl, "step", json_object_new_int64(snd_ctl_elem_info_get_step64(elemInfo))); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + { + unsigned int item, items = snd_ctl_elem_info_get_items(elemInfo); + json_object *jsonEnum = json_object_new_array(); + + for (item = 0; item < items; item++) { + snd_ctl_elem_info_set_item(elemInfo, item); + if ((err = snd_ctl_elem_info(ctlDev, elemInfo)) >= 0) { + json_object_array_add(jsonEnum, json_object_new_string(snd_ctl_elem_info_get_item_name(elemInfo))); + } } - default: break; // ignore any unknown type + json_object_object_add(jsonClassCtl, "enums", jsonEnum); + break; + } + default: break; // ignore any unknown type } - + // add collected class info with associated ACLs - json_object_object_add (ctlRequest->valuesJ,"ctl", jsonClassCtl); - - if (queryMode >= QUERY_FULL) json_object_object_add (ctlRequest->valuesJ,"acl" , getControlAcl (elemInfo)); + json_object_object_add(ctlRequest->valuesJ, "ctl", jsonClassCtl); + + if (queryMode >= QUERY_FULL) json_object_object_add(ctlRequest->valuesJ, "acl", getControlAcl(elemInfo)); // check for tlv [direct port from amixer.c] if (snd_ctl_elem_info_is_tlv_readable(elemInfo)) { unsigned int *tlv = alloca(TLV_BYTE_SIZE); if ((err = snd_ctl_elem_tlv_read(ctlDev, elemId, tlv, 4096)) < 0) { - AFB_NOTICE ("Control numid=%d err=%s element TLV read error\n", numid, snd_strerror(err)); - goto OnErrorExit; + AFB_NOTICE("Control numid=%d err=%s element TLV read error\n", numid, snd_strerror(err)); + goto OnErrorExit; } else { - json_object_object_add (ctlRequest->valuesJ,"tlv", decodeTlv (tlv, TLV_BYTE_SIZE, queryMode)); - } + json_object_object_add(ctlRequest->valuesJ, "tlv", decodeTlv(tlv, TLV_BYTE_SIZE, queryMode)); + } } } - - ctlRequest->used=1; + + ctlRequest->used = 1; return 0; - - OnErrorExit: - ctlRequest->used=-1; - return -1; + +OnErrorExit: + ctlRequest->used = -1; + return -1; } // assign multiple control to the same value -STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { + +STATIC void alsaSetGetCtls(ActionSetGetT action, afb_req request) { ctlRequestT *ctlRequest; - const char *warmsg=NULL; - int err=0, status=0, done; + const char *warmsg = NULL; + int err = 0, status = 0, done; unsigned int ctlCount; snd_ctl_t *ctlDev; - snd_ctl_elem_list_t *ctlList; + snd_ctl_elem_list_t *ctlList; queryValuesT queryValues; json_object *queryJ, *numidsJ, *sndctls; - - queryJ = alsaCheckQuery (request, &queryValues); + + queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; - + // Prase Numids + optional values - done= json_object_object_get_ex (queryJ, "ctl" , &numidsJ); - if (!done) queryValues.count=0; + done = json_object_object_get_ex(queryJ, "ctl", &numidsJ); + if (!done) queryValues.count = 0; else { - enum json_type jtype= json_object_get_type(numidsJ); + enum json_type jtype = json_object_get_type(numidsJ); switch (jtype) { case json_type_array: queryValues.numidsJ = numidsJ; - queryValues.count = json_object_array_length (numidsJ); + queryValues.count = json_object_array_length(numidsJ); break; case json_type_int: @@ -664,85 +673,85 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { queryValues.count = 1; queryValues.numidsJ = numidsJ; break; - - default: - afb_req_fail_f (request, "numid-notarray","NumId=%s NumId not valid JSON array", json_object_get_string(numidsJ)); - goto OnErrorExit; + + default: + afb_req_fail_f(request, "numid-notarray", "NumId=%s NumId not valid JSON array", json_object_get_string(numidsJ)); + goto OnErrorExit; } } - + if ((err = snd_ctl_open(&ctlDev, queryValues.devid, 0)) < 0) { - afb_req_fail_f (request, "sndcrl-notfound","devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); + afb_req_fail_f(request, "sndcrl-notfound", "devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } - - snd_ctl_elem_list_alloca(&ctlList); - if ((err = snd_ctl_elem_list (ctlDev, ctlList)) < 0) { - afb_req_fail_f (request, "listInit-failed","devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); + + snd_ctl_elem_list_alloca(&ctlList); + if ((err = snd_ctl_elem_list(ctlDev, ctlList)) < 0) { + afb_req_fail_f(request, "listInit-failed", "devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } if ((err = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) { - afb_req_fail_f (request, "listAlloc-failed","devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); + afb_req_fail_f(request, "listAlloc-failed", "devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } - - if ((err = snd_ctl_elem_list (ctlDev, ctlList)) < 0) { - afb_req_fail_f (request, "listOpen-failed","devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); + + if ((err = snd_ctl_elem_list(ctlDev, ctlList)) < 0) { + afb_req_fail_f(request, "listOpen-failed", "devid='%s' load fail error=%s\n", queryValues.devid, snd_strerror(err)); goto OnErrorExit; } // Parse numids string (empty == all) - ctlCount= snd_ctl_elem_list_get_used(ctlList); - if (queryValues.count==0) { - ctlRequest= alloca (sizeof(ctlRequestT)*(ctlCount)); + ctlCount = snd_ctl_elem_list_get_used(ctlList); + if (queryValues.count == 0) { + ctlRequest = alloca(sizeof (ctlRequestT)*(ctlCount)); } else { - ctlRequest= alloca (sizeof(ctlRequestT)*(queryValues.count)); - NumidsListParse (action, &queryValues, ctlRequest); + ctlRequest = alloca(sizeof (ctlRequestT)*(queryValues.count)); + NumidsListParse(action, &queryValues, ctlRequest); } - + // if more than one crl requested prepare an array for response - if (queryValues.count!= 1 && action==ACTION_GET) sndctls=json_object_new_array(); - else sndctls=NULL; - + if (queryValues.count != 1 && action == ACTION_GET) sndctls = json_object_new_array(); + else sndctls = NULL; + // Loop on all ctlDev controls - for (int ctlIndex=0; ctlIndex < ctlCount; ctlIndex++) { - unsigned int selected=0; + for (int ctlIndex = 0; ctlIndex < ctlCount; ctlIndex++) { + unsigned int selected = 0; int jdx; - + if (queryValues.count == 0 && action == ACTION_GET) { - selected=1; // check is this numid is selected within query - jdx = ctlIndex; // map all existing ctl as requested + selected = 1; // check is this numid is selected within query + jdx = ctlIndex; // map all existing ctl as requested } else { - int numid = snd_ctl_elem_list_get_numid(ctlList, ctlIndex); + int numid = snd_ctl_elem_list_get_numid(ctlList, ctlIndex); if (numid < 0) { AFB_NOTICE("snd_ctl_elem_list_get_numid index=%d fail", ctlIndex); continue; } // check if current control was requested in query numids list - for (jdx=0; jdx < queryValues.count; jdx++) { + for (jdx = 0; jdx < queryValues.count; jdx++) { if (numid == ctlRequest[jdx].numId) { selected = 1; break; } - } + } } - + // control is selected open ctlid and get value if (selected) { - snd_ctl_elem_id_t *elemId; + snd_ctl_elem_id_t *elemId; snd_ctl_elem_id_alloca(&elemId); - - snd_ctl_elem_list_get_id (ctlList, ctlIndex, elemId); + + snd_ctl_elem_list_get_id(ctlList, ctlIndex, elemId); switch (action) { case ACTION_GET: - err = alsaGetSingleCtl (ctlDev, elemId, &ctlRequest[jdx], queryValues.mode); + err = alsaGetSingleCtl(ctlDev, elemId, &ctlRequest[jdx], queryValues.mode); break; - + case ACTION_SET: - err = alsaSetSingleCtl (ctlDev, elemId, &ctlRequest[jdx]); + err = alsaSetSingleCtl(ctlDev, elemId, &ctlRequest[jdx]); break; - + default: err = 1; } @@ -750,51 +759,51 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { else { // Do not embed response in an array when only one ctl was requested if (action == ACTION_GET) { - if (queryValues.count > 1) json_object_array_add (sndctls, ctlRequest[jdx].valuesJ); + if (queryValues.count > 1) json_object_array_add(sndctls, ctlRequest[jdx].valuesJ); else sndctls = ctlRequest[jdx].valuesJ; } } } } - + // if we had error let's add them into response message info json_object *warningsJ = json_object_new_array(); - for (int jdx=0; jdx < queryValues.count; jdx++) { + for (int jdx = 0; jdx < queryValues.count; jdx++) { if (ctlRequest[jdx].used <= 0) { json_object *failctl = json_object_new_object(); - if (ctlRequest[jdx].numId == -1) json_object_object_add (failctl, "warning", json_object_new_string ("Numid Invalid")); + if (ctlRequest[jdx].numId == -1) json_object_object_add(failctl, "warning", json_object_new_string("Numid Invalid")); else { - if (ctlRequest[jdx].used == 0) json_object_object_add (failctl, "warning", json_object_new_string ("Numid Does Not Exist")); - if (ctlRequest[jdx].used == -1) json_object_object_add (failctl, "warning", json_object_new_string ("Value Refused")); + if (ctlRequest[jdx].used == 0) json_object_object_add(failctl, "warning", json_object_new_string("Numid Does Not Exist")); + if (ctlRequest[jdx].used == -1) json_object_object_add(failctl, "warning", json_object_new_string("Value Refused")); } - - json_object_object_add (failctl, "ctl", ctlRequest[jdx].jToken); - - json_object_array_add (warningsJ, failctl); + + json_object_object_add(failctl, "ctl", ctlRequest[jdx].jToken); + + json_object_array_add(warningsJ, failctl); } /* WARNING!!!! Check with Jose why following put free valuesJ if (ctlRequest[jdx].jToken) json_object_put(ctlRequest[jdx].jToken); if (ctlRequest[jdx].valuesJ) json_object_put(ctlRequest[jdx].valuesJ); - */ + */ } - - if (json_object_array_length(warningsJ) > 0) warmsg=json_object_get_string (warningsJ); + + if (json_object_array_length(warningsJ) > 0) warmsg = json_object_get_string(warningsJ); else json_object_put(warningsJ); - + // send response+warning if any - afb_req_success (request, sndctls, warmsg); + afb_req_success(request, sndctls, warmsg); snd_ctl_elem_list_clear(ctlList); - - OnErrorExit: - return; + +OnErrorExit: + return; } -PUBLIC void alsaGetCtls (afb_req request) { - alsaSetGetCtls (ACTION_GET, request); +PUBLIC void alsaGetCtls(afb_req request) { + alsaSetGetCtls(ACTION_GET, request); } - -PUBLIC void alsaSetCtls (afb_req request) { - alsaSetGetCtls (ACTION_SET, request); + +PUBLIC void alsaSetCtls(afb_req request) { + alsaSetGetCtls(ACTION_SET, request); } diff --git a/ALSA-afb/Alsa-Ucm.c b/ALSA-afb/Alsa-Ucm.c index ee2b36d..016fe10 100644 --- a/ALSA-afb/Alsa-Ucm.c +++ b/ALSA-afb/Alsa-Ucm.c @@ -32,7 +32,7 @@ - alsaucm -c 'HDA Intel PCH' set _verb HiFi set _enadev Headphone set _enamod RecordMedia - alsaucm -c 'HDA Intel PCH' set _verb HiFi get OutputDspName// -*/ + */ #define _GNU_SOURCE // needed for vasprintf #include @@ -42,75 +42,75 @@ #include "Alsa-ApiHat.h" typedef struct { - snd_use_case_mgr_t *ucm; - int cardId; - char *cardName; -} ucmHandleT; + snd_use_case_mgr_t *ucm; + int cardId; + char *cardName; +} ucmHandleT; static ucmHandleT ucmHandles[MAX_SND_CARD]; // Cache opened UCM handles -STATIC int alsaUseCaseOpen (struct afb_req request, queryValuesT *queryValues, int allowNewMgr) { - snd_ctl_t *ctlDev; + +STATIC int alsaUseCaseOpen(struct afb_req request, queryValuesT *queryValues, int allowNewMgr) { + snd_ctl_t *ctlDev; snd_ctl_card_info_t *cardinfo; snd_use_case_mgr_t *ucmHandle; const char *cardName; - int cardId, idx, idxFree=-1, err; - + int cardId, idx, idxFree = -1, err; + // open control interface for devid err = snd_ctl_open(&ctlDev, queryValues->devid, SND_CTL_READONLY); if (err < 0) { - ctlDev=NULL; - afb_req_fail_f (request, "devid-unknown", "SndCard devid=[%s] Not Found err=%d", queryValues->devid, err); + ctlDev = NULL; + afb_req_fail_f(request, "devid-unknown", "SndCard devid=[%s] Not Found err=%d", queryValues->devid, err); goto OnErrorExit; } - - snd_ctl_card_info_alloca(&cardinfo); - if ((err = snd_ctl_card_info(ctlDev, cardinfo)) < 0) { - afb_req_fail_f (request, "devid-invalid", "SndCard devid=[%s] Not Found err=%s", queryValues->devid, snd_strerror(err)); + + snd_ctl_card_info_alloca(&cardinfo); + if ((err = snd_ctl_card_info(ctlDev, cardinfo)) < 0) { + afb_req_fail_f(request, "devid-invalid", "SndCard devid=[%s] Not Found err=%s", queryValues->devid, snd_strerror(err)); goto OnErrorExit; } - + // search for an existing subscription and mark 1st free slot - cardId = snd_ctl_card_info_get_card(cardinfo); - for (idx= 0; idx < MAX_SND_CARD; idx ++) { - if (ucmHandles[idx].ucm != NULL) { - if (ucmHandles[idx].cardId == cardId) goto OnSuccessExit; - } else if (idxFree == -1) idxFree= idx; + cardId = snd_ctl_card_info_get_card(cardinfo); + for (idx = 0; idx < MAX_SND_CARD; idx++) { + if (ucmHandles[idx].ucm != NULL) { + if (ucmHandles[idx].cardId == cardId) goto OnSuccessExit; + } else if (idxFree == -1) idxFree = idx; }; - + if (!allowNewMgr) { - afb_req_fail_f (request, "ucm-nomgr", "SndCard devid=[%s] no exiting UCM manager session", queryValues->devid); - goto OnErrorExit; + afb_req_fail_f(request, "ucm-nomgr", "SndCard devid=[%s] no exiting UCM manager session", queryValues->devid); + goto OnErrorExit; } if (idxFree < 0 && idx == MAX_SND_CARD) { - afb_req_fail_f (request, "ucm-toomany", "SndCard devid=[%s] too many open UCM Max=%d", queryValues->devid, MAX_SND_CARD); - goto OnErrorExit; + afb_req_fail_f(request, "ucm-toomany", "SndCard devid=[%s] too many open UCM Max=%d", queryValues->devid, MAX_SND_CARD); + goto OnErrorExit; } - + idx = idxFree; - cardName = snd_ctl_card_info_get_name(cardinfo); + cardName = snd_ctl_card_info_get_name(cardinfo); err = snd_use_case_mgr_open(&ucmHandle, cardName); if (err) { - afb_req_fail_f (request, "ucm-open", "SndCard devid=[%s] name=[%s] No UCM Profile err=%s", queryValues->devid, cardName, snd_strerror(err)); - goto OnErrorExit; + afb_req_fail_f(request, "ucm-open", "SndCard devid=[%s] name=[%s] No UCM Profile err=%s", queryValues->devid, cardName, snd_strerror(err)); + goto OnErrorExit; } ucmHandles[idx].ucm = ucmHandle; - ucmHandles[idx].cardId = cardId; - ucmHandles[idx].cardName = strdup(cardName); + ucmHandles[idx].cardId = cardId; + ucmHandles[idx].cardName = strdup(cardName); - OnSuccessExit: - if (ctlDev) snd_ctl_close(ctlDev); +OnSuccessExit: + if (ctlDev) snd_ctl_close(ctlDev); return idx; - - OnErrorExit: - if (ctlDev) snd_ctl_close(ctlDev); + +OnErrorExit: + if (ctlDev) snd_ctl_close(ctlDev); return -1; } - -PUBLIC void alsaUseCaseQuery(struct afb_req request) { +PUBLIC void alsaUseCaseQuery(struct afb_req request) { int verbCount, ucmIdx; const char **verbList; snd_use_case_mgr_t *ucmHandle; @@ -118,318 +118,316 @@ PUBLIC void alsaUseCaseQuery(struct afb_req request) { json_object *ucmJs; const char *cardName; - json_object *queryJ = alsaCheckQuery (request, &queryValues); + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; - - ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE); + + ucmIdx = alsaUseCaseOpen(request, &queryValues, TRUE); if (ucmIdx < 0) goto OnErrorExit; ucmHandle = ucmHandles [ucmIdx].ucm; - cardName = ucmHandles [ucmIdx].cardName; - - verbCount = snd_use_case_get_list (ucmHandle, "_verbs", &verbList); + cardName = ucmHandles [ucmIdx].cardName; + + verbCount = snd_use_case_get_list(ucmHandle, "_verbs", &verbList); if (verbCount < 0) { - afb_req_fail_f (request, "ucm-list", "SndCard devid=[%s] name=[%s] No UCM Verbs", queryValues.devid, cardName); - goto OnErrorExit; + afb_req_fail_f(request, "ucm-list", "SndCard devid=[%s] name=[%s] No UCM Verbs", queryValues.devid, cardName); + goto OnErrorExit; } - + ucmJs = json_object_new_array(); - for (int idx=0; idx < verbCount; idx +=2) { + for (int idx = 0; idx < verbCount; idx += 2) { int devCount, modCount, tqCount; const char **devList, **modList, **tqList; json_object *ucmJ = json_object_new_object(); char identifier[32]; - - json_object_object_add (ucmJ, "verb", json_object_new_string(verbList[idx])); - if (verbList[idx+1]) json_object_object_add (ucmJ, "info", json_object_new_string(verbList[idx+1])); - - AFB_DEBUG ("Verb[%d] Action=%s Info=%s", idx, verbList[idx], verbList[idx+1]); - - snprintf (identifier, sizeof(identifier), "_devices/%s", verbList[idx]); - devCount = snd_use_case_get_list (ucmHandle, identifier, &devList); + + json_object_object_add(ucmJ, "verb", json_object_new_string(verbList[idx])); + if (verbList[idx + 1]) json_object_object_add(ucmJ, "info", json_object_new_string(verbList[idx + 1])); + + AFB_DEBUG("Verb[%d] Action=%s Info=%s", idx, verbList[idx], verbList[idx + 1]); + + snprintf(identifier, sizeof (identifier), "_devices/%s", verbList[idx]); + devCount = snd_use_case_get_list(ucmHandle, identifier, &devList); if (devCount > 0) { json_object *devsJ = json_object_new_array(); - - for (int jdx=0; jdx < devCount; jdx+=2) { + + for (int jdx = 0; jdx < devCount; jdx += 2) { json_object *devJ = json_object_new_object(); - AFB_DEBUG ("device[%d] Action=%s Info=%s", jdx, devList[jdx], devList[jdx+1]); - json_object_object_add (devJ, "dev", json_object_new_string(devList[jdx])); - if (devList[jdx+1]) json_object_object_add (devJ, "info", json_object_new_string(devList[jdx+1])); - json_object_array_add (devsJ, devJ); + AFB_DEBUG("device[%d] Action=%s Info=%s", jdx, devList[jdx], devList[jdx + 1]); + json_object_object_add(devJ, "dev", json_object_new_string(devList[jdx])); + if (devList[jdx + 1]) json_object_object_add(devJ, "info", json_object_new_string(devList[jdx + 1])); + json_object_array_add(devsJ, devJ); } - json_object_object_add(ucmJ,"devices", devsJ); + json_object_object_add(ucmJ, "devices", devsJ); snd_use_case_free_list(devList, devCount); } - - snprintf (identifier, sizeof(identifier), "_modifiers/%s", verbList[idx]); - modCount = snd_use_case_get_list (ucmHandle, identifier, &modList); + + snprintf(identifier, sizeof (identifier), "_modifiers/%s", verbList[idx]); + modCount = snd_use_case_get_list(ucmHandle, identifier, &modList); if (modCount > 0) { json_object *modsJ = json_object_new_array(); - - for (int jdx=0; jdx < modCount; jdx+=2) { + + for (int jdx = 0; jdx < modCount; jdx += 2) { json_object *modJ = json_object_new_object(); - AFB_DEBUG ("modifier[%d] Action=%s Info=%s", jdx, modList[jdx], modList[jdx+1]); - json_object_object_add (modJ, "mod", json_object_new_string(modList[jdx])); - if (modList[jdx+1]) json_object_object_add (modJ, "info", json_object_new_string(modList[jdx+1])); - json_object_array_add (modsJ, modJ); + AFB_DEBUG("modifier[%d] Action=%s Info=%s", jdx, modList[jdx], modList[jdx + 1]); + json_object_object_add(modJ, "mod", json_object_new_string(modList[jdx])); + if (modList[jdx + 1]) json_object_object_add(modJ, "info", json_object_new_string(modList[jdx + 1])); + json_object_array_add(modsJ, modJ); } - json_object_object_add(ucmJ,"modifiers", modsJ); + json_object_object_add(ucmJ, "modifiers", modsJ); snd_use_case_free_list(modList, modCount); } - snprintf (identifier, sizeof(identifier), "TQ/%s", verbList[idx]); - tqCount = snd_use_case_get_list (ucmHandle, identifier, &tqList); + snprintf(identifier, sizeof (identifier), "TQ/%s", verbList[idx]); + tqCount = snd_use_case_get_list(ucmHandle, identifier, &tqList); if (tqCount > 0) { json_object *tqsJ = json_object_new_array(); - - for (int jdx=0; jdx < tqCount; jdx+=2) { + + for (int jdx = 0; jdx < tqCount; jdx += 2) { json_object *tqJ = json_object_new_object(); - AFB_DEBUG ("toneqa[%d] Action=%s Info=%s", jdx, tqList[jdx], tqList[jdx+1]); - json_object_object_add (tqJ, "tq", json_object_new_string(tqList[jdx])); - if (tqList[jdx+1]) json_object_object_add (tqJ, "info", json_object_new_string(tqList[jdx+1])); - json_object_array_add (tqsJ, tqJ); + AFB_DEBUG("toneqa[%d] Action=%s Info=%s", jdx, tqList[jdx], tqList[jdx + 1]); + json_object_object_add(tqJ, "tq", json_object_new_string(tqList[jdx])); + if (tqList[jdx + 1]) json_object_object_add(tqJ, "info", json_object_new_string(tqList[jdx + 1])); + json_object_array_add(tqsJ, tqJ); } - json_object_object_add(ucmJ,"tqs", tqsJ); + json_object_object_add(ucmJ, "tqs", tqsJ); snd_use_case_free_list(tqList, tqCount); } - - json_object_array_add (ucmJs, ucmJ); + + json_object_array_add(ucmJs, ucmJ); } - - afb_req_success (request, ucmJs, NULL); + + afb_req_success(request, ucmJs, NULL); snd_use_case_free_list(verbList, verbCount); - - OnErrorExit: + +OnErrorExit: return; } -STATIC json_object *ucmGetValue (ucmHandleT *ucmHandle, const char *verb, const char *mod, const char *label) { +STATIC json_object *ucmGetValue(ucmHandleT *ucmHandle, const char *verb, const char *mod, const char *label) { char identifier[80]; char *value; int err; json_object *jValue; - + // handle optional parameters - if (!mod) mod=""; - if (!verb) verb=""; - + if (!mod) mod = ""; + if (!verb) verb = ""; + if (!label) { - AFB_NOTICE ("ucmGetValue cardname=[%s] value label missing", ucmHandle->cardName); - goto OnErrorExit; + AFB_NOTICE("ucmGetValue cardname=[%s] value label missing", ucmHandle->cardName); + goto OnErrorExit; } - - snprintf (identifier, sizeof(identifier), "%s/%s/%s", label, mod, verb); - err = snd_use_case_get (ucmHandle->ucm, identifier, (const char**)&value); // Note: value casting is a known "FEATURE" of AlsaUCM API + + snprintf(identifier, sizeof (identifier), "%s/%s/%s", label, mod, verb); + err = snd_use_case_get(ucmHandle->ucm, identifier, (const char**) &value); // Note: value casting is a known "FEATURE" of AlsaUCM API if (err) { - AFB_DEBUG ("ucmGetValue cardname=[%s] identifier=[%s] error=%s", ucmHandle->cardName, identifier, snd_strerror (err)); + AFB_DEBUG("ucmGetValue cardname=[%s] identifier=[%s] error=%s", ucmHandle->cardName, identifier, snd_strerror(err)); goto OnErrorExit; } - + // copy value into json object and free string - jValue = json_object_new_string (value); - free (value); - return (jValue); - - OnErrorExit: + jValue = json_object_new_string(value); + free(value); + return (jValue); + +OnErrorExit: return (NULL); } -PUBLIC void alsaUseCaseGet (struct afb_req request) { +PUBLIC void alsaUseCaseGet(struct afb_req request) { int ucmIdx, labelCount; queryValuesT queryValues; json_object *jResponse = json_object_new_object(); json_object *jWarnings = json_object_new_array(); - const char *warnings=NULL; - - json_object *queryJ = alsaCheckQuery (request, &queryValues); + const char *warnings = NULL; + + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; -; - - ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE); + ; + + ucmIdx = alsaUseCaseOpen(request, &queryValues, TRUE); if (ucmIdx < 0) goto OnErrorExit; - - const char *cardName= ucmHandles[ucmIdx].cardName; - - const char *verb = afb_req_value(request, "verb"); - const char *mod = afb_req_value(request, "mod"); - const char *dev = afb_req_value(request, "dev"); - + + const char *cardName = ucmHandles[ucmIdx].cardName; + + const char *verb = afb_req_value(request, "verb"); + const char *mod = afb_req_value(request, "mod"); + const char *dev = afb_req_value(request, "dev"); + if (dev && mod) { - afb_req_fail_f (request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM mod+dev incompatible", queryValues.devid, cardName); - goto OnErrorExit; - } - + afb_req_fail_f(request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM mod+dev incompatible", queryValues.devid, cardName); + goto OnErrorExit; + } + // device selection is handle as a modifier - if (dev) mod=dev; + if (dev) mod = dev; const char *labels = afb_req_value(request, "value"); if (!labels) { - afb_req_fail_f (request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM values name missing", queryValues.devid, cardName); - goto OnErrorExit; - } - + afb_req_fail_f(request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM values name missing", queryValues.devid, cardName); + goto OnErrorExit; + } + json_object *jLabels = json_tokener_parse(labels); if (!jLabels) { - afb_req_fail_f (request, "ucmget-notjson","labels=%s not a valid json entry", labels); - goto OnErrorExit; + afb_req_fail_f(request, "ucmget-notjson", "labels=%s not a valid json entry", labels); + goto OnErrorExit; }; - - enum json_type jtype= json_object_get_type(jLabels); + + enum json_type jtype = json_object_get_type(jLabels); switch (jtype) { - json_object *tmpJ; - + json_object *tmpJ; + case json_type_array: - labelCount = json_object_array_length (jLabels); + labelCount = json_object_array_length(jLabels); break; - + case json_type_string: - tmpJ = json_object_new_array (); + tmpJ = json_object_new_array(); labelCount = 1; - json_object_array_add (tmpJ, jLabels); - jLabels=tmpJ; + json_object_array_add(tmpJ, jLabels); + jLabels = tmpJ; break; - - default: - afb_req_fail_f (request, "ucmget-notarray","labels=%s not valid JSON array", labels); - goto OnErrorExit; + + default: + afb_req_fail_f(request, "ucmget-notarray", "labels=%s not valid JSON array", labels); + goto OnErrorExit; } - for (int idx=0; idx < labelCount; idx++) { + for (int idx = 0; idx < labelCount; idx++) { json_object *jValue, *jLabel; - const char *label ; - - jLabel= json_object_array_get_idx (jLabels, idx); - label= json_object_get_string (jLabel); - jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, mod, label); - if (jValue) json_object_object_add (jResponse, label, jValue); + const char *label; + + jLabel = json_object_array_get_idx(jLabels, idx); + label = json_object_get_string(jLabel); + jValue = ucmGetValue(&ucmHandles[ucmIdx], verb, mod, label); + if (jValue) json_object_object_add(jResponse, label, jValue); else { - json_object_array_add (jWarnings, jLabel); + json_object_array_add(jWarnings, jLabel); } } - + // use info section to notified not found values label - if (json_object_array_length (jWarnings) > 0) { - json_object *tmpJ = json_object_new_object (); - json_object_object_add (tmpJ, "no-context", jWarnings); - warnings= json_object_get_string (tmpJ); + if (json_object_array_length(jWarnings) > 0) { + json_object *tmpJ = json_object_new_object(); + json_object_object_add(tmpJ, "no-context", jWarnings); + warnings = json_object_get_string(tmpJ); } - afb_req_success (request, jResponse, warnings); - - OnErrorExit: - return; + afb_req_success(request, jResponse, warnings); + +OnErrorExit: + return; } - -PUBLIC void alsaUseCaseSet(struct afb_req request) { + +PUBLIC void alsaUseCaseSet(struct afb_req request) { int err, ucmIdx; queryValuesT queryValues; json_object *jResponse = json_object_new_object(); - - json_object *queryJ = alsaCheckQuery (request, &queryValues); + + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; - - ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE); + + ucmIdx = alsaUseCaseOpen(request, &queryValues, TRUE); if (ucmIdx < 0) goto OnErrorExit; - - snd_use_case_mgr_t *ucmMgr= ucmHandles[ucmIdx].ucm; - const char *cardName= ucmHandles[ucmIdx].cardName; - + + snd_use_case_mgr_t *ucmMgr = ucmHandles[ucmIdx].ucm; + const char *cardName = ucmHandles[ucmIdx].cardName; + const char *verb = afb_req_value(request, "verb"); - const char *mod = afb_req_value(request, "mod"); - const char *dev = afb_req_value(request, "dev"); + const char *mod = afb_req_value(request, "mod"); + const char *dev = afb_req_value(request, "dev"); // Known identifiers: _verb - set current verb = value _enadev - enable given device = value _disdev - disable given device = value _swdev/{old_device} - new_device = value if (verb) { - err = snd_use_case_set (ucmMgr, "_verb", verb); - if (err) { - afb_req_fail_f (request, "ucmset-verb", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] err=%s", queryValues.devid, cardName, verb, snd_strerror(err)); + err = snd_use_case_set(ucmMgr, "_verb", verb); + if (err) { + afb_req_fail_f(request, "ucmset-verb", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] err=%s", queryValues.devid, cardName, verb, snd_strerror(err)); goto OnErrorExit; } } if (dev) { - err = snd_use_case_set (ucmMgr, "_enadev", dev); - if (err) { - afb_req_fail_f (request, "ucmset-dev", "SndCard devid=[%s] name=[%s] Invalid UCMverb=[%s] dev=%s err=%s", queryValues.devid, cardName, verb, dev, snd_strerror(err)); + err = snd_use_case_set(ucmMgr, "_enadev", dev); + if (err) { + afb_req_fail_f(request, "ucmset-dev", "SndCard devid=[%s] name=[%s] Invalid UCMverb=[%s] dev=%s err=%s", queryValues.devid, cardName, verb, dev, snd_strerror(err)); goto OnErrorExit; } } - + if (mod) { - err = snd_use_case_set (ucmMgr, "_enamod", mod); - if (err) { - afb_req_fail_f (request, "ucmset-mod", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] mod=[%s] err=%s", queryValues.devid, cardName, verb, mod, snd_strerror(err)); + err = snd_use_case_set(ucmMgr, "_enamod", mod); + if (err) { + afb_req_fail_f(request, "ucmset-mod", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] mod=[%s] err=%s", queryValues.devid, cardName, verb, mod, snd_strerror(err)); goto OnErrorExit; } } - + // label are requested transfert request to get if (afb_req_value(request, "value")) return alsaUseCaseGet(request); - + if (queryValues.mode <= 3) { json_object *jValue; - jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, dev, "OutputDspName"); - if (jValue) json_object_object_add (jResponse, "OutputDspName", jValue); - - jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, dev, "PlaybackPCM"); - if (jValue) json_object_object_add (jResponse, "PlaybackPCM", jValue); + jValue = ucmGetValue(&ucmHandles[ucmIdx], verb, dev, "OutputDspName"); + if (jValue) json_object_object_add(jResponse, "OutputDspName", jValue); - jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, mod, "CapturePCM"); - if (jValue) json_object_object_add (jResponse, "CapturePCM", jValue); + jValue = ucmGetValue(&ucmHandles[ucmIdx], verb, dev, "PlaybackPCM"); + if (jValue) json_object_object_add(jResponse, "PlaybackPCM", jValue); + + jValue = ucmGetValue(&ucmHandles[ucmIdx], verb, mod, "CapturePCM"); + if (jValue) json_object_object_add(jResponse, "CapturePCM", jValue); } - afb_req_success (request, jResponse, NULL); - - OnErrorExit: + afb_req_success(request, jResponse, NULL); + +OnErrorExit: return; } - - -PUBLIC void alsaUseCaseReset (struct afb_req request) { +PUBLIC void alsaUseCaseReset(struct afb_req request) { int err, ucmIdx; queryValuesT queryValues; - - json_object *queryJ = alsaCheckQuery (request, &queryValues); + + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; - - ucmIdx = alsaUseCaseOpen (request, &queryValues, FALSE); + + ucmIdx = alsaUseCaseOpen(request, &queryValues, FALSE); if (ucmIdx < 0) goto OnErrorExit; - - err= snd_use_case_mgr_reset (ucmHandles[ucmIdx].ucm); + + err = snd_use_case_mgr_reset(ucmHandles[ucmIdx].ucm); if (err) { - afb_req_fail_f (request, "ucmreset-fail","devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName); - goto OnErrorExit; + afb_req_fail_f(request, "ucmreset-fail", "devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName); + goto OnErrorExit; } - - afb_req_success (request, NULL, NULL); - - OnErrorExit: - return; + + afb_req_success(request, NULL, NULL); + +OnErrorExit: + return; } - -PUBLIC void alsaUseCaseClose (struct afb_req request) { + +PUBLIC void alsaUseCaseClose(struct afb_req request) { int err, ucmIdx; queryValuesT queryValues; - - json_object *queryJ = alsaCheckQuery (request, &queryValues); + + json_object *queryJ = alsaCheckQuery(request, &queryValues); if (!queryJ) goto OnErrorExit; - - ucmIdx = alsaUseCaseOpen (request, &queryValues, FALSE); + + ucmIdx = alsaUseCaseOpen(request, &queryValues, FALSE); if (ucmIdx < 0) goto OnErrorExit; - - err= snd_use_case_mgr_close (ucmHandles[ucmIdx].ucm); + + err = snd_use_case_mgr_close(ucmHandles[ucmIdx].ucm); if (err) { - afb_req_fail_f (request, "ucmreset-close","devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName); - goto OnErrorExit; + afb_req_fail_f(request, "ucmreset-close", "devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName); + goto OnErrorExit; } - + // do not forget to release sound card name string - free (ucmHandles[ucmIdx].cardName); - - afb_req_success (request, NULL, NULL); - - OnErrorExit: - return; + free(ucmHandles[ucmIdx].cardName); + + afb_req_success(request, NULL, NULL); + +OnErrorExit: + return; } - + diff --git a/Alsa-Plugin/Alsa-Policy-Hook/AlsaHookCb.c b/Alsa-Plugin/Alsa-Policy-Hook/AlsaHookCb.c new file mode 100644 index 0000000..2675805 --- /dev/null +++ b/Alsa-Plugin/Alsa-Policy-Hook/AlsaHookCb.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); + * AlsaHookInit is mandatory and called with numid=0 + * + * Syntax in .asoundrc file + * CrlLabel { cb MyFunctionName name "My_Second_Control" } + * + * Testing: + * aplay -DAlsaHook /usr/share/sounds/alsa/test.wav + * + * References: + * https://www.spinics.net/lists/alsa-devel/msg54235.html + * https://github.com/shivdasgujare/utilities/blob/master/nexuss/alsa-scenario-hook/src/alsa-wrapper.c + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include + +#include "afb/afb-wsj1.h" +#include "afb/afb-ws-client.h" +#include +#include + + +#define PLUGIN_ENTRY_POINT AlsaInstallHook + // Fulup Note: What ever you may find on Internet you should use + // SND_CONFIG_DLSYM_VERSION_HOOK and not SND_CONFIG_DLSYM_VERSION_HOOK + SND_DLSYM_BUILD_VERSION(PLUGIN_ENTRY_POINT, SND_PCM_DLSYM_VERSION) + +// this should be more than enough +#define MAX_API_CALL 10 + +// Currently not implemented +#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x +void OnRequestCB(void* UNUSED(handle) , const char* UNUSED(api), const char* UNUSED(verb), struct afb_wsj1_msg*UNUSED(msg)) {} + + typedef struct { + snd_pcm_t *pcm; + const char *uri; + struct afb_wsj1 *wsj1; + sd_event *sdLoop; + int verbose; + sem_t semaphore; + int count; + int error; +} afbClientT; + +typedef struct { + const char *api; + const char *verb; + long timeout; + char *query; + + sd_event_source *evtSource; + char *callIdTag; + afbClientT *afbClient; +} afbRequestT; + +static void *LoopInThread(void *handle) { + afbClientT *afbClient = (afbClientT*) handle; + int count=0; + int watchdog= 60 *1000000; + + /* loop until end */ + for (;;) { + + if (afbClient->verbose) printf("ON-MAINLOOP Active count=%d\n", count++); + sd_event_run(afbClient->sdLoop, watchdog); + } + + return NULL; +} + +// lost connect with the AudioDaemon +static void OnHangupCB(void *handle, struct afb_wsj1 *wsj1) { + + afbClientT *afbClient = (afbClientT*) handle; + snd_pcm_close (afbClient->pcm); + + SNDERR("(Hoops) Lost Connection to %s", afbClient->uri); + exit(0); +} + +void OnEventCB(void *handle, const char *event, struct afb_wsj1_msg *msg) { + afbClientT *afbClient = (afbClientT*) handle; + int err; + + printf("ON-EVENT %s(%s)\n", event, afb_wsj1_msg_object_s(msg)); + + // pause PCM until request call succeed + err = snd_pcm_pause (afbClient->pcm, 1); + if (err < 0) { + fprintf (stderr, "PCM Fail to pause\n"); + goto OnErrorExit; + } + + // wait on a semaphore until request return or timeout + err = snd_pcm_pause (afbClient->pcm, 0); + if (err < 0) { + fprintf (stderr, "PCM Fail to resume\n"); + goto OnErrorExit; + } + +OnErrorExit: + return; + +} + +// callback interface for wsj1 +static struct afb_wsj1_itf itf = { + .on_hangup = OnHangupCB, + .on_call = OnRequestCB, + .on_event = OnEventCB +}; + +void OnAbortPcm (afbClientT *afbClient) { + printf("ON-ABORT-PCM PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri); + snd_pcm_close (afbClient->pcm); +} + +void OnResponseCB(void *handle, struct afb_wsj1_msg *msg) { + afbRequestT *afbRequest= (afbRequestT*)handle; + + if (afbRequest->afbClient->verbose) printf("ON-RESPONSE call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg)); + + // Cancel timeout for this request + sd_event_source_unref(afbRequest->evtSource); + + if (! afb_wsj1_msg_is_reply_ok(msg)) { + fprintf(stderr, "ON-RESPONSE ERROR call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg)); + OnAbortPcm (afbRequest->afbClient); + } + + // When not more waiting call release semaphore + afbRequest->afbClient->count--; + if (afbRequest->afbClient->count == 0) { + if (afbRequest->afbClient->verbose) printf("ON-RESPONSE No More Waiting Request\n"); + afbRequest->afbClient->error=0; + sem_post (&afbRequest->afbClient->semaphore); + } +} + +int OnTimeoutCB (sd_event_source* source, uint64_t timer, void* handle) { + afbClientT *afbClient= (afbClientT*)handle; + + fprintf(stderr, "ON-TIMEOUT Call Request Fail URI=%s\n", afbClient->uri); + + // Close PCM and release waiting client + afbClient->error=1; + sem_post (&afbClient->semaphore); + + return 0; +} + +// Call AGL binder asynchronously by with a timeout +static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int count) { + uint64_t usec; + int err; + + // create a unique tag for request + (void) asprintf(&afbRequest->callIdTag, "%d:%s/%s", count, afbRequest->api, afbRequest->verb); + + err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, afbRequest->verb, afbRequest->query, OnResponseCB, afbRequest); + if (err) goto OnErrorExit; + + // create a timer with ~250us accuracy + sd_event_now(afbClient->sdLoop, CLOCK_MONOTONIC, &usec); + sd_event_add_time(afbClient->sdLoop, &afbRequest->evtSource, CLOCK_MONOTONIC, usec+afbRequest->timeout*1000, 250, OnTimeoutCB, afbClient); + + // save client handle in request + afbRequest->afbClient = afbClient; + afbClient->count ++; + + return 0; + +OnErrorExit: + return -1; +} + +static int LaunchCallRequest(afbClientT *afbClient, afbRequestT **afbRequest) { + + pthread_t tid; + int err, idx; + + // Create a main loop + err = sd_event_default(&afbClient->sdLoop); + if (err < 0) { + fprintf(stderr, "LaunchCallRequest: Connection to default event loop failed: %s\n", strerror(-err)); + goto OnErrorExit; + } + + // init waiting counting semaphore + if (sem_init(&afbClient->semaphore, 1, 0) == -1) { + fprintf(stderr, "LaunchCallRequest: Fail Semaphore Init: %s\n", afbClient->uri); + } + + // connect the websocket wsj1 to the uri given by the first argument + afbClient->wsj1 = afb_ws_client_connect_wsj1(afbClient->sdLoop, afbClient->uri, &itf, afbClient); + if (afbClient->wsj1 == NULL) { + fprintf(stderr, "LaunchCallRequest: Connection to %s failed\n", afbClient->uri); + goto OnErrorExit; + } + + // send call request to audio-agent asynchronously (respond with thread mainloop context) + for (idx = 0; afbRequest[idx] != NULL; idx++) { + err = CallWithTimeout(afbClient, afbRequest[idx], idx); + if (err) { + fprintf(stderr, "LaunchCallRequest: Fail call %s//%s/%s&%s", afbClient->uri, afbRequest[idx]->api, afbRequest[idx]->verb, afbRequest[idx]->query); + goto OnErrorExit; + } + } + + // launch counter to keep track of waiting request call + afbClient->count=idx; + + // start a thread with a mainloop to monitor Audio-Agent + err = pthread_create(&tid, NULL, &LoopInThread, afbClient); + if (err) goto OnErrorExit; + + return 0; + +OnErrorExit: + return -1; +} + +static int AlsaCloseHook(snd_pcm_hook_t *hook) { + + afbClientT *afbClient = (afbClientT*) snd_pcm_hook_get_private (hook); + + if (afbClient->verbose) fprintf(stdout, "\nAlsaCloseHook pcm=%s uri=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri); + return 0; +} + +// Function call when Plugin PCM is OPEN +int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { + afbRequestT **afbRequest; + snd_pcm_hook_t *h_close = NULL; + snd_config_iterator_t it, next; + afbClientT *afbClient = malloc(sizeof (afbClientT)); + afbRequest = malloc(MAX_API_CALL * sizeof (afbRequestT)); + int err; + + // start populating client handle + afbClient->pcm = pcm; + afbClient->verbose = 0; + + + // Get PCM arguments from asoundrc + snd_config_for_each(it, next, conf) { + snd_config_t *node = snd_config_iterator_entry(it); + const char *id; + + // ignore comment en empty lines + if (snd_config_get_id(node, &id) < 0) continue; + if (strcmp(id, "comment") == 0 || strcmp(id, "hint") == 0) continue; + + if (strcmp(id, "uri") == 0) { + if (snd_config_get_string(node, &afbClient->uri) < 0) { + SNDERR("Invalid String for %s", id); + goto OnErrorExit; + } + continue; + } + + if (strcmp(id, "verbose") == 0) { + afbClient->verbose= snd_config_get_bool(node); + if (afbClient->verbose < 0) { + SNDERR("Invalid Boolean for %s", id); + goto OnErrorExit; + } + continue; + } + + if (strcmp(id, "request") == 0) { + const char *callConf, *callLabel; + snd_config_type_t ctype; + snd_config_iterator_t currentCall, follow; + snd_config_t *itemConf; + int callCount=0; + + ctype = snd_config_get_type(node); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string(node, &callConf); + SNDERR("Invalid compound type for %s", callConf); + goto OnErrorExit; + } + + + // loop on each call + snd_config_for_each(currentCall, follow, node) { + snd_config_t *ctlconfig = snd_config_iterator_entry(currentCall); + + // ignore empty line + if (snd_config_get_id(ctlconfig, &callLabel) < 0) continue; + + // each clt should be a valid config compound + ctype = snd_config_get_type(ctlconfig); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string(node, &callConf); + SNDERR("Invalid call element for %s", callLabel); + goto OnErrorExit; + } + + // allocate an empty call request + afbRequest[callCount] = malloc(sizeof (afbRequestT)); + afbRequest[callCount]->api=NULL; + afbRequest[callCount]->verb=NULL; + afbRequest[callCount]->query=NULL; + + + err = snd_config_search(ctlconfig, "api", &itemConf); + if (!err) { + if (snd_config_get_string(itemConf, &afbRequest[callCount]->api) < 0) { + SNDERR("Invalid api string for %s", callLabel); + goto OnErrorExit; + } + } + + err = snd_config_search(ctlconfig, "verb", &itemConf); + if (!err) { + if (snd_config_get_string(itemConf, &afbRequest[callCount]->verb) < 0) { + SNDERR("Invalid verb string %s", id); + goto OnErrorExit; + } + } + + err = snd_config_search(ctlconfig, "timeout", &itemConf); + if (!err) { + if (snd_config_get_integer(itemConf, &afbRequest[callCount]->timeout) < 0) { + SNDERR("Invalid timeout Integer %s", id); + goto OnErrorExit; + } + } + + err = snd_config_search(ctlconfig, "query", &itemConf); + if (!err) { + const char *query; + if (snd_config_get_string(itemConf, &query) < 0) { + SNDERR("Invalid query string %s", id); + goto OnErrorExit; + } + // cleanup string for json_tokener + afbRequest[callCount]->query = strdup(query); + for (int idx = 0; query[idx] != '\0'; idx++) { + if (query[idx] == '\'') afbRequest[callCount]->query[idx] = '"'; + else afbRequest[callCount]->query[idx] = query[idx]; + } + json_object *queryJ = json_tokener_parse(query); + if (!queryJ) { + SNDERR("Invalid Json %s query=%s format \"{'tok1':'val1', 'tok2':'val2'}\" ", id, query); + goto OnErrorExit; + } + } + + // Simple check on call request validity + if (!afbRequest[callCount]->query) afbRequest[callCount]->query= ""; + if (!afbRequest[callCount]->timeout) afbRequest[callCount]->timeout=100; + if (!afbRequest[callCount]->verb || !afbRequest[callCount]->api) { + SNDERR("Missing api/verb %s in asoundrc", callLabel); + goto OnErrorExit; + } + + // move to next call if any + callCount ++; + if (callCount == MAX_API_CALL) { + SNDERR("Too Many call MAX_API_CALL=%d", MAX_API_CALL); + goto OnErrorExit; + } + afbRequest[callCount]=NULL; // afbRequest array is NULL terminated + + } + continue; + } + } + + if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Install Start PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri); + + err = snd_pcm_hook_add(&h_close, afbClient->pcm, SND_PCM_HOOK_TYPE_CLOSE, AlsaCloseHook, afbClient); + if (err < 0) goto OnErrorExit; + + // launch call request and create a waiting mainloop thread + err = LaunchCallRequest(afbClient, afbRequest); + if (err < 0) { + fprintf (stderr, "PCM Fail to Enter Mainloop\n"); + goto OnErrorExit; + } + + // wait for all call request to return + sem_wait(&afbClient->semaphore); + if (afbClient->error) { + fprintf (stderr, "PCM Authorisation from Audio Agent Refused\n"); + goto OnErrorExit; + } + + if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Install Success PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri); + return 0; + +OnErrorExit: + fprintf(stderr, "\nAlsaPcmHook Plugin Install Fail PCM=%s\n", snd_pcm_name(afbClient->pcm)); + if (h_close) + snd_pcm_hook_remove(h_close); + + return -EINVAL; +} + diff --git a/Alsa-Plugin/Alsa-Policy-Hook/CMakeLists.txt b/Alsa-Plugin/Alsa-Policy-Hook/CMakeLists.txt new file mode 100644 index 0000000..3780ee5 --- /dev/null +++ b/Alsa-Plugin/Alsa-Policy-Hook/CMakeLists.txt @@ -0,0 +1,38 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# 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. +########################################################################### + +# Activate ALSA dynamic build build mode get resolve "snd_dlsym_start" +add_compile_options(-DPIC) + + +PROJECT_TARGET_ADD(alsa_hook_cb) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE AlsaHookCb.c) + + # Alsa Plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "" + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) diff --git a/Alsa-Plugin/Alsa-Policy-Hook/README.md b/Alsa-Plugin/Alsa-Policy-Hook/README.md new file mode 100644 index 0000000..108c2b5 --- /dev/null +++ b/Alsa-Plugin/Alsa-Policy-Hook/README.md @@ -0,0 +1,65 @@ +Alsa-Hook-Plugin + +Object: Provide a Hook on Alsa PCM to check permission again AGL Advance Audio Agent +Status: Working Proof of Concept +Author: Fulup Ar Foll fulup@iot.bzh +Date : August-2017 + +Functionalities: + - Execute a set of websocket RPC request again AGL binders to allow/deny access + - Keep websocket open in an idepandant thread on order to monitor event receive from AGL audio agent + +Installation + - Alsaplugins are typically search in /usr/share/alsa-lib. Nevertheless a full path might be given + - This plugin implement a hook on a slave PCM. Typically this slave PCM is a dedicated virtual channel (eg: navigation, emergency,...) + - Config should be place in ~/.asoundrc (see config sample in PROJECT_ROOT/conf.d/alsa) + +Config +``` +# Define sharelib location and entry point +# ----------------------------------------- +pcm_hook_type.MyHookPlugin { + install "AlsaInstallHook" + lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Hook-Callback/alsa_hook_cb.so" +} + +# Create PCM HOOK with corresponding request calls to AGL Audio Agent +# -------------------------------------------------------------------- +pcm.MyNavigationHook { + type hooks + slave.pcm "MyMixerPCM" # Target PCM to effectively play sound + + # Call request that should succeed before accessing slave PCM + hooks.0 { + type "MyHookPlugin" # Name=xxxx should match with pcm_hook_type.xxxx + hook_args { + + # URI to AGL-Advance-Audio-Agent binder + uri "ws://localhost:1234/api?token='audio-agent-token'" + + # Call request to send to binder (initial label is free, query is optional) + request { + MyFirstCheck { + api "alsacore" + verb "ping" + } + MySecondCheck { + api "alsacore" + verb "ucmset" + query "{'devid':'hw:v1340','verb':'Navigation','dev':'speakers'}" + } + } + } + } +} + +``` + +NOTE: + +* Hook plugin is loaded by Alsa libasound within player context. It inherits client process attributes, as UID/GID or +the SMACK label on AGL. This smack label is tested by AGL security framework when requested a call on the audio-agent binder. +As a result the call will only succeed it the permission attached the application in Cynara matches. + +* Hook plugin keep a connection with the Audio-Agent until PCM is closed by the application. This connection allow the +Audio-Agent to send events. eg: pause, quit, mute, ... \ No newline at end of file diff --git a/Alsa-Plugin/CMakeLists.txt b/Alsa-Plugin/CMakeLists.txt new file mode 100644 index 0000000..b0c2e6a --- /dev/null +++ b/Alsa-Plugin/CMakeLists.txt @@ -0,0 +1,23 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# 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. +########################################################################### + + +# Include any directory starting with a Capital letter +# ----------------------------------------------------- +PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN}) + diff --git a/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlug.h b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlug.h new file mode 100644 index 0000000..bb2dd79 --- /dev/null +++ b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlug.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); + * AfbHalInit is mandatory and called with numid=0 + * + * Syntaxe in .asoundrc file + * CrlLabel { cb MyFunctionName name "My_Second_Control" } + */ + + +#include +#include +#include + +#ifndef SOUND_HAL_MAX_CTLS +#define SOUND_HAL_MAX_CTLS 255 +#endif + +typedef enum { + CTLCB_INIT, + CTLCB_CLOSE, + CTLCB_ELEM_COUNT, + CTLCB_ELEM_LIST, + CTLCB_FIND_ELEM, + CTLCB_FREE_KEY, + CTLCB_GET_ATTRIBUTE, + CTLCB_GET_INTEGER_INFO, + CTLCB_GET_INTEGER64_INFO, + CTLCB_GET_ENUMERATED_INFO, + CTLCB_GET_ENUMERATED_NAME, + CTLCB_READ_INTEGER, + CTLCB_READ_INTEGER64, + CTLCB_READ_ENUMERATED, + CTLCB_READ_BYTES, + CTLCB_READ_IEC958, + CTLCB_WRITE_INTEGER, + CTLCB_WRITE_INTEGER64, + CTLCB_WRITE_ENUMERATED, + CTLCB_WRITE_BYTES, + CTLCB_WRITE_IEC958, + CTLCB_SUBSCRIBE_EVENTS, + CTLCB_READ_EVENT, + CTLCB_POLL_DESCRIPTORS_COUNT, + CTLCB_POLL_DESCRIPTORS +} snd_ctl_action_t; + +typedef struct { + int ctlNumid; + const char *ctlName; +} snd_ctl_conf_t; + +typedef struct { + int type; + int acc; + unsigned count; +} snd_ctl_get_attrib_t; + +typedef struct { + int imin; + int imax; + int istep; +} snd_ctl_get_int_info_t; + +typedef int(*snd_ctl_cb_t)(void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void *response); + +typedef struct snd_ctl_hal { + snd_ctl_ext_t ext; + char *devid; + snd_ctl_t *ctlDev; + unsigned int ctlsCount; + void *dlHandle; + snd_ctl_conf_t ctls[SOUND_HAL_MAX_CTLS]; + snd_ctl_elem_info_t *infos[SOUND_HAL_MAX_CTLS]; + snd_ctl_cb_t cbs[SOUND_HAL_MAX_CTLS]; +} snd_ctl_hal_t; + + diff --git a/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCb.c b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCb.c new file mode 100644 index 0000000..b96f3d9 --- /dev/null +++ b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCb.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); + * AfbHalInit is mandatory and called with numid=0 + * + * Syntax in .asoundrc file + * CrlLabel { cb MyFunctionName name "My_Second_Control" } + * + * Testing: + * amixer -Dagl_hal controls + * amixer -Dagl_hal cget name=My_Sample_Callback + */ + + +#include "AlsaHalPlug.h" +#include + +int AfbHalInitCB(void *handle, snd_ctl_action_t action, int numid, void*response) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) handle; + + fprintf(stdout, "\n - CB AfbHalInit devid=%s\n", plughandle->devid); + return 0; +} + +int AfbHalSampleCB(void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void*response) { + + switch (action) { + case CTLCB_GET_ATTRIBUTE: + { + fprintf(stdout, " - AfbHalSampleCB CTLCB_GET_ATTRIBUTE numid=%d\n", (int) key + 1); + snd_ctl_get_attrib_t *item = (snd_ctl_get_attrib_t*) response; + item->type = 1; + item->count = 2; + item->acc = SND_CTL_EXT_ACCESS_READWRITE; + break; + } + + case CTLCB_GET_INTEGER_INFO: + { + fprintf(stdout, " - AfbHalSampleCB CTLCB_GET_INTEGER_INFO numid=%d\n", (int) key + 1); + snd_ctl_get_int_info_t *item = (snd_ctl_get_attrib_t*) response; + item->istep = 10; + item->imin = 20; + item->imax = 200; + break; + } + + default: + fprintf(stdout, "CB AfbHalSampleCB unsupported action=%d numid=%d\n", action, (int) key + 1); + } + return 0; +} diff --git a/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCtl.c b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCtl.c new file mode 100644 index 0000000..5e92dd6 --- /dev/null +++ b/Alsa-Plugin/_Alsa-Hal-plugin/AlsaHalPlugCtl.c @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * Testing: + * 1) Copy generated plugin [libasound_module_pcm_afbhal.so] in alsa-lib/ dir visible from LD_LIBRARY_PATH (eg: /usr/lib64/alsa-lib) + * 2) Create a ~/.asounrc file base on following template + * ctl.agl_hal { + * type afbhal + * devid "hw:4" + * cblib "afbhal_cb_sample.so" + * ctls { + * # ctlLabel {numid integer name "Alsa Ctl Name"} + * MasterSwitch { numid 4 name "My_First_Control" } + * MasterVol { numid 5 name "My_Second_Control" } + * CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} + * } + * pcm.agl_hal { + * type copy # Copy PCM + * slave "hw:4" # Slave name + * } + * + * } + * 3) Test with + * - amixer -Dagl_hal controls # should list all your controls + * - amixer -Dagl_hal cget numid=1 + * - amixer -Dagl_hal cset numid=1 '10,20' + */ + + +#include +#include +#include "AlsaHalPlug.h" +#include + +static snd_ctl_ext_key_t AfbHalElemFind(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_ext_key_t key = NULL; + + int numid = snd_ctl_elem_id_get_numid(id); + if (numid > 0) { + if (numid > plughandle->ctlsCount) goto OnErrorExit; + key = (snd_ctl_ext_key_t) numid - 1; + goto SucessExit; + } + + const char *ctlName = snd_ctl_elem_id_get_name(id); + if (ctlName == NULL) goto OnErrorExit; + + for (int idx = 0; idx < plughandle->ctlsCount; idx++) { + if (!strcmp(ctlName, plughandle->ctls[idx].ctlName)) { + key = idx; + goto SucessExit; + } + } + +SucessExit: + return key; + +OnErrorExit: + return -1; +} + +static int AfbHalGetAttrib(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int *type, unsigned int *acc, unsigned int *count) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + + // search for equivalent NUMID in effective sound card + if (elemInfo) { + *type = snd_ctl_elem_info_get_type(elemInfo); + *count = snd_ctl_elem_info_get_count(elemInfo); + *acc = SND_CTL_EXT_ACCESS_READWRITE; // Future ToBeDone + return 0; + } + + if (callback) { + snd_ctl_get_attrib_t item; + + int err = callback(plughandle, CTLCB_GET_ATTRIBUTE, key, &item); + if (!err) { + *type = item.type; + *acc = item.acc; + *count = item.count; + } + return err; + } + + return -1; +} + +static int AfbHalGetIntInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *imin, long *imax, long *istep) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + if (elemInfo) { + + // Should be normalised to make everything 0-100% + *imin = (long) snd_ctl_elem_info_get_min(elemInfo); + *imax = (long) snd_ctl_elem_info_get_min(elemInfo); + *istep = (long) snd_ctl_elem_info_get_min(elemInfo); + return 0; + } + + if (callback) { + snd_ctl_get_int_info_t item; + + int err = callback(plughandle, CTLCB_GET_INTEGER_INFO, key, &item); + if (!err) { + *imin = item.imin; + *imax = item.imax; + *istep = item.istep; + } + return err; + } + + return -1; +} + +static int AfbHalGetEnumInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + if (elemInfo) *items = snd_ctl_elem_info_get_items(elemInfo); + if (callback) callback(plughandle, CTLCB_GET_ENUMERATED_INFO, key, items); + + return 0; +} + +static int AfbHalGetEnumName(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int item, char *name, size_t name_max_len) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + + //name= snd_ctl_elem_info_get_item_name(elemInfo); + return 0; +} + +static int AfbHalReadInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + + return 0; +} + +static int AfbHalReadEnumerate(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + return 0; +} + +static int AfbHalWriteInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + return 0; +} + +static int AfbHalWriteEnum(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + return 0; +} + +static int AfbHalEventRead(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + return -EAGAIN; +} + +static int AfbHalElemList(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, plughandle->ctls[offset].ctlName); + + return 0; +} + +static int AfbHalElemCount(snd_ctl_ext_t *ext) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + int count = plughandle->ctlsCount; + return count; +} + +static void AfbHalClose(snd_ctl_ext_t *ext) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + int err; + + for (int idx = 0; idx < plughandle->ctlsCount; idx++) { + if (plughandle->ctls[idx].ctlName) free((void*) plughandle->ctls[idx].ctlName); + } + + err = snd_ctl_close(plughandle->ctlDev); + if (err) SNDERR("Fail Close sndctl: devid=%s err=%s", plughandle->devid, snd_strerror(err)); + + if (plughandle->devid) free(plughandle->devid); + free(plughandle); +} + +static snd_ctl_ext_callback_t afbHalCBs = { + .close = AfbHalClose, + .elem_count = AfbHalElemCount, + .elem_list = AfbHalElemList, + .find_elem = AfbHalElemFind, + .get_attribute = AfbHalGetAttrib, + .get_integer_info = AfbHalGetIntInfo, + .get_enumerated_info = AfbHalGetEnumInfo, + .get_enumerated_name = AfbHalGetEnumName, + .read_integer = AfbHalReadInt, + .read_enumerated = AfbHalReadEnumerate, + .write_integer = AfbHalWriteInt, + .write_enumerated = AfbHalWriteEnum, + .read_event = AfbHalEventRead, +}; + +SND_CTL_PLUGIN_DEFINE_FUNC(afbhal) { + + snd_config_iterator_t it, next; + snd_ctl_hal_t *plughandle; + int err; + snd_ctl_cb_t AfbHalInitCB; + const char *libname; + + plughandle = calloc(1, sizeof (snd_ctl_hal_t)); + + snd_config_for_each(it, next, conf) { + snd_config_t *node = snd_config_iterator_entry(it); + const char *id; + + // ignore comment en empty lines + if (snd_config_get_id(node, &id) < 0) continue; + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) continue; + + // devid should point onto a valid sound card + if (strcmp(id, "devid") == 0) { + const char *devid; + if (snd_config_get_string(node, &devid) < 0) { + SNDERR("Invalid string type for %s", id); + return -EINVAL; + } + plughandle->devid = strdup(devid); + + // open control interface for devid + err = snd_ctl_open(&plughandle->ctlDev, plughandle->devid, 0); + if (err < 0) { + SNDERR("Fail to open control device for devid=%s", plughandle->devid); + return -EINVAL; + } + continue; + } + + if (strcmp(id, "cblib") == 0) { + if (snd_config_get_string(node, &libname) < 0) { + SNDERR("Invalid libname string for %s", id); + return -EINVAL; + } + + plughandle->dlHandle = dlopen(libname, RTLD_NOW); + if (!plughandle->dlHandle) { + SNDERR("Fail to open callback sharelib=%s error=%s", libname, dlerror()); + return -EINVAL; + } + + AfbHalInitCB = dlsym(plughandle->dlHandle, "AfbHalInitCB"); + if (!AfbHalInitCB) { + SNDERR("Fail find 'AfbHalInitCB' symbol into callbacks sharelib=%s", libname); + return -EINVAL; + } + + err = (*AfbHalInitCB)(plughandle, CTLCB_INIT, 0, 0); + if (err) { + SNDERR("Fail AfbHalInitCB err=%d", err); + return -EINVAL; + } + + continue; + } + + if (strcmp(id, "ctl") == 0) { + const char *ctlConf; + snd_config_type_t ctype; + snd_config_iterator_t currentCtl, follow; + snd_config_t *itemConf; + + ctype = snd_config_get_type(node); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string(node, &ctlConf); + SNDERR("Invalid compound type for %s", node); + return -EINVAL; + } + + // loop on each ctl within ctls + + snd_config_for_each(currentCtl, follow, node) { + snd_config_t *ctlconfig = snd_config_iterator_entry(currentCtl); + snd_ctl_elem_info_t *elemInfo; + const char *ctlLabel, *ctlName; + + // ignore empty line + if (snd_config_get_id(ctlconfig, &ctlLabel) < 0) continue; + + // each clt should be a valid config compound + ctype = snd_config_get_type(ctlconfig); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string(node, &ctlConf); + SNDERR("Invalid ctl config for %s", ctlLabel); + return -EINVAL; + } + + err = snd_config_search(ctlconfig, "ctl", &itemConf); + if (!err) { + if (snd_config_get_integer(itemConf, (long*) &plughandle->ctls[plughandle->ctlsCount].ctlNumid) < 0) { + SNDERR("Not Integer: ctl:%s numid should be a valid integer", ctlLabel); + return -EINVAL; + } + + // Make sure than numid is valid on slave snd card + snd_ctl_elem_info_malloc(&elemInfo); + snd_ctl_elem_info_set_numid(elemInfo, (int) plughandle->ctls[plughandle->ctlsCount].ctlNumid); + plughandle->infos[plughandle->ctlsCount] = elemInfo; + + err = snd_ctl_elem_info(plughandle->ctlDev, elemInfo); + if (err) { + SNDERR("Not Found: 'numid=%d' for 'devid=%s'", plughandle->ctls[plughandle->ctlsCount].ctlNumid, plughandle->devid); + return -EINVAL; + } + } + + err = snd_config_search(ctlconfig, "ctlcb", &itemConf); + if (!err) { + const char *funcname; + void *funcaddr; + + if (snd_config_get_string(itemConf, &funcname) < 0) { + SNDERR("Not string: ctl:%s cbname should be a valid string", ctlLabel); + return -EINVAL; + } + + if (funcname[0] != '@') { + SNDERR("Not string: ctl:%s cbname=%s should be prefixed with '@' ", ctlLabel, funcname); + return -EINVAL; + } + + if (!plughandle->dlHandle) { + SNDERR("No CB: ctl:%s cblib:/my/libcallback missing from asoundrc", ctlLabel); + return -EINVAL; + } + + funcaddr = dlsym(plughandle->dlHandle, &funcname[1]); + if (!funcaddr) { + SNDERR("NotFound CB: ctl:%s cbname='%s' no symbol into %s", ctlLabel, &funcname[1], libname); + return -EINVAL; + } + plughandle->cbs[plughandle->ctlsCount] = funcaddr; + } + + err = snd_config_search(ctlconfig, "name", &itemConf); + if (err) { + SNDERR("Not Found: 'name' mandatory in ctl config"); + return -EINVAL; + } + + if (snd_config_get_string(itemConf, &ctlName) < 0) { + SNDERR("Not String: ctl:%s 'name' should be a valie string", ctlLabel); + return -EINVAL; + } + plughandle->ctls[plughandle->ctlsCount].ctlName = strdup(ctlName); + + // move to next ctl if any + plughandle->ctlsCount++; + } // end for each ctl + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + + + + // Create ALSA control plugin structure + plughandle->ext.version = SND_CTL_EXT_VERSION; + plughandle->ext.card_idx = 0; /* FIXME */ + strcpy(plughandle->ext.id, "AFB-HAL-CTL"); + strcpy(plughandle->ext.driver, "AFB-HAL"); + strcpy(plughandle->ext.name, "AFB-HAL Control Plugin"); + strcpy(plughandle->ext.mixername, "AFB-HAL Mixer Plugin"); + strcpy(plughandle->ext.longname, "Automotive-Linux Sound Abstraction Control Plugin"); + plughandle->ext.poll_fd = -1; + plughandle->ext.callback = &afbHalCBs; + plughandle->ext.private_data = (void*) plughandle; + + + + err = snd_ctl_ext_create(&plughandle->ext, name, mode); + if (err < 0) { + SNDERR("Fail Register sndctl for devid=%s", plughandle->devid); + goto OnErrorExit; + } + + // Plugin register controls update handlep before exiting + *handlep = plughandle->ext.handle; + return 0; + +OnErrorExit: + free(plughandle); + return -1; +} + +SND_CTL_PLUGIN_SYMBOL(afbhal); diff --git a/Alsa-Plugin/_Alsa-Hal-plugin/CMakeLists.txt b/Alsa-Plugin/_Alsa-Hal-plugin/CMakeLists.txt new file mode 100644 index 0000000..93642b2 --- /dev/null +++ b/Alsa-Plugin/_Alsa-Hal-plugin/CMakeLists.txt @@ -0,0 +1,55 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# 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. +########################################################################### + +# Activate ALSA dynamic build build mode get resolve "snd_dlsym_start" +add_compile_options(-DPIC) + +# Add target to project dependency list +PROJECT_TARGET_ADD(ctl_afbhal) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCtl.c) + + # Alsa Plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "libasound_module_" + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib/alsa-lib) + +PROJECT_TARGET_ADD(cb_sample) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCb.c) + + # Alsa Plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afbhal_" + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) diff --git a/Alsa-Plugin/_Alsa-Hal-plugin/README.md b/Alsa-Plugin/_Alsa-Hal-plugin/README.md new file mode 100644 index 0000000..1c1e7a2 --- /dev/null +++ b/Alsa-Plugin/_Alsa-Hal-plugin/README.md @@ -0,0 +1,44 @@ +Hal-Plugin + +Object: provide an ALSA a HAL (Hardware Abstraction Layer) to automotive sound cards +Status: Current version is proof of concept and not a usable product +Author: Fulup Ar Foll +Date : June-2017 + +HAL-plugin allows: + - to expose any existing NUMID under a customized label name, this is order to abstract sound card config. + - to add non alsa NUMID supported through a callback mechanism (eg: volume rampdown, power off, ...) + +installation + - Plugin should be placed where ever alsaplugins are located (typically: /usr/share/alsa-lib) + - Callback sharelib directory should be declare in $LD_LIBRARY_PATH + +Config +``` +cat ~/.asoundrc + ctl.agl_hal { + type afbhal + devid "hw:4" + cblib "afbhal_cb_sample.so" + ctls { + # ctlLabel {numid integer name "Alsa Ctl Name"} + MasterSwitch { numid 4 name "My_First_Control" } + MasterVol { numid 5 name "My_Second_Control" } + CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} + } + pcm.agl_hal { + type copy # Copy PCM + slave "hw:4" # Slave name + } +``` + +With such a config file + - numid=4 from sndcard=hw:4 is renamed "My_First_Control" + - numid=4 from sndcard=hw:4 is renamed "My_Second_Control" + - numid=4 will call AfbHalSampleCB from afbhal_cb_sample.so + +Note: to really implement a usable HAL at minimum every ALSA call should be implemented and read/write of values should be normalised from 0 to 100% with a step of 1. + +Conclusion: This demonstrate that implementing a HAL that both abstract ALSA get/set and enable non ALSA support is possible at an acceptable cost +without having to hack ALSA source code. The beauty of the model is than it is fully transparent to any ALSA application. The limit is that the plugin is loaded +within every application context, thus interaction with an external event loop remains complete as well as conflict management in case of share resources. diff --git a/HAL-afb/HAL-interface/hal-interface.c b/HAL-afb/HAL-interface/hal-interface.c index cba10d7..4d86504 100644 --- a/HAL-afb/HAL-interface/hal-interface.c +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -25,40 +25,12 @@ alsaHalSndCardT *halSndCard; -STATIC const char *halCtlsLabels[] = { - - [Master_Playback_Volume] = "Master_Playback_Volume", - [Master_OnOff_Switch] = "Master_OnOff_Switch", - [Master_Playback_Ramp]= "Master_Playback_Ramp", - [PCM_Playback_Volume] = "PCM_Playback_Volume", - [PCM_Playback_Switch] = "PCM_Playback_Switch", - [Capture_Volume] = "Capture_Volume", - - [Vol_Ramp] = "Volume_Ramp", - [Vol_Ramp_Set_Mode] = "Volume_Ramp_Mode", - [Vol_Ramp_Set_Delay] = "Volume_Ramp_Delay", - [Vol_Ramp_Set_Down] = "Volume_Ramp_Down", - [Vol_Ramp_Set_Up] = "Volume_Ramp_Up", - [Vol_Ramp_Set_Slave] = "Volume_Ramp_Slave", - - [EndHalCrlTag] = NULL -}; - -PUBLIC char *halVolRampModes[] = { - - [RAMP_VOL_NONE] = "None", - [RAMP_VOL_NORMAL] = "Normal", - [RAMP_VOL_SMOOTH] = "Smooth", - [RAMP_VOL_EMERGENCY] = "Emergency", - - [EndHalVolMod] = NULL, - -}; // Force specific HAL to depend on ShareHalLib -PUBLIC char* SharedHalLibVersion="1.0"; - +PUBLIC char* SharedHalLibVersion = "1.0"; + // Subscribe to AudioBinding events + STATIC void halSubscribe(afb_req request) { const char *devidJ = afb_req_value(request, "devid"); if (devidJ == NULL) { @@ -67,194 +39,200 @@ STATIC void halSubscribe(afb_req request) { } // Map HAL ctlName to ctlLabel -STATIC int halCtlStringToIndex (const char* label) { - alsaHalMapT *halCtls= halSndCard->ctls; - for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { - if (halCtls[idx].label && !strcmp (halCtls[idx].label, label)) return idx; +STATIC int halCtlStringToIndex(const char* label) { + alsaHalMapT *halCtls = halSndCard->ctls; + + for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { + if (halCtls[idx].label && !strcmp(halCtls[idx].label, label)) return idx; } - + // not found return -1; } -STATIC int halCtlTagToIndex (halCtlsEnumT tag) { - alsaHalMapT *halCtls= halSndCard->ctls; +STATIC int halCtlTagToIndex(halCtlsTagT tag) { + alsaHalMapT *halCtls = halSndCard->ctls; - for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { - if (halCtls[idx].tag == tag) return idx; + for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { + if (halCtls[idx].tag == tag) return idx; } - + // not found return -1; } // Return ALL HAL snd controls + PUBLIC void halListCtls(afb_req request) { - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; json_object *ctlsHalJ = json_object_new_array(); - + for (int idx = 0; halCtls[idx].ctl.numid; idx++) { json_object *ctlHalJ = json_object_new_object(); - - json_object_object_add(ctlHalJ, "label", json_object_new_string(halCtls[idx].label)); - json_object_object_add(ctlHalJ, "tag" , json_object_new_int(halCtls[idx].tag)); + + if (halCtls[idx].label) json_object_object_add(ctlHalJ, "label", json_object_new_string(halCtls[idx].label)); + else json_object_object_add(ctlHalJ, "label", json_object_new_string("HAL Label Not Set ")); + json_object_object_add(ctlHalJ, "tag", json_object_new_int(halCtls[idx].tag)); json_object_object_add(ctlHalJ, "count", json_object_new_int(halCtls[idx].ctl.count)); - - json_object_array_add (ctlsHalJ, ctlHalJ); + + json_object_array_add(ctlsHalJ, ctlHalJ); } - - afb_req_success (request, ctlsHalJ, NULL); + + afb_req_success(request, ctlsHalJ, NULL); } -STATIC int halGetCtlIndex (afb_req request, json_object*ctlInJ) { +STATIC int halGetCtlIndex(afb_req request, json_object*ctlInJ) { json_object *tmpJ; int tag, index, done; // check 1st short command mode [tag1, tag2, ...] - enum json_type jtype = json_object_get_type (ctlInJ); + enum json_type jtype = json_object_get_type(ctlInJ); switch (jtype) { - case json_type_array: - tmpJ = json_object_array_get_idx (ctlInJ, 0); + case json_type_array: + tmpJ = json_object_array_get_idx(ctlInJ, 0); tag = json_object_get_int(tmpJ); - index = halCtlTagToIndex(tag); + index = halCtlTagToIndex(tag); break; - - case json_type_int: + + case json_type_int: tag = json_object_get_int(ctlInJ); - index = halCtlTagToIndex(tag); + index = halCtlTagToIndex(tag); break; - + case json_type_object: - done = json_object_object_get_ex (ctlInJ, "tag" , &tmpJ); + done = json_object_object_get_ex(ctlInJ, "tag", &tmpJ); if (done) { tag = json_object_get_int(tmpJ); - index = halCtlTagToIndex(tag); + index = halCtlTagToIndex(tag); } else { const char *label; - done = json_object_object_get_ex (ctlInJ, "label" , &tmpJ); + done = json_object_object_get_ex(ctlInJ, "label", &tmpJ); if (!done) goto OnErrorExit; label = json_object_get_string(tmpJ); index = halCtlStringToIndex(label); } break; - - default: + + default: goto OnErrorExit; } - + if (index < 0) goto OnErrorExit; - + // return corresponding lowlevel numid to querylist return index; - - OnErrorExit: - afb_req_fail_f(request, "ctl-invalid", "No Label/Tag given ctl='%s'", json_object_get_string(ctlInJ)); - return -1; -} +OnErrorExit: + afb_req_fail_f(request, "ctl-invalid", "No Label/Tag given ctl='%s'", json_object_get_string(ctlInJ)); + return -1; +} -STATIC int halCallAlsaSetCtls (json_object *ctlsOutJ) { +STATIC int halCallAlsaSetCtls(json_object *ctlsOutJ) { json_object *responseJ, *queryJ; int err; - + // Call now level CTL queryJ = json_object_new_object(); - json_object_object_add(queryJ, "devid", json_object_new_string (halSndCard->devid)); + json_object_object_add(queryJ, "devid", json_object_new_string(halSndCard->devid)); json_object_object_add(queryJ, "ctl", ctlsOutJ); - - err= afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ); - json_object_put (responseJ); // let's ignore response - + + err = afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ); + json_object_put(responseJ); // let's ignore response + return err; } // retrieve a single HAL control from its tag. -PUBLIC int halSetCtlByTag (halRampEnumT tag, int value) { + +PUBLIC int halSetCtlByTag(halRampEnumT tag, int value) { json_object *ctlJ = json_object_new_array(); - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; int err, index; - - index = halCtlTagToIndex(tag); + + index = halCtlTagToIndex(tag); if (index < 0) goto OnErrorExit; - + json_object_array_add(ctlJ, json_object_new_int(halCtls[index].ctl.numid)); - json_object_array_add(ctlJ, volumeNormalise (ACTION_SET, &halCtls[index].ctl, json_object_new_int(value))); - + json_object_array_add(ctlJ, volumeNormalise(ACTION_SET, &halCtls[index].ctl, json_object_new_int(value))); + err = halCallAlsaSetCtls(ctlJ); - + return err; - - OnErrorExit: - return -1; + +OnErrorExit: + return -1; } // Translate high level control to low level and call lower layer + PUBLIC void halSetCtls(afb_req request) { - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; int err, done, index; json_object *ctlsInJ, *ctlsOutJ, *valuesJ; // get query from request ctlsInJ = afb_req_json(request); - + switch (json_object_get_type(ctlsInJ)) { - case json_type_object: { + case json_type_object: + { ctlsOutJ = json_object_new_object(); - + // control is in literal form {tag=xxx, label=xxx, value=xxxx} - index = halGetCtlIndex (request, ctlsInJ); + index = halGetCtlIndex(request, ctlsInJ); if (index < 0) goto OnErrorExit; - - done= json_object_object_get_ex (ctlsInJ, "val" , &valuesJ); + + done = json_object_object_get_ex(ctlsInJ, "val", &valuesJ); if (!done) { afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ)); - goto OnErrorExit; + goto OnErrorExit; } - - json_object_object_add (ctlsOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); - json_object_object_add (ctlsOutJ,"val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); + + json_object_object_add(ctlsOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); + json_object_object_add(ctlsOutJ, "val", volumeNormalise(ACTION_SET, &halCtls[index].ctl, valuesJ)); break; } - - case json_type_array: { + + case json_type_array: + { ctlsOutJ = json_object_new_array(); - - for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); - index= halGetCtlIndex (request, ctlInJ); + + for (int idx = 0; idx < json_object_array_length(ctlsInJ); idx++) { + json_object *ctlInJ = json_object_array_get_idx(ctlsInJ, idx); + index = halGetCtlIndex(request, ctlInJ); if (index < 0) goto OnErrorExit; - done= json_object_object_get_ex (ctlInJ, "val" , &valuesJ); + done = json_object_object_get_ex(ctlInJ, "val", &valuesJ); if (!done) { afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ)); - goto OnErrorExit; + goto OnErrorExit; } // let's create alsa low level set control request json_object *ctlOutJ = json_object_new_object(); - json_object_object_add (ctlOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); - json_object_object_add (ctlOutJ, "val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); - - json_object_array_add (ctlsOutJ, ctlOutJ); + json_object_object_add(ctlOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); + json_object_object_add(ctlOutJ, "val", volumeNormalise(ACTION_SET, &halCtls[index].ctl, valuesJ)); + + json_object_array_add(ctlsOutJ, ctlOutJ); } break; } - + default: afb_req_fail_f(request, "ctl-invalid", "Not a valid JSON ctl='%s'", json_object_get_string(ctlsInJ)); - goto OnErrorExit; + goto OnErrorExit; } - err = halCallAlsaSetCtls (ctlsOutJ); + err = halCallAlsaSetCtls(ctlsOutJ); if (err) { afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(ctlsOutJ)); - goto OnErrorExit; + goto OnErrorExit; } - - afb_req_success (request, NULL, NULL); + + afb_req_success(request, NULL, NULL); return; OnErrorExit: @@ -262,8 +240,9 @@ OnErrorExit: }; // Remap low level controls into HAL hight level ones + STATIC json_object *HalGetPrepareResponse(afb_req request, json_object *ctlsJ) { - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; json_object *halResponseJ; int length; @@ -274,14 +253,14 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, json_object *ctlsJ) { length = json_object_array_length(ctlsJ); break; case json_type_object: - halResponseJ=NULL; + halResponseJ = NULL; length = 1; break; default: afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol ctlsJ=%s", json_object_get_string(ctlsJ)); goto OnErrorExit; } - + // loop on array and store values into client context for (int idx = 0; idx < length; idx++) { json_object *sndCtlJ, *valJ, *numidJ; @@ -289,356 +268,358 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, json_object *ctlsJ) { // extract control from array if any if (halResponseJ) sndCtlJ = json_object_array_get_idx(ctlsJ, idx); - else sndCtlJ=ctlsJ; - - if (!json_object_object_get_ex(sndCtlJ, "id", &numidJ) || !json_object_object_get_ex(sndCtlJ, "val", &valJ)) { + else sndCtlJ = ctlsJ; + + if (!json_object_object_get_ex(sndCtlJ, "id", &numidJ) || !json_object_object_get_ex(sndCtlJ, "val", &valJ)) { afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from alsa/getcontrol ctl=%s", json_object_get_string(sndCtlJ)); goto OnErrorExit; } - + // HAL and Business logic use the same AlsaMixerHal.h direct conversion - numid= (halCtlsEnumT) json_object_get_int(numidJ); + numid = (halCtlsTagT) json_object_get_int(numidJ); for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - if (halCtls[idx].ctl.numid == numid) { + if (halCtls[idx].ctl.numid == numid) { // translate low level numid to HAL one and normalise values json_object *halCtlJ = json_object_new_object(); json_object_object_add(halCtlJ, "label", json_object_new_string(halCtls[idx].label)); // idx+1 == HAL/NUMID - json_object_object_add(halCtlJ, "tag" , json_object_new_int(halCtls[idx].tag)); // idx+1 == HAL/NUMID - json_object_object_add(halCtlJ, "val" , volumeNormalise(ACTION_GET, &halCtls[idx].ctl, valJ)); - + json_object_object_add(halCtlJ, "tag", json_object_new_int(halCtls[idx].tag)); // idx+1 == HAL/NUMID + json_object_object_add(halCtlJ, "val", volumeNormalise(ACTION_GET, &halCtls[idx].ctl, valJ)); + if (halResponseJ) json_object_array_add(halResponseJ, halCtlJ); - else halResponseJ=halCtlJ; - + else halResponseJ = halCtlJ; + break; - } + } } - if ( halCtls[idx].ctl.numid == 0) { + if (halCtls[idx].ctl.numid == 0) { afb_req_fail_f(request, "ctl-invalid", "Invalid Control numid=%d from alsa/getcontrol ctlJ=%s", numid, json_object_get_string(sndCtlJ)); goto OnErrorExit; - } + } } return halResponseJ; - - OnErrorExit: - return NULL; -} - +OnErrorExit: + return NULL; +} -STATIC json_object *halCallAlsaGetCtls (json_object *ctlsOutJ) { +STATIC json_object *halCallAlsaGetCtls(json_object *ctlsOutJ) { json_object *responseJ, *queryJ; int err, done; - + // Call now level CTL queryJ = json_object_new_object(); - json_object_object_add(queryJ, "devid", json_object_new_string (halSndCard->devid)); + json_object_object_add(queryJ, "devid", json_object_new_string(halSndCard->devid)); json_object_object_add(queryJ, "ctl", ctlsOutJ); - err= afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); + err = afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); if (err) goto OnErrorExit; - + // Let ignore info data if any and keep on response - done = json_object_object_get_ex (responseJ, "response", &responseJ); + done = json_object_object_get_ex(responseJ, "response", &responseJ); if (!done) goto OnErrorExit; - + return responseJ; - - OnErrorExit: - return NULL; + +OnErrorExit: + return NULL; } // retrieve a single HAL control from its tag. -PUBLIC json_object *halGetCtlByTag (halRampEnumT tag) { + +PUBLIC json_object *halGetCtlByTag(halRampEnumT tag) { json_object *responseJ, *valJ; - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; int done, index; - - index = halCtlTagToIndex(tag); + + index = halCtlTagToIndex(tag); if (index < 0) goto OnErrorExit; responseJ = halCallAlsaGetCtls(json_object_new_int(halCtls[index].ctl.numid)); - + done = json_object_object_get_ex(responseJ, "val", &valJ); if (!done) goto OnErrorExit; - + return volumeNormalise(ACTION_GET, &halCtls[index].ctl, valJ); - - OnErrorExit: - return NULL; + +OnErrorExit: + return NULL; } // Translate high level control to low level and call lower layer + PUBLIC void halGetCtls(afb_req request) { int index; - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; json_object *ctlsInJ, *ctlsOutJ, *responseJ; // get query from request ctlsInJ = afb_req_json(request); ctlsOutJ = json_object_new_array(); - + switch (json_object_get_type(ctlsInJ)) { - case json_type_object: { + case json_type_object: + { - index = halGetCtlIndex (request, ctlsInJ); + index = halGetCtlIndex(request, ctlsInJ); if (index < 0) goto OnErrorExit; - json_object_array_add (ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid)); + json_object_array_add(ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid)); break; } - - case json_type_array: { - - for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); - index= halGetCtlIndex (request, ctlInJ); + + case json_type_array: + { + + for (int idx = 0; idx < json_object_array_length(ctlsInJ); idx++) { + json_object *ctlInJ = json_object_array_get_idx(ctlsInJ, idx); + index = halGetCtlIndex(request, ctlInJ); if (index < 0) goto OnErrorExit; - json_object_array_add (ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid)); + json_object_array_add(ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid)); } break; } - + default: afb_req_fail_f(request, "ctl-invalid", "Not a valid JSON ctl='%s'", json_object_get_string(ctlsInJ)); - goto OnErrorExit; + goto OnErrorExit; } // Call now level CTL - responseJ = halCallAlsaGetCtls (ctlsOutJ); + responseJ = halCallAlsaGetCtls(ctlsOutJ); if (!responseJ) { afb_req_fail_f(request, "subcall:alsacore/getctl", "%s", json_object_get_string(responseJ)); - goto OnErrorExit; + goto OnErrorExit; } - + // map back low level response to HAL ctl with normalised values - json_object *halResponse = HalGetPrepareResponse(request, responseJ); + json_object *halResponse = HalGetPrepareResponse(request, responseJ); if (!halResponse) goto OnErrorExit; - - afb_req_success (request, halResponse, NULL); + + afb_req_success(request, halResponse, NULL); return; OnErrorExit: return; }; - -STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, json_object *sndCtlJ) { +STATIC int UpdateOneSndCtl(alsaHalCtlMapT *ctl, json_object *sndCtlJ) { json_object *tmpJ, *ctlJ; - json_object_object_get_ex (sndCtlJ, "name" , &tmpJ); - ctl->name = (char*)json_object_get_string(tmpJ); - - json_object_object_get_ex (sndCtlJ, "id" , &tmpJ); - ctl->numid = json_object_get_int(tmpJ); - + json_object_object_get_ex(sndCtlJ, "name", &tmpJ); + ctl->name = (char*) json_object_get_string(tmpJ); + + json_object_object_get_ex(sndCtlJ, "id", &tmpJ); + ctl->numid = json_object_get_int(tmpJ); + // make sure we face a valid Alsa Low level ctl - if (!json_object_object_get_ex (sndCtlJ, "ctl" , &ctlJ)) goto OnErrorExit; - - json_object_object_get_ex (ctlJ, "min" , &tmpJ); - ctl->minval = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "max" , &tmpJ); - ctl->maxval = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "step" , &tmpJ); - ctl->step = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "count" , &tmpJ); - ctl->count = json_object_get_int(tmpJ); - - json_object_object_get_ex (ctlJ, "type" , &tmpJ); - ctl->type = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); - + if (!json_object_object_get_ex(sndCtlJ, "ctl", &ctlJ)) goto OnErrorExit; + + json_object_object_get_ex(ctlJ, "min", &tmpJ); + ctl->minval = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "max", &tmpJ); + ctl->maxval = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "step", &tmpJ); + ctl->step = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "count", &tmpJ); + ctl->count = json_object_get_int(tmpJ); + + json_object_object_get_ex(ctlJ, "type", &tmpJ); + ctl->type = (snd_ctl_elem_type_t) json_object_get_int(tmpJ); + // process dbscale TLV if any - if (json_object_object_get_ex (sndCtlJ, "tlv" , &tmpJ)) { + if (json_object_object_get_ex(sndCtlJ, "tlv", &tmpJ)) { json_object *dbscaleJ; - - if (!json_object_object_get_ex (tmpJ, "dbscale" , &dbscaleJ)) { + + if (!json_object_object_get_ex(tmpJ, "dbscale", &dbscaleJ)) { AFB_WARNING("TLV found but not DBscale attached ctl name=%s numid=%d", ctl->name, ctl->numid); } else { - ctl->dbscale = malloc (sizeof (alsaHalDBscaleT)); - - json_object_object_get_ex (dbscaleJ, "min" , &tmpJ); - ctl->dbscale->min = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); - - json_object_object_get_ex (dbscaleJ, "max" , &tmpJ); - ctl->dbscale->max = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); - - json_object_object_get_ex (dbscaleJ, "step" , &tmpJ); - ctl->dbscale->step = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); - - json_object_object_get_ex (dbscaleJ, "mute" , &tmpJ); - ctl->dbscale->mute = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + ctl->dbscale = malloc(sizeof (alsaHalDBscaleT)); + + json_object_object_get_ex(dbscaleJ, "min", &tmpJ); + ctl->dbscale->min = (snd_ctl_elem_type_t) json_object_get_int(tmpJ); + + json_object_object_get_ex(dbscaleJ, "max", &tmpJ); + ctl->dbscale->max = (snd_ctl_elem_type_t) json_object_get_int(tmpJ); + + json_object_object_get_ex(dbscaleJ, "step", &tmpJ); + ctl->dbscale->step = (snd_ctl_elem_type_t) json_object_get_int(tmpJ); + + json_object_object_get_ex(dbscaleJ, "mute", &tmpJ); + ctl->dbscale->mute = (snd_ctl_elem_type_t) json_object_get_int(tmpJ); } } - + return 0; - - OnErrorExit: - return -1; + +OnErrorExit: + return -1; } // this is call when after all bindings are loaded -PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard) { + +PUBLIC int halServiceInit(const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard) { int err; json_object *queryurl, *responseJ, *devidJ, *ctlsJ, *tmpJ; - alsaHalMapT *halCtls= alsaHalSndCard->ctls; - + alsaHalMapT *halCtls = alsaHalSndCard->ctls; + // if not volume normalisation CB provided use default one - if (!alsaHalSndCard->volumeCB) alsaHalSndCard->volumeCB=volumeNormalise; - halSndCard= alsaHalSndCard; - - err= afb_daemon_require_api("alsacore", 1); + if (!alsaHalSndCard->volumeCB) alsaHalSndCard->volumeCB = volumeNormalise; + halSndCard = alsaHalSndCard; + + err = afb_daemon_require_api("alsacore", 1); if (err) { - AFB_ERROR ("AlsaCore missing cannot use AlsaHAL"); - goto OnErrorExit; + AFB_ERROR("AlsaCore missing cannot use AlsaHAL"); + goto OnErrorExit; } // register HAL with Alsa Low Level Binder queryurl = json_object_new_object(); json_object_object_add(queryurl, "prefix", json_object_new_string(apiPrefix)); json_object_object_add(queryurl, "sndname", json_object_new_string(alsaHalSndCard->name)); - - err= afb_service_call_sync ("alsacore", "halregister", queryurl, &responseJ); + + err = afb_service_call_sync("alsacore", "halregister", queryurl, &responseJ); json_object_put(queryurl); if (err) { - AFB_NOTICE ("Fail to register HAL to ALSA lowlevel binding Response='%s'", json_object_get_string(responseJ)); + AFB_NOTICE("Fail to register HAL to ALSA lowlevel binding Response='%s'", json_object_get_string(responseJ)); goto OnErrorExit; } - + // extract sound devidJ from HAL registration if (!json_object_object_get_ex(responseJ, "response", &tmpJ) || !json_object_object_get_ex(tmpJ, "devid", &devidJ)) { - AFB_ERROR ("Ooops: Internal error no devidJ return from HAL registration Response='%s'", json_object_get_string(responseJ)); + AFB_ERROR("Ooops: Internal error no devidJ return from HAL registration Response='%s'", json_object_get_string(responseJ)); goto OnErrorExit; } - + // save devid for future use - halSndCard->devid = strdup (json_object_get_string(devidJ)); - + halSndCard->devid = strdup(json_object_get_string(devidJ)); + // for each Non Alsa Control callback create a custom control - ctlsJ= json_object_new_array(); - for (int idx= 0; (halCtls[idx].ctl.name||halCtls[idx].ctl.numid); idx++) { + ctlsJ = json_object_new_array(); + for (int idx = 0; (halCtls[idx].ctl.name || halCtls[idx].ctl.numid); idx++) { json_object *ctlJ; - - // map HAL ctlTad with halCrlLabel to simplify set/get ctl operations using Labels - halCtls[idx].label = halCtlsLabels[halCtls[idx].tag]; - - if (halCtls[idx].cb.callback != NULL) { - ctlJ = json_object_new_object(); - if (halCtls[idx].ctl.name) json_object_object_add(ctlJ, "name" , json_object_new_string(halCtls[idx].ctl.name)); - if (halCtls[idx].ctl.minval) json_object_object_add(ctlJ, "min" , json_object_new_int(halCtls[idx].ctl.minval)); - if (halCtls[idx].ctl.maxval) json_object_object_add(ctlJ, "max" , json_object_new_int(halCtls[idx].ctl.maxval)); - if (halCtls[idx].ctl.step) json_object_object_add(ctlJ, "step" , json_object_new_int(halCtls[idx].ctl.step)); - if (halCtls[idx].ctl.type) json_object_object_add(ctlJ, "type" , json_object_new_int(halCtls[idx].ctl.type)); - if (halCtls[idx].ctl.count) json_object_object_add(ctlJ, "count", json_object_new_int(halCtls[idx].ctl.count)); - if (halCtls[idx].ctl.value) json_object_object_add(ctlJ, "value", json_object_new_int(halCtls[idx].ctl.value)); - - if (halCtls[idx].ctl.dbscale) { - json_object *dbscaleJ=json_object_new_object(); - if (halCtls[idx].ctl.dbscale->max) json_object_object_add(dbscaleJ, "max" , json_object_new_int(halCtls[idx].ctl.dbscale->max)); - if (halCtls[idx].ctl.dbscale->min) json_object_object_add(dbscaleJ, "min" , json_object_new_int(halCtls[idx].ctl.dbscale->min)); - if (halCtls[idx].ctl.dbscale->step) json_object_object_add(dbscaleJ, "step", json_object_new_int(halCtls[idx].ctl.dbscale->step)); - if (halCtls[idx].ctl.dbscale->mute) json_object_object_add(dbscaleJ, "mute", json_object_new_int(halCtls[idx].ctl.dbscale->mute)); - json_object_object_add(ctlJ, "dbscale" , dbscaleJ); - } - - if (halCtls[idx].ctl.enums) { - json_object *enumsJ=json_object_new_array(); - for (int jdx=0; halCtls[idx].ctl.enums[jdx]; jdx++) { - json_object_array_add(enumsJ, json_object_new_string(halCtls[idx].ctl.enums[jdx])); - } - json_object_object_add(ctlJ, "enums" , enumsJ); + + // Try to find best equivalent label for tag + if (halCtls[idx].tag >StartHalCrlTag && halCtls[idx].tag < EndHalCrlTag && halCtls[idx].label != NULL) { + halCtls[idx].label = halCtlsLabels[halCtls[idx].tag]; + } else { + if (halCtls[idx].ctl.name) halCtls[idx].label=halCtls[idx].ctl.name; + else if (halCtls[idx].info) halCtls[idx].label=halCtls[idx].info; + } + + ctlJ = json_object_new_object(); + if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "ctl", json_object_new_int(halCtls[idx].ctl.numid)); + if (halCtls[idx].ctl.name) json_object_object_add(ctlJ, "name", json_object_new_string(halCtls[idx].ctl.name)); + if (halCtls[idx].ctl.minval) json_object_object_add(ctlJ, "min", json_object_new_int(halCtls[idx].ctl.minval)); + if (halCtls[idx].ctl.maxval) json_object_object_add(ctlJ, "max", json_object_new_int(halCtls[idx].ctl.maxval)); + if (halCtls[idx].ctl.step) json_object_object_add(ctlJ, "step", json_object_new_int(halCtls[idx].ctl.step)); + if (halCtls[idx].ctl.type) json_object_object_add(ctlJ, "type", json_object_new_int(halCtls[idx].ctl.type)); + if (halCtls[idx].ctl.count) json_object_object_add(ctlJ, "count", json_object_new_int(halCtls[idx].ctl.count)); + if (halCtls[idx].ctl.value) json_object_object_add(ctlJ, "value", json_object_new_int(halCtls[idx].ctl.value)); + + if (halCtls[idx].ctl.dbscale) { + json_object *dbscaleJ = json_object_new_object(); + if (halCtls[idx].ctl.dbscale->max) json_object_object_add(dbscaleJ, "max", json_object_new_int(halCtls[idx].ctl.dbscale->max)); + if (halCtls[idx].ctl.dbscale->min) json_object_object_add(dbscaleJ, "min", json_object_new_int(halCtls[idx].ctl.dbscale->min)); + if (halCtls[idx].ctl.dbscale->step) json_object_object_add(dbscaleJ, "step", json_object_new_int(halCtls[idx].ctl.dbscale->step)); + if (halCtls[idx].ctl.dbscale->mute) json_object_object_add(dbscaleJ, "mute", json_object_new_int(halCtls[idx].ctl.dbscale->mute)); + json_object_object_add(ctlJ, "dbscale", dbscaleJ); + } + + if (halCtls[idx].ctl.enums) { + json_object *enumsJ = json_object_new_array(); + for (int jdx = 0; halCtls[idx].ctl.enums[jdx]; jdx++) { + json_object_array_add(enumsJ, json_object_new_string(halCtls[idx].ctl.enums[jdx])); } - json_object_array_add(ctlsJ, ctlJ); - } else { - ctlJ = json_object_new_object(); - if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "ctl" , json_object_new_int(halCtls[idx].ctl.numid)); - if (halCtls[idx].ctl.name) json_object_object_add(ctlJ, "name" , json_object_new_string(halCtls[idx].ctl.name)); - json_object_array_add(ctlsJ, ctlJ); - } + json_object_object_add(ctlJ, "enums", enumsJ); + } + json_object_array_add(ctlsJ, ctlJ); } - + // Build new queryJ to add HAL custom control if any - if (json_object_array_length (ctlsJ) > 0) { + if (json_object_array_length(ctlsJ) > 0) { queryurl = json_object_new_object(); json_object_get(devidJ); // make sure devidJ does not get free by 1st call. - json_object_object_add(queryurl, "devid",devidJ); - json_object_object_add(queryurl, "ctl",ctlsJ); - json_object_object_add(queryurl, "mode",json_object_new_int(QUERY_COMPACT)); - err= afb_service_call_sync ("alsacore", "addcustomctl", queryurl, &responseJ); + json_object_object_add(queryurl, "devid", devidJ); + json_object_object_add(queryurl, "ctl", ctlsJ); + json_object_object_add(queryurl, "mode", json_object_new_int(QUERY_COMPACT)); + err = afb_service_call_sync("alsacore", "addcustomctl", queryurl, &responseJ); if (err) { - AFB_ERROR ("Fail creating HAL Custom ALSA ctls Response='%s'", json_object_get_string(responseJ)); + AFB_ERROR("Fail creating HAL Custom ALSA ctls Response='%s'", json_object_get_string(responseJ)); goto OnErrorExit; - } + } } // Make sure response is valid - json_object_object_get_ex (responseJ, "response" , &ctlsJ); + json_object_object_get_ex(responseJ, "response", &ctlsJ); if (json_object_get_type(ctlsJ) != json_type_array) { - AFB_ERROR ("Response Invalid JSON array ctls Response='%s'", json_object_get_string(responseJ)); - goto OnErrorExit; + AFB_ERROR("Response Invalid JSON array ctls Response='%s'", json_object_get_string(responseJ)); + goto OnErrorExit; } - + // update HAL data from JSON response - for (int idx= 0; idx < json_object_array_length (ctlsJ); idx++) { - json_object *ctlJ = json_object_array_get_idx (ctlsJ, idx); - err= UpdateOneSndCtl(&halCtls[idx].ctl, ctlJ) ; + for (int idx = 0; idx < json_object_array_length(ctlsJ); idx++) { + json_object *ctlJ = json_object_array_get_idx(ctlsJ, idx); + err = UpdateOneSndCtl(&halCtls[idx].ctl, ctlJ); if (err) { - AFB_ERROR ("Fail found MAP Alsa Low level=%s", json_object_get_string(ctlJ)); - goto OnErrorExit; + AFB_ERROR("Fail found MAP Alsa Low level=%s", json_object_get_string(ctlJ)); + goto OnErrorExit; } } - + // finally register for alsa lowlevel event queryurl = json_object_new_object(); - json_object_object_add(queryurl, "devid",devidJ); - err= afb_service_call_sync ("alsacore", "subscribe", queryurl, &responseJ); + json_object_object_add(queryurl, "devid", devidJ); + err = afb_service_call_sync("alsacore", "subscribe", queryurl, &responseJ); if (err) { - AFB_ERROR ("Fail subscribing to ALSA lowlevel events"); + AFB_ERROR("Fail subscribing to ALSA lowlevel events"); goto OnErrorExit; } - - return (0); - - OnErrorExit: + + return (0); + +OnErrorExit: return (1); }; // This receive all event this binding subscribe to + PUBLIC void halServiceEvent(const char *evtname, json_object *eventJ) { int numid; - alsaHalMapT *halCtls= halSndCard->ctls; + alsaHalMapT *halCtls = halSndCard->ctls; json_object *numidJ, *valuesJ; - + AFB_DEBUG("halServiceEvent evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); - - json_object_object_get_ex (eventJ, "id" , &numidJ); - numid = json_object_get_int (numidJ); + + json_object_object_get_ex(eventJ, "id", &numidJ); + numid = json_object_get_int(numidJ); if (!numid) { AFB_ERROR("halServiceEvent noid: evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); return; } - json_object_object_get_ex (eventJ, "val" , &valuesJ); - + json_object_object_get_ex(eventJ, "val", &valuesJ); + // search it corresponding numid in halCtls attach a callback if (numid) { - for (int idx= 0; halCtls[idx].ctl.numid; idx++) { + for (int idx = 0; halCtls[idx].ctl.numid; idx++) { if (halCtls[idx].ctl.numid == numid && halCtls[idx].cb.callback != NULL) { - halCtls[idx].cb.callback (halCtls[idx].tag, &halCtls[idx].ctl, halCtls[idx].cb.handle, valuesJ); + halCtls[idx].cb.callback(halCtls[idx].tag, &halCtls[idx].ctl, halCtls[idx].cb.handle, valuesJ); } } - } + } } // Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT PUBLIC afb_verb_v2 halServiceApi[] = { /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ - { .verb = "ping", .callback = pingtest , .info="ping test for API"}, - { .verb = "ctl-list", .callback = halListCtls , .info="List AGL normalised Sound Controls"}, - { .verb = "ctl-get", .callback = halGetCtls , .info="Get one/many sound controls"}, - { .verb = "ctl-set", .callback = halSetCtls , .info="Set one/many sound controls"}, - { .verb = "evt-sub", .callback = halSubscribe, .info="Subscribe to HAL events"}, + { .verb = "ping", .callback = pingtest, .info = "ping test for API"}, + { .verb = "ctl-list", .callback = halListCtls, .info = "List AGL normalised Sound Controls"}, + { .verb = "ctl-get", .callback = halGetCtls, .info = "Get one/many sound controls"}, + { .verb = "ctl-set", .callback = halSetCtls, .info = "Set one/many sound controls"}, + { .verb = "evt-sub", .callback = halSubscribe, .info = "Subscribe to HAL events"}, { .verb = NULL} /* marker for end of the array */ }; diff --git a/HAL-afb/HAL-interface/hal-interface.h b/HAL-afb/HAL-interface/hal-interface.h index 23944ad..a6310e7 100644 --- a/HAL-afb/HAL-interface/hal-interface.h +++ b/HAL-afb/HAL-interface/hal-interface.h @@ -24,22 +24,22 @@ #include "audio-interface.h" #include - typedef enum { - ACTION_SET, - ACTION_GET + ACTION_SET, + ACTION_GET } ActionSetGetT; // VolRamp Handle Store current status for a given VolRam CB set + typedef struct { - halRampEnumT mode; - halCtlsEnumT slave; - int delay; // delay between volset in us - int stepDown; // linear % - int stepUp; // linear % - int current; // current volume for slave ctl - int target; // target volume - sd_event_source *evtsrc; // event loop timer source + halRampEnumT mode; + halCtlsTagT slave; + int delay; // delay between volset in us + int stepDown; // linear % + int stepUp; // linear % + int current; // current volume for slave ctl + int target; // target volume + sd_event_source *evtsrc; // event loop timer source } halVolRampT; typedef struct { @@ -50,56 +50,56 @@ typedef struct { } alsaHalDBscaleT; typedef struct { - char* name; - int numid; - snd_ctl_elem_type_t type; - int count; - int minval; - int maxval; - int value; - int step; - char **enums; - alsaHalDBscaleT *dbscale; + char* name; + int numid; + snd_ctl_elem_type_t type; + int count; + int minval; + int maxval; + int value; + int step; + const char **enums; + alsaHalDBscaleT *dbscale; } alsaHalCtlMapT; + // avoid compiler warning [Jose does not like typedef :) ] typedef struct afb_service alsaHalServiceT; typedef struct { - void (*callback)(halCtlsEnumT tag, alsaHalCtlMapT *control, void* handle, json_object *valuesJ); - void* handle; + void (*callback)(halCtlsTagT tag, alsaHalCtlMapT *control, void* handle, json_object *valuesJ); + void* handle; } alsaHalCbMapT; typedef struct { - halCtlsEnumT tag; + halCtlsTagT tag; const char *label; alsaHalCtlMapT ctl; alsaHalCbMapT cb; char* info; } alsaHalMapT; -typedef struct { - const char *name; - const char *info; - alsaHalMapT *ctls; - const char *devid; - json_object* (*volumeCB)(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); +typedef struct { + const char *name; + const char *info; + alsaHalMapT *ctls; + const char *devid; + json_object* (*volumeCB)(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); } alsaHalSndCardT; // hal-interface.c extern afb_verb_v2 halServiceApi[]; -extern char *halVolRampModes[]; PUBLIC void halServiceEvent(const char *evtname, json_object *object); -PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard); -PUBLIC json_object *halGetCtlByTag (halRampEnumT tag); -PUBLIC int halSetCtlByTag (halRampEnumT tag, int value); +PUBLIC int halServiceInit(const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard); +PUBLIC json_object *halGetCtlByTag(halRampEnumT tag); +PUBLIC int halSetCtlByTag(halRampEnumT tag, int value); // hal-volramp.c -PUBLIC void volumeRamp (halCtlsEnumT halTag,alsaHalCtlMapT *control, void* handle, json_object *valJ); +PUBLIC void volumeRamp(halCtlsTagT halTag, alsaHalCtlMapT *control, void* handle, json_object *valJ); // hal-volume.c -PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); #endif /* SHAREHALLIB_H */ diff --git a/HAL-afb/HAL-interface/hal-volramp.c b/HAL-afb/HAL-interface/hal-volramp.c index 6343a06..eb39aae 100644 --- a/HAL-afb/HAL-interface/hal-volramp.c +++ b/HAL-afb/HAL-interface/hal-volramp.c @@ -19,26 +19,26 @@ #define _GNU_SOURCE // needed for vasprintf #include "hal-interface.h" -STATIC int RampTimerCB (sd_event_source* source,uint64_t timer, void* handle) { - halVolRampT *volRamp= (halVolRampT*)handle; - int err; - uint64_t usec; +STATIC int RampTimerCB(sd_event_source* source, uint64_t timer, void* handle) { + halVolRampT *volRamp = (halVolRampT*) handle; + int err; + uint64_t usec; // RampDown if (volRamp->current > volRamp->target) { - volRamp->current = volRamp->current - volRamp->stepDown; - if (volRamp->current < volRamp->target) volRamp->current = volRamp->target; + volRamp->current = volRamp->current - volRamp->stepDown; + if (volRamp->current < volRamp->target) volRamp->current = volRamp->target; } - + // RampUp if (volRamp->current < volRamp->target) { - volRamp->current = volRamp->current + volRamp->stepUp; - if (volRamp->current > volRamp->target) volRamp->current = volRamp->target; + volRamp->current = volRamp->current + volRamp->stepUp; + if (volRamp->current > volRamp->target) volRamp->current = volRamp->target; } - + // request current Volume Level err = halSetCtlByTag(volRamp->slave, volRamp->current); - if (err) goto OnErrorExit; + if (err) goto OnErrorExit; // we reach target stop volram event if (volRamp->current == volRamp->target) sd_event_source_unref(source); @@ -46,121 +46,140 @@ STATIC int RampTimerCB (sd_event_source* source,uint64_t timer, void* handle) { // otherwise validate timer for a new run sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); - err=sd_event_source_set_time(source, usec+volRamp->delay); + err = sd_event_source_set_time(source, usec + volRamp->delay); } return 0; - - OnErrorExit: - AFB_WARNING ("RampTimerCB Fail to set HAL ctl tag=%d vol=%d", Master_Playback_Volume, volRamp->current); + +OnErrorExit: + AFB_WARNING("RampTimerCB Fail to set HAL ctl tag=%d vol=%d", Master_Playback_Volume, volRamp->current); sd_event_source_unref(source); // abandon VolRamp return -1; } -/* UCS2 Interface Timer Callback */ STATIC void SetRampTimer(void *handle) { - halVolRampT *volRamp= (halVolRampT*)handle; - - uint64_t usec; - /* set a timer with 250ms accuracy */ - sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); - sd_event_add_time(afb_daemon_get_event_loop(), &volRamp->evtsrc, CLOCK_MONOTONIC, usec, 250, RampTimerCB, volRamp); + halVolRampT *volRamp = (halVolRampT*) handle; + uint64_t usec; + + // set a timer with ~250us accuracy + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_add_time(afb_daemon_get_event_loop(), &volRamp->evtsrc, CLOCK_MONOTONIC, usec, 250, RampTimerCB, volRamp); } -STATIC int volumeDoRamp (halVolRampT *volRamp, int numid, json_object *volumeJ) { +STATIC int volumeDoRamp(halVolRampT *volRamp, int numid, json_object *volumeJ) { json_object *responseJ; - + // request current Volume Level responseJ = halGetCtlByTag(volRamp->slave); if (!responseJ) { - AFB_WARNING ("volumeDoRamp Fail to get HAL ctl tag=%d", Master_Playback_Volume); + AFB_WARNING("volumeDoRamp Fail to get HAL ctl tag=%d", Master_Playback_Volume); goto OnErrorExit; - } - + } + // use 1st volume value as target for ramping switch (json_object_get_type(volumeJ)) { case json_type_array: - volRamp->target=json_object_get_int(json_object_array_get_idx(volumeJ,0)); + volRamp->target = json_object_get_int(json_object_array_get_idx(volumeJ, 0)); break; - + case json_type_int: - volRamp->target=json_object_get_int(volumeJ); + volRamp->target = json_object_get_int(volumeJ); break; - + default: - AFB_WARNING ("volumeDoRamp Invalid volumeJ=%s", json_object_get_string(volumeJ)); + AFB_WARNING("volumeDoRamp Invalid volumeJ=%s", json_object_get_string(volumeJ)); goto OnErrorExit; } - + // use 1st volume value as current for ramping switch (json_object_get_type(responseJ)) { case json_type_array: - volRamp->current=json_object_get_int(json_object_array_get_idx(responseJ,0)); + volRamp->current = json_object_get_int(json_object_array_get_idx(responseJ, 0)); break; - + case json_type_int: - volRamp->current=json_object_get_int(responseJ); + volRamp->current = json_object_get_int(responseJ); break; - + default: - AFB_WARNING ("volumeDoRamp Invalid reponseJ=%s", json_object_get_string(responseJ)); + AFB_WARNING("volumeDoRamp Invalid reponseJ=%s", json_object_get_string(responseJ)); goto OnErrorExit; } - - SetRampTimer (volRamp); - + + SetRampTimer(volRamp); + return 0; - OnErrorExit: +OnErrorExit: return -1; } - -PUBLIC void volumeRamp (halCtlsEnumT halTag, alsaHalCtlMapT *ctl, void* handle, json_object *valJ) { +PUBLIC void volumeRamp(halCtlsTagT halTag, alsaHalCtlMapT *ctl, void* handle, json_object *valJ) { halVolRampT *volRamp = (halVolRampT*) handle; json_object *tmpJ; - if (json_object_get_type(valJ) != json_type_array || volRamp==NULL) goto OnErrorExit; - + if (json_object_get_type(valJ) != json_type_array || volRamp == NULL) goto OnErrorExit; + switch (halTag) { - case Vol_Ramp: - tmpJ = json_object_array_get_idx(valJ, 0); - volumeDoRamp (volRamp, ctl->numid, tmpJ); + + // Only config use wellknown tag. Default is DoVolRamp + default: + tmpJ = json_object_array_get_idx(valJ, 0); + volumeDoRamp(volRamp, ctl->numid, tmpJ); break; - + case Vol_Ramp_Set_Mode: - tmpJ = json_object_array_get_idx(valJ, 0); - volRamp->mode = json_object_get_int (tmpJ); + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->mode = json_object_get_int(tmpJ); + switch (volRamp->mode) { + + case RAMP_VOL_SMOOTH: + volRamp->delay = 100 * 1000; + volRamp->stepDown = 1; + volRamp->stepUp = 1; + break; + + case RAMP_VOL_NORMAL: + volRamp->delay = 100 * 1000; + volRamp->stepDown = 3; + volRamp->stepUp = 2; + break; + + case RAMP_VOL_EMERGENCY: + volRamp->delay = 50 * 1000; + volRamp->stepDown = 6; + volRamp->stepUp = 2; + break; + + default: + goto OnErrorExit; + } break; - + case Vol_Ramp_Set_Slave: - tmpJ = json_object_array_get_idx(valJ, 0); - volRamp->slave = json_object_get_int (tmpJ); + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->slave = json_object_get_int(tmpJ); break; - + case Vol_Ramp_Set_Delay: - tmpJ = json_object_array_get_idx(valJ, 0); - volRamp->delay = 1000*json_object_get_int (tmpJ); + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->delay = 1000 * json_object_get_int(tmpJ); break; case Vol_Ramp_Set_Down: - tmpJ = json_object_array_get_idx(valJ, 0); - volRamp->stepDown = json_object_get_int (tmpJ); + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepDown = json_object_get_int(tmpJ); break; case Vol_Ramp_Set_Up: - tmpJ = json_object_array_get_idx(valJ, 0); - volRamp->stepUp = json_object_get_int (tmpJ); + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepUp = json_object_get_int(tmpJ); break; - - default: - goto OnErrorExit; } - + + return; + +OnErrorExit: + AFB_WARNING("volumeRamp: Invalid Ctrl Event halCtlsTagT=%d numid=%d name=%s value=%s", halTag, ctl->numid, ctl->name, json_object_get_string(valJ)); return; - - OnErrorExit: - AFB_WARNING ("Invalid Ctrl Event halCtlsEnumT=%d numid=%d name=%s value=%s", halTag, ctl->numid, ctl->name, json_object_get_string(valJ)); - return; } - \ No newline at end of file diff --git a/HAL-afb/HAL-interface/hal-volume.c b/HAL-afb/HAL-interface/hal-volume.c index 593b74c..ad2b9a1 100644 --- a/HAL-afb/HAL-interface/hal-volume.c +++ b/HAL-afb/HAL-interface/hal-volume.c @@ -25,126 +25,126 @@ #include #include "hal-interface.h" - -typedef enum { - NORMALIZE_NONE=0, - NORMALIZE_DB_LINEAR, - NORMALIZE_DB_MATH, - NORMALIZE_LINEAR, +typedef enum { + NORMALIZE_NONE = 0, + NORMALIZE_DB_LINEAR, + NORMALIZE_DB_MATH, + NORMALIZE_LINEAR, } enumRandeModeDB_T; // Return Value express from 0-100% -STATIC int dbNormalizeVal (enumRandeModeDB_T normaliseMode, const alsaHalDBscaleT *dbscale, int value) { + +STATIC int dbNormalizeVal(enumRandeModeDB_T normaliseMode, const alsaHalDBscaleT *dbscale, int value) { double normalized, min_norm; // To get real DB from TLV DB values should be divided by 100 switch (normaliseMode) { case NORMALIZE_DB_LINEAR: - normalized = ((double)(value-dbscale->min)/(double)(dbscale->max-dbscale->min)); + normalized = ((double) (value - dbscale->min) / (double) (dbscale->max - dbscale->min)); break; - - case NORMALIZE_DB_MATH: - normalized = exp10((double)(value - dbscale->max) / 6000.0); + + case NORMALIZE_DB_MATH: + normalized = exp10((double) (value - dbscale->max) / 6000.0); if (dbscale->min != SND_CTL_TLV_DB_GAIN_MUTE) { - min_norm = exp10((double)(dbscale->min - dbscale->max) / 20); - normalized = (normalized - min_norm) / (1 - min_norm); + min_norm = exp10((double) (dbscale->min - dbscale->max) / 20); + normalized = (normalized - min_norm) / (1 - min_norm); } - + break; - + default: - normalized=0; + normalized = 0; } - - return (int)round(normalized*100); + + return (int) round(normalized * 100); } // HAL normalise volume values to 0-100% -PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ) { + +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ) { enumRandeModeDB_T useNormalizeDB; int length; // If Integer look for DBscale if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - + // If not valid db_scale let's use raw_scale if (!halCtls->dbscale || (halCtls->dbscale->min >= halCtls->dbscale->max)) { - - // dbscale is invalid let's try raw range - if (halCtls->minval >= halCtls->maxval) goto ExitOnError; - - // Use Raw Scale Model - useNormalizeDB= NORMALIZE_LINEAR; - + + // dbscale is invalid let's try raw range + if (halCtls->minval >= halCtls->maxval) goto ExitOnError; + + // Use Raw Scale Model + useNormalizeDB = NORMALIZE_LINEAR; + } else { // db_scale looks OK let's use it - if ((halCtls->dbscale->max - halCtls->dbscale->min) <= MAX_LINEAR_DB_SCALE * 100) useNormalizeDB= NORMALIZE_DB_LINEAR; + if ((halCtls->dbscale->max - halCtls->dbscale->min) <= MAX_LINEAR_DB_SCALE * 100) useNormalizeDB = NORMALIZE_DB_LINEAR; else useNormalizeDB = NORMALIZE_LINEAR; // Fulup not sure how to handle this useNormalizeDB=NORMALIZE_DB_MATH; } - } else useNormalizeDB= NORMALIZE_NONE; - - + } else useNormalizeDB = NORMALIZE_NONE; + + // loop on values to normalise - enum json_type jtype=json_object_get_type(valuesJ); - if (jtype == json_type_array) length = json_object_array_length(valuesJ); + enum json_type jtype = json_object_get_type(valuesJ); + if (jtype == json_type_array) length = json_object_array_length(valuesJ); else length = 1; - - json_object *normalisedJ= json_object_new_array(); - for (int idx=0; idx < length; idx++) { + + json_object *normalisedJ = json_object_new_array(); + for (int idx = 0; idx < length; idx++) { int value; - + if (jtype == json_type_array) { - json_object *valueJ = json_object_array_get_idx (valuesJ, idx); + json_object *valueJ = json_object_array_get_idx(valuesJ, idx); value = json_object_get_int(valueJ); } else { value = json_object_get_int(valuesJ); } - + // If Integer scale to 0/100 if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - + switch (action) { - + case ACTION_GET: - switch (useNormalizeDB) { + switch (useNormalizeDB) { case NORMALIZE_LINEAR: - value = 100* (value - halCtls->minval) / (halCtls->maxval - halCtls->minval); + value = 100 * (value - halCtls->minval) / (halCtls->maxval - halCtls->minval); break; case NORMALIZE_DB_MATH: //ToBeDone - value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); + value = dbNormalizeVal(useNormalizeDB, halCtls->dbscale, value); break; - case NORMALIZE_NONE: + case NORMALIZE_NONE: default: value = value; } break; - - case ACTION_SET: - switch (useNormalizeDB) { + + case ACTION_SET: + switch (useNormalizeDB) { case NORMALIZE_LINEAR: - value = (value * (halCtls->maxval - halCtls->minval))/100; + value = (value * (halCtls->maxval - halCtls->minval)) / 100; break; case NORMALIZE_DB_MATH: //ToBeDone - value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); + value = dbNormalizeVal(useNormalizeDB, halCtls->dbscale, value); break; - case NORMALIZE_NONE: + case NORMALIZE_NONE: default: value = value; } break; - - default: - AFB_NOTICE ("volumeNormalise: invalid action value=%d", (int)action); - goto ExitOnError; + + default: + AFB_NOTICE("volumeNormalise: invalid action value=%d", (int) action); + goto ExitOnError; } - } - - json_object_array_add(normalisedJ, json_object_new_int(value)); + } + + json_object_array_add(normalisedJ, json_object_new_int(value)); } - + return (normalisedJ); - - ExitOnError: - return NULL; + +ExitOnError: + return NULL; } - \ No newline at end of file diff --git a/HAL-afb/HAL-plugin/CMakeLists.txt b/HAL-afb/HAL-plugin/CMakeLists.txt deleted file mode 100644 index 93642b2..0000000 --- a/HAL-afb/HAL-plugin/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Fulup Ar Foll -# -# 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. -########################################################################### - -# Activate ALSA dynamic build build mode get resolve "snd_dlsym_start" -add_compile_options(-DPIC) - -# Add target to project dependency list -PROJECT_TARGET_ADD(ctl_afbhal) - - # Define targets - ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCtl.c) - - # Alsa Plugin properties - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "libasound_module_" - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - ${link_libraries} - ) - install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib/alsa-lib) - -PROJECT_TARGET_ADD(cb_sample) - - # Define targets - ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCb.c) - - # Alsa Plugin properties - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afbhal_" - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - ${link_libraries} - ) - install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) diff --git a/HAL-afb/HAL-plugin/HalPlug.h b/HAL-afb/HAL-plugin/HalPlug.h deleted file mode 100644 index 5e3da6f..0000000 --- a/HAL-afb/HAL-plugin/HalPlug.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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. - * - * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); - * AfbHalInit is mandatory and called with numid=0 - * - * Syntaxe in .asoundrc file - * CrlLabel { cb MyFunctionName name "My_Second_Control" } - */ - - -#include -#include -#include - -#ifndef SOUND_HAL_MAX_CTLS -#define SOUND_HAL_MAX_CTLS 255 -#endif - - -typedef enum { - CTLCB_INIT , - CTLCB_CLOSE , - CTLCB_ELEM_COUNT , - CTLCB_ELEM_LIST , - CTLCB_FIND_ELEM , - CTLCB_FREE_KEY , - CTLCB_GET_ATTRIBUTE , - CTLCB_GET_INTEGER_INFO , - CTLCB_GET_INTEGER64_INFO , - CTLCB_GET_ENUMERATED_INFO , - CTLCB_GET_ENUMERATED_NAME , - CTLCB_READ_INTEGER , - CTLCB_READ_INTEGER64 , - CTLCB_READ_ENUMERATED , - CTLCB_READ_BYTES , - CTLCB_READ_IEC958 , - CTLCB_WRITE_INTEGER , - CTLCB_WRITE_INTEGER64 , - CTLCB_WRITE_ENUMERATED , - CTLCB_WRITE_BYTES , - CTLCB_WRITE_IEC958 , - CTLCB_SUBSCRIBE_EVENTS , - CTLCB_READ_EVENT , - CTLCB_POLL_DESCRIPTORS_COUNT , - CTLCB_POLL_DESCRIPTORS -} snd_ctl_action_t; - -typedef struct { - int ctlNumid; - const char *ctlName; -} snd_ctl_conf_t; - -typedef struct { - int type; - int acc; - unsigned count; -} snd_ctl_get_attrib_t; - -typedef struct { - int imin; - int imax; - int istep; -} snd_ctl_get_int_info_t; - -typedef int(*snd_ctl_cb_t)(void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void *response); - -typedef struct snd_ctl_hal { - snd_ctl_ext_t ext; - char *devid; - snd_ctl_t *ctlDev; - unsigned int ctlsCount; - void *dlHandle; - snd_ctl_conf_t ctls[SOUND_HAL_MAX_CTLS]; - snd_ctl_elem_info_t *infos[SOUND_HAL_MAX_CTLS]; - snd_ctl_cb_t cbs[SOUND_HAL_MAX_CTLS]; -} snd_ctl_hal_t; - - diff --git a/HAL-afb/HAL-plugin/HalPlugCb.c b/HAL-afb/HAL-plugin/HalPlugCb.c deleted file mode 100644 index 362858d..0000000 --- a/HAL-afb/HAL-plugin/HalPlugCb.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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. - * - * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); - * AfbHalInit is mandatory and called with numid=0 - * - * Syntax in .asoundrc file - * CrlLabel { cb MyFunctionName name "My_Second_Control" } - * - * Testing: - * amixer -Dagl_hal controls - * amixer -Dagl_hal cget name=My_Sample_Callback - */ - - -#include "HalPlug.h" -#include - -int AfbHalInitCB (void *handle, snd_ctl_action_t action, int numid, void*response) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*)handle; - - fprintf (stdout, "\n - CB AfbHalInit devid=%s\n", plughandle->devid); - return 0; -} - -int AfbHalSampleCB (void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void*response) { - - switch (action) { - case CTLCB_GET_ATTRIBUTE: { - fprintf (stdout, " - AfbHalSampleCB CTLCB_GET_ATTRIBUTE numid=%d\n", (int)key+1); - snd_ctl_get_attrib_t *item = (snd_ctl_get_attrib_t*)response; - item->type = 1; - item->count= 2; - item->acc = SND_CTL_EXT_ACCESS_READWRITE; - break; - } - - case CTLCB_GET_INTEGER_INFO: { - fprintf (stdout, " - AfbHalSampleCB CTLCB_GET_INTEGER_INFO numid=%d\n", (int)key+1); - snd_ctl_get_int_info_t *item = (snd_ctl_get_attrib_t*)response; - item->istep= 10; - item->imin = 20; - item->imax = 200; - break; - } - - default: - fprintf (stdout, "CB AfbHalSampleCB unsupported action=%d numid=%d\n", action, (int)key+1); - } - return 0; -} diff --git a/HAL-afb/HAL-plugin/HalPlugCtl.c b/HAL-afb/HAL-plugin/HalPlugCtl.c deleted file mode 100644 index 4f76595..0000000 --- a/HAL-afb/HAL-plugin/HalPlugCtl.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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. - * - * Testing: - * 1) Copy generated plugin [libasound_module_pcm_afbhal.so] in alsa-lib/ dir visible from LD_LIBRARY_PATH (eg: /usr/lib64/alsa-lib) - * 2) Create a ~/.asounrc file base on following template - * ctl.agl_hal { - * type afbhal - * devid "hw:4" - * cblib "afbhal_cb_sample.so" - * ctls { - * # ctlLabel {numid integer name "Alsa Ctl Name"} - * MasterSwitch { numid 4 name "My_First_Control" } - * MasterVol { numid 5 name "My_Second_Control" } - * CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} - * } - * pcm.agl_hal { - * type copy # Copy PCM - * slave "hw:4" # Slave name - * } - * - * } - * 3) Test with - * - amixer -Dagl_hal controls # should list all your controls - * - amixer -Dagl_hal cget numid=1 - * - amixer -Dagl_hal cset numid=1 '10,20' - */ - - -#include -#include -#include "HalPlug.h" -#include - - -static snd_ctl_ext_key_t AfbHalElemFind(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_ext_key_t key=NULL; - - int numid = snd_ctl_elem_id_get_numid(id); - if (numid > 0) { - if (numid > plughandle->ctlsCount) goto OnErrorExit; - key= (snd_ctl_ext_key_t) numid -1; - goto SucessExit; - } - - const char *ctlName= snd_ctl_elem_id_get_name(id); - if (ctlName == NULL) goto OnErrorExit; - - for (int idx=0; idx < plughandle->ctlsCount; idx++) { - if (! strcmp(ctlName, plughandle->ctls[idx].ctlName)) { - key = idx; - goto SucessExit; - } - } - - SucessExit: - return key; - - OnErrorExit: - return -1; -} - -static int AfbHalGetAttrib(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int *type, unsigned int *acc, unsigned int *count) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - snd_ctl_cb_t callback = plughandle->cbs[key]; - - - // search for equivalent NUMID in effective sound card - if (elemInfo) { - *type = snd_ctl_elem_info_get_type(elemInfo); - *count = snd_ctl_elem_info_get_count(elemInfo); - *acc = SND_CTL_EXT_ACCESS_READWRITE; // Future ToBeDone - return 0; - } - - if (callback) { - snd_ctl_get_attrib_t item; - - int err = callback(plughandle, CTLCB_GET_ATTRIBUTE, key, &item); - if (!err) { - *type = item.type; - *acc = item.acc; - *count= item.count; - } - return err; - } - - return -1; -} - -static int AfbHalGetIntInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *imin, long *imax, long *istep) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - snd_ctl_cb_t callback = plughandle->cbs[key]; - - if (elemInfo) { - - // Should be normalised to make everything 0-100% - *imin = (long)snd_ctl_elem_info_get_min(elemInfo); - *imax = (long)snd_ctl_elem_info_get_min(elemInfo); - *istep= (long)snd_ctl_elem_info_get_min(elemInfo); - return 0; - } - - if (callback) { - snd_ctl_get_int_info_t item; - - int err = callback(plughandle, CTLCB_GET_INTEGER_INFO, key, &item); - if (!err) { - *imin = item.imin; - *imax = item.imax; - *istep= item.istep; - } - return err; - } - - return -1; -} - -static int AfbHalGetEnumInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - snd_ctl_cb_t callback = plughandle->cbs[key]; - - if(elemInfo) *items= snd_ctl_elem_info_get_items(elemInfo); - if(callback) callback(plughandle, CTLCB_GET_ENUMERATED_INFO, key, items); - - return 0; -} - -static int AfbHalGetEnumName(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int item, char *name, size_t name_max_len) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - - //name= snd_ctl_elem_info_get_item_name(elemInfo); - return 0; -} - -static int AfbHalReadInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - - return 0; -} - -static int AfbHalReadEnumerate(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - - return 0; -} - -static int AfbHalWriteInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - - return 0; -} - -static int AfbHalWriteEnum(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - - return 0; -} - -static int AfbHalEventRead(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - - return -EAGAIN; -} - -static int AfbHalElemList(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - - snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, plughandle->ctls[offset].ctlName); - - return 0; -} - -static int AfbHalElemCount(snd_ctl_ext_t *ext) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - int count = plughandle->ctlsCount; - return count; -} - -static void AfbHalClose(snd_ctl_ext_t *ext) { - snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - int err; - - for (int idx=0; idx < plughandle->ctlsCount; idx++) { - if (plughandle->ctls[idx].ctlName) free((void*)plughandle->ctls[idx].ctlName); - } - - err = snd_ctl_close(plughandle->ctlDev); - if (err) SNDERR("Fail Close sndctl: devid=%s err=%s", plughandle->devid, snd_strerror(err)); - - if (plughandle->devid) free(plughandle->devid); - free(plughandle); -} - -static snd_ctl_ext_callback_t afbHalCBs = { - .close = AfbHalClose, - .elem_count = AfbHalElemCount, - .elem_list = AfbHalElemList, - .find_elem = AfbHalElemFind, - .get_attribute = AfbHalGetAttrib, - .get_integer_info = AfbHalGetIntInfo, - .get_enumerated_info = AfbHalGetEnumInfo, - .get_enumerated_name = AfbHalGetEnumName, - .read_integer = AfbHalReadInt, - .read_enumerated = AfbHalReadEnumerate, - .write_integer = AfbHalWriteInt, - .write_enumerated = AfbHalWriteEnum, - .read_event = AfbHalEventRead, -}; - -SND_CTL_PLUGIN_DEFINE_FUNC(afbhal) { - - snd_config_iterator_t it, next; - snd_ctl_hal_t *plughandle; - int err; - snd_ctl_cb_t AfbHalInitCB; - const char *libname; - - plughandle = calloc(1, sizeof(snd_ctl_hal_t)); - - snd_config_for_each(it, next, conf) { - snd_config_t *node = snd_config_iterator_entry(it); - const char *id; - - // ignore comment en empty lines - if (snd_config_get_id(node, &id) < 0) continue; - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) continue; - - // devid should point onto a valid sound card - if (strcmp(id, "devid") == 0) { - const char *devid; - if (snd_config_get_string(node, &devid) < 0) { - SNDERR("Invalid string type for %s", id); - return -EINVAL; - } - plughandle->devid=strdup(devid); - - // open control interface for devid - err = snd_ctl_open(&plughandle->ctlDev, plughandle->devid, 0); - if (err < 0) { - SNDERR("Fail to open control device for devid=%s", plughandle->devid); - return -EINVAL; - } - continue; - } - - if (strcmp(id, "cblib") == 0) { - if (snd_config_get_string(node, &libname) < 0) { - SNDERR("Invalid libname string for %s", id); - return -EINVAL; - } - - plughandle->dlHandle= dlopen(libname, RTLD_NOW); - if (!plughandle->dlHandle) { - SNDERR("Fail to open callback sharelib=%s error=%s", libname, dlerror()); - return -EINVAL; - } - - AfbHalInitCB = dlsym(plughandle->dlHandle, "AfbHalInitCB"); - if (!AfbHalInitCB) { - SNDERR("Fail find 'AfbHalInitCB' symbol into callbacks sharelib=%s", libname); - return -EINVAL; - } - - err = (*AfbHalInitCB)(plughandle,CTLCB_INIT, 0,0); - if (err) { - SNDERR("Fail AfbHalInitCB err=%d", err); - return -EINVAL; - } - - continue; - } - - if (strcmp(id, "ctl") == 0) { - const char *ctlConf; - snd_config_type_t ctype; - snd_config_iterator_t currentCtl, follow; - snd_config_t *itemConf; - - ctype = snd_config_get_type (node); - if (ctype != SND_CONFIG_TYPE_COMPOUND) { - snd_config_get_string (node, &ctlConf); - SNDERR("Invalid compound type for %s", node); - return -EINVAL; - } - - // loop on each ctl within ctls - snd_config_for_each (currentCtl, follow, node) { - snd_config_t *ctlconfig = snd_config_iterator_entry(currentCtl); - snd_ctl_elem_info_t *elemInfo; - const char *ctlLabel, *ctlName; - - // ignore empty line - if (snd_config_get_id(ctlconfig, &ctlLabel) < 0) continue; - - // each clt should be a valid config compound - ctype = snd_config_get_type (ctlconfig); - if (ctype != SND_CONFIG_TYPE_COMPOUND) { - snd_config_get_string (node, &ctlConf); - SNDERR("Invalid ctl config for %s", ctlLabel); - return -EINVAL; - } - - err=snd_config_search(ctlconfig, "ctl", &itemConf); - if (!err) { - if (snd_config_get_integer(itemConf, (long*)&plughandle->ctls[plughandle->ctlsCount].ctlNumid) < 0) { - SNDERR("Not Integer: ctl:%s numid should be a valid integer", ctlLabel); - return -EINVAL; - } - - // Make sure than numid is valid on slave snd card - snd_ctl_elem_info_malloc(&elemInfo); - snd_ctl_elem_info_set_numid(elemInfo, (int)plughandle->ctls[plughandle->ctlsCount].ctlNumid); - plughandle->infos[plughandle->ctlsCount]= elemInfo; - - err = snd_ctl_elem_info(plughandle->ctlDev, elemInfo); - if (err) { - SNDERR("Not Found: 'numid=%d' for 'devid=%s'", plughandle->ctls[plughandle->ctlsCount].ctlNumid, plughandle->devid); - return -EINVAL; - } - } - - err=snd_config_search(ctlconfig, "ctlcb", &itemConf); - if (!err) { - const char *funcname; - void *funcaddr; - - if (snd_config_get_string(itemConf, &funcname) < 0) { - SNDERR("Not string: ctl:%s cbname should be a valid string", ctlLabel); - return -EINVAL; - } - - if (funcname[0] != '@') { - SNDERR("Not string: ctl:%s cbname=%s should be prefixed with '@' ", ctlLabel, funcname); - return -EINVAL; - } - - if (!plughandle->dlHandle) { - SNDERR("No CB: ctl:%s cblib:/my/libcallback missing from asoundrc", ctlLabel); - return -EINVAL; - } - - funcaddr = dlsym(plughandle->dlHandle, &funcname[1]); - if (!funcaddr) { - SNDERR("NotFound CB: ctl:%s cbname='%s' no symbol into %s", ctlLabel, &funcname[1], libname); - return -EINVAL; - } - plughandle->cbs[plughandle->ctlsCount]=funcaddr; - } - - err=snd_config_search(ctlconfig, "name", &itemConf); - if (err) { - SNDERR("Not Found: 'name' mandatory in ctl config"); - return -EINVAL; - } - - if (snd_config_get_string(itemConf, &ctlName) < 0) { - SNDERR("Not String: ctl:%s 'name' should be a valie string", ctlLabel); - return -EINVAL; - } - plughandle->ctls[plughandle->ctlsCount].ctlName = strdup(ctlName); - - // move to next ctl if any - plughandle->ctlsCount++; - } // end for each ctl - continue; - } - SNDERR("Unknown field %s", id); - return -EINVAL; - } - - - - // Create ALSA control plugin structure - plughandle->ext.version = SND_CTL_EXT_VERSION; - plughandle->ext.card_idx = 0; /* FIXME */ - strcpy(plughandle->ext.id , "AFB-HAL-CTL"); - strcpy(plughandle->ext.driver , "AFB-HAL"); - strcpy(plughandle->ext.name , "AFB-HAL Control Plugin"); - strcpy(plughandle->ext.mixername, "AFB-HAL Mixer Plugin"); - strcpy(plughandle->ext.longname , "Automotive-Linux Sound Abstraction Control Plugin"); - plughandle->ext.poll_fd = -1; - plughandle->ext.callback = &afbHalCBs; - plughandle->ext.private_data = (void*)plughandle; - - - - err = snd_ctl_ext_create(&plughandle->ext, name, mode); - if (err < 0) { - SNDERR("Fail Register sndctl for devid=%s", plughandle->devid); - goto OnErrorExit; - } - - // Plugin register controls update handlep before exiting - *handlep = plughandle->ext.handle; - return 0; - -OnErrorExit: - free(plughandle); - return -1; -} - -SND_CTL_PLUGIN_SYMBOL(afbhal); diff --git a/HAL-afb/HAL-plugin/README.md b/HAL-afb/HAL-plugin/README.md deleted file mode 100644 index 1c1e7a2..0000000 --- a/HAL-afb/HAL-plugin/README.md +++ /dev/null @@ -1,44 +0,0 @@ -Hal-Plugin - -Object: provide an ALSA a HAL (Hardware Abstraction Layer) to automotive sound cards -Status: Current version is proof of concept and not a usable product -Author: Fulup Ar Foll -Date : June-2017 - -HAL-plugin allows: - - to expose any existing NUMID under a customized label name, this is order to abstract sound card config. - - to add non alsa NUMID supported through a callback mechanism (eg: volume rampdown, power off, ...) - -installation - - Plugin should be placed where ever alsaplugins are located (typically: /usr/share/alsa-lib) - - Callback sharelib directory should be declare in $LD_LIBRARY_PATH - -Config -``` -cat ~/.asoundrc - ctl.agl_hal { - type afbhal - devid "hw:4" - cblib "afbhal_cb_sample.so" - ctls { - # ctlLabel {numid integer name "Alsa Ctl Name"} - MasterSwitch { numid 4 name "My_First_Control" } - MasterVol { numid 5 name "My_Second_Control" } - CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} - } - pcm.agl_hal { - type copy # Copy PCM - slave "hw:4" # Slave name - } -``` - -With such a config file - - numid=4 from sndcard=hw:4 is renamed "My_First_Control" - - numid=4 from sndcard=hw:4 is renamed "My_Second_Control" - - numid=4 will call AfbHalSampleCB from afbhal_cb_sample.so - -Note: to really implement a usable HAL at minimum every ALSA call should be implemented and read/write of values should be normalised from 0 to 100% with a step of 1. - -Conclusion: This demonstrate that implementing a HAL that both abstract ALSA get/set and enable non ALSA support is possible at an acceptable cost -without having to hack ALSA source code. The beauty of the model is than it is fully transparent to any ALSA application. The limit is that the plugin is loaded -within every application context, thus interaction with an external event loop remains complete as well as conflict management in case of share resources. diff --git a/HAL-afb/HDA-intel/IntelHdaHAL.c b/HAL-afb/HDA-intel/IntelHdaHAL.c index f7aec59..f5c260e 100644 --- a/HAL-afb/HDA-intel/IntelHdaHAL.c +++ b/HAL-afb/HDA-intel/IntelHdaHAL.c @@ -27,48 +27,52 @@ // Default Values for MasterVolume Ramping -STATIC halVolRampT volRampMaster= { - .mode = RAMP_VOL_NORMAL, - .slave = Master_Playback_Volume, - .delay = 100*1000, // ramping delay in us - .stepDown=1, - .stepUp =1, +STATIC halVolRampT volRampMaster = { + .mode = RAMP_VOL_NORMAL, + .slave = Master_Playback_Volume, + .delay = 100 * 1000, // ramping delay in us + .stepDown = 1, + .stepUp = 1, }; // Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. -STATIC alsaHalMapT alsaHalMap[]= { - { .tag=Master_Playback_Volume, . ctl={ .name="Master Playback Volume" } }, - { .tag=PCM_Playback_Volume , .ctl={ .name="PCM Playback Volume" } }, - { .tag=PCM_Playback_Switch , .ctl={ .name="Master Playback Switch" } }, - { .tag=Capture_Volume , .ctl={ .name="Capture Volume" } }, +STATIC alsaHalMapT alsaHalMap[] = { + { .tag = Master_Playback_Volume, . ctl = + { .name = "Master Playback Volume"}}, + { .tag = PCM_Playback_Volume, .ctl = + { .name = "PCM Playback Volume"}}, + { .tag = PCM_Playback_Switch, .ctl = + { .name = "Master Playback Switch"}}, + { .tag = Capture_Volume, .ctl = + { .name = "Capture Volume"}}, - { .tag=Vol_Ramp , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", - .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Hal-VolRamp"} - }, - - { .tag=EndHalCrlTag} /* marker for end of the array */ -} ; + { .tag = Master_Playback_Volume, .cb = + {.callback = volumeRamp, .handle = &volRampMaster}, .info = "ramp volume linearly according to current ramp setting", + .ctl = + {.numid = 0, .type = SND_CTL_ELEM_TYPE_INTEGER, .count = 1, .minval = 0, .maxval = 100, .step = 1, .name = "Hal-VolRamp"}}, -// HAL sound card mapping info -STATIC alsaHalSndCardT alsaHalSndCard = { - .name = "HDA Intel PCH", // WARNING: name MUST match with 'aplay -l' - .info = "Hardware Abstraction Layer for IntelHDA sound card", - .ctls = alsaHalMap, + { .tag = EndHalCrlTag} /* marker for end of the array */ }; +// HAL sound card mapping info +STATIC alsaHalSndCardT alsaHalSndCard = { + .name = "HDA Intel PCH", // WARNING: name MUST match with 'aplay -l' + .info = "Hardware Abstraction Layer for IntelHDA sound card", + .ctls = alsaHalMap, +}; -STATIC int sndServiceInit () { +STATIC int sndServiceInit() { int err; - AFB_DEBUG ("IntelHalBinding Init"); + AFB_DEBUG("IntelHal Binding Init"); - err = halServiceInit (afbBindingV2.api, &alsaHalSndCard); + err = halServiceInit(afbBindingV2.api, &alsaHalSndCard); return err; } // API prefix should be unique for each snd card PUBLIC const struct afb_binding_v2 afbBindingV2 = { - .api = "intel-hda", - .init = sndServiceInit, - .verbs = halServiceApi, + .api = "intel-hda", + .init = sndServiceInit, + .verbs = halServiceApi, .onevent = halServiceEvent, }; diff --git a/HAL-afb/Jabra-Solemate/CMakeLists.txt b/HAL-afb/Jabra-Solemate/CMakeLists.txt new file mode 100644 index 0000000..9504175 --- /dev/null +++ b/HAL-afb/Jabra-Solemate/CMakeLists.txt @@ -0,0 +1,42 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# 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. +########################################################################### + + +# Add target to project dependency list +PROJECT_TARGET_ADD(hal-jabra-usb) + + # Define project Targets + ADD_LIBRARY(${TARGET_NAME} MODULE JabraUsbHAL.c) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + hal-interface + audio-interface + ) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) \ No newline at end of file diff --git a/HAL-afb/Jabra-Solemate/JabraUsbHAL.c b/HAL-afb/Jabra-Solemate/JabraUsbHAL.c new file mode 100644 index 0000000..f3753ec --- /dev/null +++ b/HAL-afb/Jabra-Solemate/JabraUsbHAL.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * + * To find out which control your sound card uses + * aplay -l # Check sndcard name name in between [] + * amixer -D hw:USB controls # get supported controls + * amixer -Dhw:USB cget name=Power-Switch + * amixer -Dhw:USB cset name=Power-Switch true + * + */ +#define _GNU_SOURCE +#include "hal-interface.h" +#include "audio-interface.h" + +// Define few private tag for not standard functions +#define PCM_Volume_Multimedia 1000 +#define PCM_Volume_Navigation 1001 +#define PCM_Volume_Emergency 1002 + +// Default Values for MasterVolume Ramping +STATIC halVolRampT volRampMaster= { + .mode = RAMP_VOL_NORMAL, + .slave = Master_Playback_Volume, + .delay = 100*1000, // ramping delay in us + .stepDown=1, + .stepUp =1, +}; + +STATIC halVolRampT volRampMultimedia= { + .slave = PCM_Volume_Multimedia, + .delay = 100*1000, // ramping delay in us + .stepDown= 2, + .stepUp = 1, +}; + +STATIC halVolRampT volRampNavigation= { + .slave = PCM_Volume_Navigation, + .delay = 100*1000, // ramping delay in us + .stepDown= 4, + .stepUp = 2, +}; +// Default Values for MasterVolume Ramping +STATIC halVolRampT volRampEmergency= { + .slave = PCM_Volume_Emergency, + .delay = 50*1000, // ramping delay in us + .stepDown= 6, + .stepUp = 3, +}; + +// Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. +STATIC alsaHalMapT alsaHalMap[]= { + { .tag=Master_Playback_Volume, . ctl={.name="PCM Playback Volume" } }, + { .tag=PCM_Playback_Volume , .ctl={.name="PCM Playback Volume" } }, + { .tag=PCM_Playback_Switch , .ctl={.name="PCM Playback Switch" } }, + { .tag=Capture_Volume , .ctl={.name="Mic Capture Volume" } }, + + // Sound card does not have hardware volume ramping + { .tag=Master_Playback_Ramp , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Rampup Master"} + }, + + // Implement Rampup Volume for Virtual Channels + { .tag=Multimedia_Playback_Volume, .cb={.callback=volumeRamp, .handle=&volRampMultimedia}, .info="Rampup Multimedia Volume", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .name="Playback Multimedia Ramp"} + }, + { .tag=Navigation_Playback_Volume, .cb={.callback=volumeRamp, .handle=&volRampNavigation}, .info="RampUp Navigation Volume", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Playback Navigation Ramp"} + }, + { .tag=Emergency_Playback_Volume, .cb={.callback=volumeRamp, .handle=&volRampEmergency}, .info="Rampup Emergency Volume", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Playback Emergency Ramp"} + }, + + // Sound Card does not support hardware channel volume mixer (note softvol default range 0-256) + { .tag=PCM_Volume_Multimedia, .info="Playback Multimedia Softvol", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .maxval=255, .value=200, .name="Playback Multimedia"} + }, + { .tag=PCM_Volume_Navigation, .info="Playback Navigation Softvol", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .maxval=255,.value=200, .name="Playback Navigation"} + }, + { .tag=PCM_Volume_Emergency, .info="Playback Emergency Softvol", + .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .maxval=255, .value=200, .name="Playback Emergency"} + }, + + + { .tag=EndHalCrlTag} /* marker for end of the array */ +} ; + +// HAL sound card mapping info +STATIC alsaHalSndCardT alsaHalSndCard = { + .name = "Jabra SOLEMATE v1.34.0", // WARNING: name MUST match with 'aplay -l' + .info = "Hardware Abstraction Layer for Jabra Solamte USB speakers", + .ctls = alsaHalMap, + .volumeCB = NULL, // use default volume normalisation function +}; + + +STATIC int sndServiceInit () { + int err; + err = halServiceInit (afbBindingV2.api, &alsaHalSndCard); + return err; +} + +// API prefix should be unique for each snd card +PUBLIC const struct afb_binding_v2 afbBindingV2 = { + .api = "jabra-usb", + .init = sndServiceInit, + .verbs = halServiceApi, + .onevent = halServiceEvent, +}; diff --git a/HAL-afb/Scarlett-Focusrite/CMakeLists.txt b/HAL-afb/Scarlett-Focusrite/CMakeLists.txt index 4cbac19..93752a7 100644 --- a/HAL-afb/Scarlett-Focusrite/CMakeLists.txt +++ b/HAL-afb/Scarlett-Focusrite/CMakeLists.txt @@ -21,7 +21,7 @@ PROJECT_TARGET_ADD(hal-scalett-usb) # Define project Targets - ADD_LIBRARY(hal-scalett-usb MODULE ScarlettUsbHAL.c) + ADD_LIBRARY(${TARGET_NAME} MODULE ScarlettUsbHAL.c) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES diff --git a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c index dd4499e..9b31c96 100644 --- a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c +++ b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c @@ -57,7 +57,7 @@ STATIC alsaHalMapT alsaHalMap[]= { { .tag=Vol_Ramp_Set_Up , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="set linear step up ramp [default 10]", .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .value=10, .name="Hal-VolRamp-Step-Up"} }, - { .tag=Vol_Ramp , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", + { .tag=Master_Playback_Volume , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Hal-VolRamp"} }, @@ -75,7 +75,7 @@ STATIC alsaHalSndCardT alsaHalSndCard = { STATIC int sndServiceInit () { int err; - AFB_DEBUG ("IntelHalBinding Init"); + AFB_DEBUG ("Scarlett Binding Init"); err = halServiceInit (afbBindingV2.api, &alsaHalSndCard); return err; diff --git a/HAL-afb/Unicens-USB/CMakeLists.txt b/HAL-afb/Unicens-USB/CMakeLists.txt deleted file mode 100644 index 5ca563b..0000000 --- a/HAL-afb/Unicens-USB/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Fulup Ar Foll -# -# 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. -########################################################################### - - -# Add target to project dependency list -PROJECT_TARGET_ADD(hal-unicens-usb) - - # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE UnicensHAL.c UnicensVol.c) - - # Binder exposes a unique public entry point - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afb-" - LABELS "BINDING" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - hal-interface - audio-interface - ) - - # installation directory - INSTALL(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) \ No newline at end of file diff --git a/HAL-afb/Unicens-USB/UnicensHAL.c b/HAL-afb/Unicens-USB/UnicensHAL.c deleted file mode 100644 index 9a42afd..0000000 --- a/HAL-afb/Unicens-USB/UnicensHAL.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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. - * - * - * To find out which control your sound card uses - * aplay -l # Check sndcard name name in between [] - * amixer -D hw:xx controls # get supported controls - * amixer -D "hw:3" cget numid=xx # get control settings - * - */ -#define _GNU_SOURCE -#include "hal-interface.h" -#include "audio-interface.h" - - - -// Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. -STATIC alsaHalMapT alsaHalMap[]= { - { .tag=Master_Playback_Volume, .ctl={.numid=04 } }, - { .tag=PCM_Playback_Volume , .ctl={.numid=06 } }, - { .tag=PCM_Playback_Switch , .ctl={.numid=05 } }, - { .tag=Capture_Volume , .ctl={.numid=12 } }, - - { .tag=EndHalCrlTag} /* marker for end of the array */ -} ; - -// HAL sound card mapping info -STATIC alsaHalSndCardT alsaHalSndCard = { - .name = "HDA Intel PCH", // WARNING: name MUST match with 'aplay -l' - .info = "Hardware Abstraction Layer for IntelHDA sound card", - .ctls = alsaHalMap, -}; - - -STATIC int sndServiceInit () { - int err; - AFB_DEBUG ("IntelHalBinding Init"); - - err = halServiceInit (afbBindingV2.api, &alsaHalSndCard); - return err; -} - -// API prefix should be unique for each snd card -PUBLIC const struct afb_binding_v2 afbBindingV2 = { - .api = "unicens-usb", - .init = sndServiceInit, - .verbs = halServiceApi, - .onevent = halServiceEvent, -}; diff --git a/HAL-afb/Unicens-USB/UnicensVol.c b/HAL-afb/Unicens-USB/UnicensVol.c deleted file mode 100644 index cf4f497..0000000 --- a/HAL-afb/Unicens-USB/UnicensVol.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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. - * - * - * To find out which control your sound card uses - * aplay -l # Check sndcard name name in between [] - * amixer -D hw:xx controls # get supported controls - * amixer -D "hw:3" cget numid=xx # get control settings - * - */ -#define _GNU_SOURCE -#include "hal-interface.h" -#include "audio-interface.h" - -STATIC json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, json_object *valJ) { - json_object *reponseJ; - - AFB_INFO ("Power Set value=%s", json_object_get_string(valJ)); - - reponseJ=json_object_new_object(); - json_object_object_add (reponseJ, "Callback", json_object_new_string("Hello From HAL")); - - return reponseJ; -} diff --git a/README.md b/README.md index a746cbf..4e449cb 100644 --- a/README.md +++ b/README.md @@ -78,14 +78,18 @@ AFB_daemon dependency on Standard Linux Distributions cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX .. make - afb-daemon --workdir=.. --ldpaths=build --port=1234 --roothttp=./htdocs --token="" --verbose + afb-daemon --workdir=. --ldpaths=. --port=1234 --roothttp=../htdocs --token="" --verbose + + Warning: See below net on GDB requiring (--workdir=.) ``` -# Local Source Debug with GDB +# Note: GDB Source Debug limits/features Warning: technically AGL bindings are shared libraries loaded thought 'dlopen'. GDB supports source debug of dynamically loaded libraries, but user should be warn that the actual path to sharelib symbols is directly inherited from DLOPEN. -As a result if you change your directory after binder start with --workdir=xxx then GDB will not find symbols anymore +As a result if you change your directory after binder start with --workdir=xxx then GDB will stop working. + +Conclusion: double-check that --workdir=. and run debug session from ./build directory. Any IDEs like Netbeans or VisualCode should work out of the box. ``` diff --git a/Shared-Interface/audio-interface.c b/Shared-Interface/audio-interface.c index b4750ae..3386460 100644 --- a/Shared-Interface/audio-interface.c +++ b/Shared-Interface/audio-interface.c @@ -16,13 +16,42 @@ * */ #define _GNU_SOURCE // needed for vasprintf +#include "audio-interface.h" -#include -#include -#include +PUBLIC const char *halCtlsLabels[] = { + + [Master_Playback_Volume] = "Master_Playback_Volume", + [Master_OnOff_Switch] = "Master_OnOff_Switch", + [Master_Playback_Ramp]= "Master_Playback_Ramp", + [PCM_Playback_Volume] = "PCM_Playback_Volume", + [PCM_Playback_Switch] = "PCM_Playback_Switch", + [Capture_Volume] = "Capture_Volume", -#include "audio-interface.h" + [Vol_Ramp_Set_Mode] = "Volume_Ramp_Mode", + [Vol_Ramp_Set_Delay] = "Volume_Ramp_Delay", + [Vol_Ramp_Set_Down] = "Volume_Ramp_Down", + [Vol_Ramp_Set_Up] = "Volume_Ramp_Up", + [Vol_Ramp_Set_Slave] = "Volume_Ramp_Slave", + + // Application Virtual Channels + [Multimedia_Playback_Volume] = "Multimedia_Playback_Volume", + [Navigation_Playback_Volume] = "Navigation_Playback_Volume", + [Emergency_Playback_Volume] = "Emergency_Playback_Volume", + + // Do not remove EndHalCrlTag + [EndHalCrlTag] = NULL +}; +PUBLIC const char *halVolRampModes[] = { + + [RAMP_VOL_NONE] = "None", + [RAMP_VOL_NORMAL] = "Normal", + [RAMP_VOL_SMOOTH] = "Smooth", + [RAMP_VOL_EMERGENCY] = "Emergency", + + [EndHalVolMod] = NULL, + +}; PUBLIC void pingtest(struct afb_req request) { json_object *query = afb_req_json(request); diff --git a/Shared-Interface/audio-interface.h b/Shared-Interface/audio-interface.h index 453a9c4..7bf258a 100644 --- a/Shared-Interface/audio-interface.h +++ b/Shared-Interface/audio-interface.h @@ -35,6 +35,9 @@ #endif #define STATIC static +// Soft control have dynamically allocated numid +#define CTL_AUTO -1 + typedef enum { QUERY_QUIET =0, QUERY_COMPACT =1, @@ -63,7 +66,6 @@ typedef enum { StartHalCrlTag=0, // volume RAMP - Vol_Ramp, Vol_Ramp_Set_Mode, Vol_Ramp_Set_Delay, Vol_Ramp_Set_Down, @@ -77,9 +79,15 @@ typedef enum { PCM_Playback_Switch, Capture_Volume, Master_OnOff_Switch, + + // Application Virtual Channels + Multimedia_Playback_Volume, + Navigation_Playback_Volume, + Emergency_Playback_Volume, EndHalCrlTag // used to compute number of ctls -} halCtlsEnumT; +} halCtlsTagT; + typedef enum { RAMP_VOL_NONE = 0, @@ -92,6 +100,8 @@ typedef enum { } halRampEnumT; PUBLIC void pingtest(struct afb_req request); +extern const char *halVolRampModes[]; +extern const char *halCtlsLabels[]; #endif /* AUDIO_INTERF_H */ diff --git a/conf.d/alsa/asoundrc.sample b/conf.d/alsa/asoundrc.sample index bd5b3b7..2611478 100644 --- a/conf.d/alsa/asoundrc.sample +++ b/conf.d/alsa/asoundrc.sample @@ -1,9 +1,30 @@ -# Dmixer mixe multiple audio channel into one -pcm.SoftMixer { +# +# Author: Fulup Ar Foll +# Object: PCM hook type +# +# Test : Note: Jabra_USB=hw:v1340 +# Check SoundCard speaker-test -Dhw:v1340 -c2 -twav +# Check MixerPCM speaker-test -DMyMixerPCM -c2 -twav +# Check HookPCM speaker-test -DMyNavigationHook -c2 -twav +# Check NavPCM speaker-test -DMyNavPCM -c2 -twav +# MultiMedia aplay -DDMyNavPCM /usr/share/sounds/alsa/test.wav +# +# Bug/Feature: when softvol control is initialised from plugin and not +# from AGL binding. At 1st run ctl has invalid TLV and cannot be +# use. Bypass Solution: +# * start audio-binder before playing sound (binding create control before softvol plugin) +# * run a dummy aplay -DMyNavPCM "" to get a clean control +# +# References: https://www.spinics.net/lists/alsa-devel/msg54235.html +# -------------------------------------------------------------------- + +# Mixer PCM allow to play multiple stream simultaneously +# ------------------------------------------------------ +pcm.MyMixerPCM { type dmix ipc_key 1024 ipc_key_add_uid false - ipc_perm 0666 # mixing for all users + ipc_perm 0666 # mixing for all users # Define target effective sound card (cannot be a plugin) slave { @@ -21,52 +42,101 @@ pcm.SoftMixer { } } -pcm.music { - type softvol - slave.pcm "SoftMixer" - control { - name "MasterMusic" - card 0 +# Define a Hook_type with a private sharelib +# ------------------------------------------- +pcm_hook_type.MyHookPlugin { + install "AlsaInstallHook" + lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Hook-Callback/alsa_hook_cb.so" +} + +# Define a HookedPCM that point to Hook_type sharelib +# ---------------------------------------------------- +pcm.MyNavigationHook { + type hooks + slave.pcm "MyMixerPCM" + + # Defined used hook sharelib and provide arguments/config to install func + hooks.0 { + type "MyHookPlugin" + hook_args { + + # Every Call should return OK in order PCM to open + uri "ws://localhost:1234/api?token='audio-agent-token'" + request { + CallPing { + api "alsacore" + verb "ping" + } + CallUCM { + api "alsacore" + verb "ping" + query "{'devid':'hw:v1340','verb':'Navigation','dev':'speakers'}" + } + } + } } +} + +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyMultimediaPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" # Make this plugin visible from aplay -L hint { show on - description "Music" + description "Navigation SolftVol PCM" } } -pcm.navi { - type softvol - slave { - pcm "SoftMixer" - } - control { - name "MasterNavi" - card 0 - } +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyNavPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + + # Make this plugin visible from aplay -L hint { show on - description "Navi" - } + description "Navigation SolftVol PCM" + } } -pcm.notif { - type softvol - slave { - pcm "SoftMixer" - } - control { - name "MasterNotif" - card 0 - } +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyAlarmPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + + # Make this plugin visible from aplay -L hint { show on - description "Notif" - } + description "Navigation SolftVol PCM" + } } -pcm.!default { - type plug - slave.pcm "SoftMixer" #make use of Softvol -} diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index 116ed2a..1a53d8a 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -61,6 +61,7 @@ set (PKG_REQUIRED_LIST libmicrohttpd>=0.9.55 afb-daemon json-c + libafbwsc ) # LANG Specific compile flags set for all build types diff --git a/htdocs/docs/AAAA-architecture.odg b/htdocs/docs/AAAA-architecture.odg new file mode 100644 index 0000000..1ebdb1c Binary files /dev/null and b/htdocs/docs/AAAA-architecture.odg differ diff --git a/htdocs/docs/AAAA-architecture.pdf b/htdocs/docs/AAAA-architecture.pdf new file mode 100644 index 0000000..a9d0605 Binary files /dev/null and b/htdocs/docs/AAAA-architecture.pdf differ diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index afcd6dc..186abf5 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -9,6 +9,32 @@ Alsa-SetGet.c Alsa-Ucm.c + + + AlsaHalPlugCb.c + AlsaHalPlugCtl.c + + + AlsaHookPlug.c + + + AlsaHookCb.c + + + + + + + CMakeCCompilerId.c + + + CMakeCXXCompilerId.cpp + + + feature_tests.c + feature_tests.cxx + + AudioCommonLib.c @@ -19,13 +45,14 @@ hal-volume.c - HalPlugCb.c - HalPlugCtl.c HalPlugPcm.c IntelHdaHAL.c + + JabraUsbHAL.c + ScarlettUsbHAL.c @@ -96,6 +123,8 @@ ${MAKE} -f Makefile install ${MAKE} -f Makefile clean build/CMakeFiles/feature_tests.bin + + build @@ -106,6 +135,7 @@ + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -123,6 +153,7 @@ + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -133,6 +164,7 @@ + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -143,12 +175,21 @@ + ../../../opt/include/alsa /usr/include/json-c Shared-Interface + ../../../opt/include build/ALSA-afb + + + + @@ -183,41 +224,21 @@ - - - - HAL-afb/HAL-plugin - build/HAL-afb/HAL-plugin - - - - - - - HAL-afb/HAL-plugin - build/HAL-afb/HAL-plugin - - - - - ../../../opt/include/afb - HAL-afb/HAL-plugin - ../../../opt/include/alsa - build/HAL-afb/HAL-plugin - - - CONTROL_CDEV_RX="/dev/inic-usb-crx" - CONTROL_CDEV_TX="/dev/inic-usb-ctx" - MAX_SND_CARD=16 - + + + + + + + + ../../../opt/include/afb + Alsa-Plugin/Alsa-Hook-Callback + ../../../opt/include/alsa + /usr/include/json-c + ../../../opt/include + build/Alsa-Plugin/Alsa-Hook-Callback + + + CONTROL_CDEV_RX="/dev/inic-usb-crx" + CONTROL_CDEV_TX="/dev/inic-usb-ctx" + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + PIC + TLV_BYTE_SIZE=256 + alsa_hook_cb_EXPORTS + + + + build/HAL-afb/HAL-plugin /usr/include/alsa ../../../opt/include /usr/include/json-c @@ -326,6 +369,7 @@ + build/HAL-afb/HAL-plugin ../../../opt/include/afb @@ -339,14 +383,24 @@ HAL-afb/HAL-interface + ../../../opt/include/alsa /usr/include/json-c + + + + HAL-afb/HAL-plugin + ../../../opt/include/alsa + + + HAL-afb/HDA-intel + ../../../opt/include/alsa /usr/include/json-c Shared-Interface HAL-afb/HAL-interface @@ -355,10 +409,24 @@ + + + + HAL-afb/Jabra-Solemate + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/Jabra-Solemate + + + HAL-afb/Scarlett-Focusrite + ../../../opt/include/alsa /usr/include/json-c Shared-Interface HAL-afb/HAL-interface @@ -378,11 +446,19 @@ + build/HAL-afb/HAL-plugin ../../../opt/include/afb HighLevel-afb + + + + build/HAL-afb/HAL-plugin + + + @@ -402,10 +478,9 @@ - - - - + + @@ -416,7 +491,9 @@ build ${MAKE} -f Makefile remote-target-populate ${MAKE} -f Makefile clean - build/CMakeFiles/feature_tests.bin + + + build @@ -425,8 +502,9 @@ - + + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -435,15 +513,16 @@ - + build/ALSA-afb - + + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -452,8 +531,9 @@ - + + ../../../opt/include/alsa /usr/include/json-c Shared-Interface ../../../opt/include @@ -462,14 +542,23 @@ - + + ../../../opt/include/alsa /usr/include/json-c Shared-Interface + ../../../opt/include build/ALSA-afb + + + + @@ -478,11 +567,8 @@ ex="false" tool="0" flavor2="3"> - + - ../../../opt/include/afb - HAL-afb/HAL-interface - /usr/include/json-c Shared-Interface ../../../opt/include build/HAL-afb/HAL-interface @@ -493,67 +579,44 @@ ex="false" tool="0" flavor2="3"> - + - ../../../opt/include - ../../../opt/include/alsa - /usr/include/p11-kit-1 - /usr/include/json-c - HAL-afb/HAL-interface Shared-Interface build/HAL-afb/HAL-interface - - MAX_LINEAR_DB_SCALE=24 - TLV_BYTE_SIZE=256 - - + - ../../../opt/include/afb - HAL-afb/HAL-interface - /usr/include/json-c build/HAL-afb/HAL-interface - - - - build/HAL-afb/HAL-plugin - - - - - - - build/HAL-afb/HAL-plugin - - - - - - ../../../opt/include/alsa - build/HAL-afb/HAL-plugin - + - + + + + + - + - + Shared-Interface HAL-afb/HAL-interface @@ -563,7 +626,7 @@ - + HAL-afb/HAL-interface build/HAL-afb/Unicens-USB @@ -571,7 +634,7 @@ - + Shared-Interface ../../../opt/include @@ -580,7 +643,7 @@ - + /usr/include/json-c Shared-Interface @@ -624,7 +687,7 @@ - + @@ -635,9 +698,22 @@ + + + + ../../../opt/include/afb + Alsa-Plugin/Alsa-Hook-Callback + ../../../opt/include/alsa + /usr/include/json-c + ../../../opt/include + build/Alsa-Plugin/Alsa-Hook-Callback + + + + build/HAL-afb/HAL-plugin /usr/include/alsa ../../../opt/include /usr/include/json-c @@ -662,10 +738,21 @@ + + + + HAL-afb/HAL-interface + ../../../opt/include/alsa + /usr/include/json-c + + + + build/HAL-afb/HAL-plugin HAL-afb/HAL-plugin + ../../../opt/include/alsa @@ -673,6 +760,7 @@ HAL-afb/HDA-intel + ../../../opt/include/alsa /usr/include/json-c Shared-Interface HAL-afb/HAL-interface @@ -681,10 +769,24 @@ + + + + HAL-afb/Jabra-Solemate + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/Jabra-Solemate + + + HAL-afb/Scarlett-Focusrite + ../../../opt/include/alsa /usr/include/json-c Shared-Interface HAL-afb/HAL-interface @@ -697,6 +799,7 @@ HAL-afb/Unicens-USB + ../../../opt/include/alsa /usr/include/json-c @@ -704,11 +807,345 @@ + build/HAL-afb/HAL-plugin ../../../opt/include/afb HighLevel-afb + + + + build/HAL-afb/HAL-plugin + + + + + + + ../../../opt/include/afb + Shared-Interface + /usr/include/json-c + build/Shared-Interface + + + + + + + default + false + false + + + + + + + + + + build + ${MAKE} + ${MAKE} clean + + + + build + cmake .. + true + + + + + + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + ../../../opt/include + build/ALSA-afb + + + + + + + build/ALSA-afb + + + + + + + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + ../../../opt/include + build/ALSA-afb + + + + + + + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + ../../../opt/include + build/ALSA-afb + + + + + + + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + ../../../opt/include + build/ALSA-afb + + + + + + + + + + + + + + + + + + + Shared-Interface + ../../../opt/include + build/HAL-afb/HAL-interface + + + + + + + Shared-Interface + build/HAL-afb/HAL-interface + + + + + + + build/HAL-afb/HAL-interface + + + + + + + + + + + + + + + + + + + + + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/Unicens-USB + + + + + + + HAL-afb/HAL-interface + build/HAL-afb/Unicens-USB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../opt/include/afb + ALSA-afb + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + build/Alsa-Plugin/Alsa-Policy-Hook + + + CONTROL_CDEV_RX="/dev/inic-usb-crx" + CONTROL_CDEV_TX="/dev/inic-usb-ctx" + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + PIC + TLV_BYTE_SIZE=256 + alsa_hook_cb_EXPORTS + + + + + + + ../../../opt/include/afb + HAL-afb/HAL-interface + ../../../opt/include/alsa + /usr/include/json-c + + + + + + + ../../../opt/include/afb + HAL-afb/HDA-intel + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/HDA-intel + + + + + + + HAL-afb/Jabra-Solemate + ../../../opt/include/alsa + /usr/include/json-c + ../../../opt/include/afb + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/Jabra-Solemate + + + + + + + ../../../opt/include/afb + HAL-afb/Scarlett-Focusrite + ../../../opt/include/alsa + /usr/include/json-c + Shared-Interface + HAL-afb/HAL-interface + ../../../opt/include + build/HAL-afb/Scarlett-Focusrite + + + + + + + ../../../opt/include/afb + HAL-afb/Unicens-USB + ../../../opt/include/alsa + /usr/include/json-c + + + diff --git a/nbproject/project.xml b/nbproject/project.xml index 3e443c4..859334c 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -21,6 +21,10 @@ Laptop 0 + + AplayHook + 0 + false -- cgit 1.2.3-korg