From 521281617cec5d89725c4844eac68b5a772fab57 Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Sat, 11 Mar 2017 00:27:30 +0100 Subject: Work in Progress Static/Dynamic Lib Fixed. Source Tree cleaned up Compile but to be tested Business Logic not done --- AlsaSound/CoreBinding/AlsaAfbBinding.c | 7 +- AlsaSound/CoreBinding/AlsaLibMapping.c | 1 - AlsaSound/CoreBinding/AlsaLibMapping.h | 19 +- AlsaSound/HALayer/CMakeLists.txt | 11 +- AlsaSound/HALayer/IntelHda/CMakeLists.txt | 2 +- AlsaSound/HALayer/IntelHda/IntelHdaHAL.c | 60 +++-- AlsaSound/HALayer/Shared/CMakeLists.txt | 30 --- AlsaSound/HALayer/Shared/SharedHalLib.c | 252 --------------------- AlsaSound/HALayer/SharedHal/CMakeLists.txt | 29 +++ AlsaSound/HALayer/SharedHal/SharedHalLib.c | 341 +++++++++++++++++++++++++++++ AlsaSound/HALayer/SharedHal/SharedHalLib.h | 62 ++++++ AlsaSound/HALayer/include/AlsaHalCtls.h | 57 ----- AlsaSound/HALayer/include/AlsaHalIface.h | 52 ----- AlsaSound/HALayer/include/MiscHelpers.h | 33 --- AudioLogic/AudioLogicBinding.c | 21 +- AudioLogic/AudioLogicLib.c | 253 +++++++++++---------- AudioLogic/AudioLogicLib.h | 24 +- CMakeLists.txt | 7 +- Common/AudioCommonLib.c | 101 +++++++++ Common/AudioCommonLib.h | 68 ++++++ Common/CMakeLists.txt | 31 +++ nbproject/configurations.xml | 22 +- 22 files changed, 838 insertions(+), 645 deletions(-) delete mode 100644 AlsaSound/HALayer/Shared/CMakeLists.txt delete mode 100644 AlsaSound/HALayer/Shared/SharedHalLib.c create mode 100644 AlsaSound/HALayer/SharedHal/CMakeLists.txt create mode 100644 AlsaSound/HALayer/SharedHal/SharedHalLib.c create mode 100644 AlsaSound/HALayer/SharedHal/SharedHalLib.h delete mode 100644 AlsaSound/HALayer/include/AlsaHalCtls.h delete mode 100644 AlsaSound/HALayer/include/AlsaHalIface.h delete mode 100644 AlsaSound/HALayer/include/MiscHelpers.h create mode 100644 Common/AudioCommonLib.c create mode 100644 Common/AudioCommonLib.h create mode 100644 Common/CMakeLists.txt diff --git a/AlsaSound/CoreBinding/AlsaAfbBinding.c b/AlsaSound/CoreBinding/AlsaAfbBinding.c index 4ae7ccc..a4433e8 100644 --- a/AlsaSound/CoreBinding/AlsaAfbBinding.c +++ b/AlsaSound/CoreBinding/AlsaAfbBinding.c @@ -30,17 +30,12 @@ PUBLIC const struct afb_binding_interface *afbIface; -static void localping(struct afb_req request) { - json_object *query = afb_req_json(request); - afb_req_success(request, query, NULL); -} - /* * array of the verbs exported to afb-daemon */ static const struct afb_verb_desc_v1 binding_verbs[] = { /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "ping" , .session= AFB_SESSION_NONE, .callback= localping, .info= "Ping Binding" }, + { .name= "ping" , .session= AFB_SESSION_NONE, .callback= pingtest, .info= "Ping Binding" }, { .name= "getinfo", .session= AFB_SESSION_NONE, .callback= alsaGetInfo, .info= "List All/One Sound Cards Info" }, { .name= "getctl", .session= AFB_SESSION_NONE, .callback= alsaGetCtl, .info= "List All/One Controls from selected sndcard" }, { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= alsaSubcribe, .info= "Subscribe to events from selected sndcard" }, diff --git a/AlsaSound/CoreBinding/AlsaLibMapping.c b/AlsaSound/CoreBinding/AlsaLibMapping.c index 900aa3d..58f52c2 100644 --- a/AlsaSound/CoreBinding/AlsaLibMapping.c +++ b/AlsaSound/CoreBinding/AlsaLibMapping.c @@ -27,7 +27,6 @@ #include #include "AlsaLibMapping.h" - #include static struct afb_service srvitf; diff --git a/AlsaSound/CoreBinding/AlsaLibMapping.h b/AlsaSound/CoreBinding/AlsaLibMapping.h index e9e3a42..fb1d39a 100644 --- a/AlsaSound/CoreBinding/AlsaLibMapping.h +++ b/AlsaSound/CoreBinding/AlsaLibMapping.h @@ -15,28 +15,11 @@ * limitations under the License. */ - -// few coding convention -typedef int BOOL; -#ifndef PUBLIC - #define PUBLIC -#endif -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif -#define STATIC static #ifndef ALSALIBMAPPING_H #define ALSALIBMAPPING_H -#include -#include -#include - -#include "MiscHelpers.h" +#include "AudioCommonLib.h" // import from AlsaAfbBinding extern const struct afb_binding_interface *afbIface; diff --git a/AlsaSound/HALayer/CMakeLists.txt b/AlsaSound/HALayer/CMakeLists.txt index 0b12f62..4601c79 100644 --- a/AlsaSound/HALayer/CMakeLists.txt +++ b/AlsaSound/HALayer/CMakeLists.txt @@ -18,9 +18,14 @@ PROJECT(hadware-abstraction-layer C) -# Fulup NOTE: this might have to be change for a formal static/dynamic lib -set(halsharedlib ${CMAKE_CURRENT_SOURCE_DIR}/Shared/SharedHalLib.c) +SET(include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/SharedHal ${include_dirs}) +# Add ShareHalLib statically within each HAL bindings +SET(link_libraries + ${CMAKE_BINARY_DIR}/AlsaSound/HALayer/SharedHal/libsharedhal.a + ${link_libraries} +) + +ADD_SUBDIRECTORY(SharedHal) ADD_SUBDIRECTORY(IntelHda) -ADD_SUBDIRECTORY(Shared) diff --git a/AlsaSound/HALayer/IntelHda/CMakeLists.txt b/AlsaSound/HALayer/IntelHda/CMakeLists.txt index 77218e2..89665e4 100644 --- a/AlsaSound/HALayer/IntelHda/CMakeLists.txt +++ b/AlsaSound/HALayer/IntelHda/CMakeLists.txt @@ -22,7 +22,7 @@ INCLUDE_DIRECTORIES(${include_dirs}) ################################################## # Inte-HDA sound card Hardware Abstraction Layer ################################################## -ADD_LIBRARY(intel-hda-hal MODULE IntelHdaHAL.c ${halsharedlib}) +ADD_LIBRARY(intel-hda-hal MODULE IntelHdaHAL.c ) SET_TARGET_PROPERTIES(intel-hda-hal PROPERTIES PREFIX "" diff --git a/AlsaSound/HALayer/IntelHda/IntelHdaHAL.c b/AlsaSound/HALayer/IntelHda/IntelHdaHAL.c index b04b545..5d668ba 100644 --- a/AlsaSound/HALayer/IntelHda/IntelHdaHAL.c +++ b/AlsaSound/HALayer/IntelHda/IntelHdaHAL.c @@ -15,9 +15,20 @@ * limitations under the License. */ #define _GNU_SOURCE -#include "AlsaHalIface.h" // Include Share Interface to Alsa Sound Card HAL +#include "AudioCommonLib.h" +#include "SharedHalLib.h" // Include Share Interface to Alsa Sound Card HAL -/***************************************************************************** +// Force a hard dependency to ShareHallLib +PUBLIC char* SharedHalLibVersion; + +// Init is call after all binding are loaded +STATIC int IntelHalInit (const struct afb_binding_interface *itf, struct afb_service service) { + DEBUG (itf, "IntelHalBinding Initialised"); + + return 0; // 0=OK +} + +/****************************************************************************************** * alsaCtlsMap link hight level sound control with low level Alsa numid ctls. * * To find out which control your sound card uses @@ -25,13 +36,16 @@ * amixer -D hw:xx controls * amixer -D hw:xx contents * amixer -D "hw:3" cget numid=xx - *****************************************************************************/ -STATIC alsaHalCtlMapT alsaHalCtlsMap[]= { - { .control=Master_Playback_Volume, .numid=16, .group=OUTVOL, .values=1, .minval=0, .maxval= 87 , .step=0, .acl=RW, .info= "Master Playback Volume" }, - { .control=PCM_Playback_Volume , .numid=27, .group=PCMVOL, .values=2, .minval=0, .maxval= 255, .step=0, .acl=RW, .info= "PCM Playback Volume" }, - { .control=PCM_Playback_Switch , .numid=17, .group=SWITCH, .values=1, .minval=0, .maxval= 1 , .step=0, .acl=RW, .info= "Master Playback Switch" }, - { .control=Capture_Volume , .numid=12, .group=INVOL , .values=2, .minval=0, .maxval= 31 , .step=0, .acl=RW, .info= "Capture Volume" }, - { .numid=0 } /* marker for end of the array */ + * + * When automatic mapping to Alsa numid is not enough a custom callback might be used + * .cb={.handle=xxxx, .callback=(json_object)MyCtlFunction(struct afb_service service, int controle, int value, const struct alsaHalCtlMapS *map)}; + ********************************************************************************************/ +STATIC alsaHalMapT alsaHalMap[]= { + { .alsa={.control=Master_Playback_Volume,.numid=16,.group=OUTVOL,.values=1,.minval=0,.maxval= 87 ,.step=0,.acl=RW}, .info= "Master Playback Volume" }, + { .alsa={.control=PCM_Playback_Volume ,.numid=27,.group=PCMVOL,.values=2,.minval=0,.maxval= 255,.step=0,.acl=RW}, .info= "PCM Playback Volume" }, + { .alsa={.control=PCM_Playback_Switch ,.numid=17,.group=SWITCH,.values=1,.minval=0,.maxval= 1 ,.step=0,.acl=RW}, .info= "Master Playback Switch" }, + { .alsa={.control=Capture_Volume ,.numid=12,.group=INVOL ,.values=2,.minval=0,.maxval= 31 ,.step=0,.acl=RW}, .info= "Capture Volume" }, + { .alsa={.numid=0}, .cb={.callback=NULL, .handle=NULL}} /* marker for end of the array */ } ; /*********************************************************************************** @@ -46,29 +60,9 @@ STATIC alsaHalCtlMapT alsaHalCtlsMap[]= { * ***********************************************************************************/ PUBLIC alsaHalSndCardT alsaHalSndCard = { - .name = "HDA Intel PCH", - .info = "Hardware Abstraction Layer for IntelHDA sound card", - .ctls = alsaHalCtlsMap, -}; - -/*********************************************************************************** - * AlsaHalSndT provides - * - cardname used to map a given card to its HAL - * - ctls previously defined AlsaHalMapT control maps - * - info free text - * - * WARNING: name should fit with 'aplay -l' as it used to map from devid to HAL - * you may also retreive shortname when AudioBinder is running from a browser - * http://localhost:1234/api/alsacore/getcardid?devid=hw:xxx - * - ***********************************************************************************/ -PUBLIC struct afb_binding alsaHalBinding = { - /* description conforms to VERSION 1 */ - .type= AFB_BINDING_VERSION_1, - .v1= { - .prefix= "intel-hda", + .name = "HDA Intel PCH", .info = "Hardware Abstraction Layer for IntelHDA sound card", - } + .ctls = alsaHalMap, + .prefix="intel-hda", + .initCB=IntelHalInit, // if NULL no initcallback }; - - diff --git a/AlsaSound/HALayer/Shared/CMakeLists.txt b/AlsaSound/HALayer/Shared/CMakeLists.txt deleted file mode 100644 index 27d93e0..0000000 --- a/AlsaSound/HALayer/Shared/CMakeLists.txt +++ /dev/null @@ -1,30 +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. -########################################################################### - - -INCLUDE_DIRECTORIES(${include_dirs}) - -################################################## -# Shared HAL(Hardware Abstraction Layer) -################################################## -ADD_LIBRARY(shared-hal MODULE SharedHalLib.c) - -TARGET_LINK_LIBRARIES(shared-hal ${link_libraries}) -INSTALL(TARGETS shared-hal LIBRARY DESTINATION ${binding_install_dir}) - - diff --git a/AlsaSound/HALayer/Shared/SharedHalLib.c b/AlsaSound/HALayer/Shared/SharedHalLib.c deleted file mode 100644 index ffa25e0..0000000 --- a/AlsaSound/HALayer/Shared/SharedHalLib.c +++ /dev/null @@ -1,252 +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. - * - * reference: - * amixer contents; amixer controls; - * http://www.tldp.org/HOWTO/Alsa-sound-6.html - */ -#define _GNU_SOURCE // needed for vasprintf -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "MiscHelpers.h" -#include "AlsaHalIface.h" - -typdef struct { - int numid; -} shareHallMap_T; - -static struct afb_service srvitf; -static const struct afb_binding_interface *afbIface; -static shareHallMap_T *shareHallMap; - - -STATIC void localping(struct afb_req request) { - json_object *query = afb_req_json(request); - afb_req_success(request, query, NULL); -} - -// This callback when api/alsacore/subscribe returns -STATIC void alsaSubcribeCB (void *handle, int iserror, struct json_object *result) { - struct afb_req request = afb_req_unstore(handle); - struct json_object *x, *resp = NULL; - const char *info = NULL; - - if (result) { - INFO (afbIface, "result=[%s]\n", json_object_to_json_string (result)); - if (json_object_object_get_ex(result, "request", &x) && json_object_object_get_ex(x, "info", &x)) - info = json_object_get_string(x); - if (!json_object_object_get_ex(result, "response", &resp)) resp = NULL; - } - - // push message respond - if (iserror) afb_req_fail_f(request, "Fail", info); - else afb_req_success(request, resp, info); - - // free calling request - afb_req_unref(request); -} - -// Create and subscribe to alsacore ctl events -STATIC void halMonitor(struct afb_req request) { - - // save request in session as it might be used after return by callback - struct afb_req *handle = afb_req_store(request); - - // push request to low level binding - if (!handle) afb_req_fail(request, "error", "out of memory"); - else afb_service_call(srvitf, "alsacore", "subctl", json_object_get(afb_req_json(request)), alsaSubcribeCB, handle); - - // success/failure messages return from callback -} - -// Subscribe to AudioBinding events -STATIC void halSubscribe (struct afb_req request) { - const char *devid = afb_req_value(request, "devid"); - if (devid == NULL) { - afb_req_fail_f (request, "devid-missing", "devid=hw:xxx missing"); - } -} - -// Call when all bindings are loaded and ready to accept request -STATIC void halGetVol(struct afb_req request) { - - // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card - afb_req_success (request, NULL, NULL); - return; - -} - -STATIC void halSetVol(struct afb_req request) { - const char *arg; - const char *pcm; - - arg = afb_req_value(request, "vol"); - if (arg == NULL) { - afb_req_fail_f (request, "argument-missing", "vol=[0,100] missing"); - goto OnErrorExit; - } - - pcm = afb_req_value(request, "pcm"); - if (pcm == NULL) pcm="Master"; - - // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card - afb_req_success (request, NULL, NULL); - return; - - OnErrorExit: - return; - -} - -// this is call when after all bindings are loaded -STATIC int halGetControl(struct afb_service service) { - srvitf = service; - struct json_object *queryin, *queryout, *ctls, *devid; - - // get query from request - queryin = afb_req_json(request); - - // check devid was given - devid= json_object_object_get(queryin,"devid"); - if (!ctls) { - afb_req_fail_f(request, "devid-notfound", "No DevID given query=[%s]", json_object_get_string(queryin)); - goto OnErrorExit; - } - - // loop on requested controls - ctls= json_object_object_get(queryin,"ctls"); - if (!ctls || json_object_array_length(ctls) <= 0) { - afb_req_fail_f(request, "ctls-notfound", "No Controls given query=[%s]", json_object_get_string(queryin)); - goto OnErrorExit; - } - - for (int idx=0; idx< json_object_array_length(ctls), idx++) { - struct json_object *ctl; - halControlEnumT control; - int value; - - // each controls should be halControlEnumT+value - ctl = json_object_array_get_idx(ctls, idx); - if (json_object_array_length(ctl != 2)) { - afb_req_fail_f(request, "ctl-invalid", "Invalid Control devid=%s ctl=[%s]"json_object_get_string(devid), json_object_get_string(cls)); - goto OnErrorExit; - } - - // As HAL and Business logic use the same AlsaMixerHal.h direct conversion is not an issue - control = (halControlEnumT)json_object_get_int(json_object_array_get_idx(ctl,0)); - value = json_object_get_int(json_object_array_get_idx(ctl,0)); - - if (control >= EndHalCrlTag || control <= StartHalCrlTag) { - afb_req_fail_f(request, "ctl-invalid", "Invalid Control devid=%s ctl=[%s] should be [%d=%d]" - , json_object_get_string(devid), json_object_get_string(cls)), StartHalCrlTag, EndHalCrlTag; - goto OnErrorExit; - } - - - default: - NOTICE (afbIface, "audioLogicOpenCB2 unknown HAL control=[%s]", json_object_get_string(ctl)): - } - } - - - // register HAL with Alsa Low Level Binder devid=hw:0&numid=1&quiet=0 - queryurl=json_object_new_object(); - json_object_object_add(queryurl, "prefix",json_object_new_string(alsaHalBinding.v1.prefix)); - json_object_object_add(queryurl, "name" ,json_object_new_string(alsaHalSndCard.name)); - afb_service_call(srvitf, "alsacore", "registerHal", queryurl, halGetControlCB, queryurl); - - - afb_req_success (request, sndctrls, NULL); - return; - -OnErrorExit: - -}; - -STATIC void halInitCB (void *handle, int iserror, struct json_object *result) { - struct json_object *queryurl = (json_object*)handle; - - if (iserror) NOTICE (afbIface, "halInitCB: registration alsaHAL query=[%s] Fail", json_object_to_json_string(queryurl)); - else DEBUG(afbIface, "halInitCB: registration alsaHAL card=[%s] Success", json_object_to_json_string(queryurl)); -} - -// This receive all event this binding subscribe to -PUBLIC void afbBindingV1ServiceEvent(const char *evtname, struct json_object *object) { - - NOTICE (afbIface, "afbBindingV1ServiceEvent evtname=%s [msg=%s]", evtname, json_object_to_json_string(object)); -} - -// this is call when after all bindings are loaded -PUBLIC int afbBindingV1ServiceInit(struct afb_service service) { - srvitf = service; - struct json_object *queryurl; - - // API prefix is used as sndcard halname - alsaHalBinding.v1.prefix= prefix; - - // register HAL with Alsa Low Level Binder - queryurl=json_object_new_object(); - json_object_object_add(ctx->queryurl, "prefix",json_object_new_string(alsaHalBinding.v1.prefix)); - json_object_object_add(ctx->queryurl, "name" ,json_object_new_string(alsaHalSndCard.name)); - afb_service_call(srvitf, "alsacore", "registerHal", queryurl, halInitCB, NULL); - - return 0; -}; - - -// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT -STATIC const struct afb_verb_desc_v1 halSharedApi[] = { - /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "ping" , .session= AFB_SESSION_NONE, .callback= localping, .info= "Ping Binding" }, - { .name= "getcontrol", .session= AFB_SESSION_NONE, .callback= halGetControl,.info= "Get Control" }, - { .name= "setvolume", .session= AFB_SESSION_NONE, .callback= halSetVol, .info= "Set Volume" }, - { .name= "getvolume", .session= AFB_SESSION_NONE, .callback= halGetVol, .info= "Get Volume" }, - { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= halSubscribe, .info= "Subscribe AudioBinding Events" }, - { .name= NULL } /* marker for end of the array */ -}; - -// Process HAL mapping from alsaHalSndCardT before registering HAL binder -PUBLIC const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) { - int count; - afbIface= itf; // need to keep a static trace of binder interface for avances functions - alsaHalBinding.verbs=halSharedApi; // complete sndcard specific alsaHalBinding with standard HAL APIs - alsaHalCtlMapT *alsaHalCtls = MapalsaHalSndCard.alsaHalCtlsMap; // Get sndcard specific HAL control mapping - - if (alsaHalCtls == NULL) { - ERROR (afbIface, "afbBindingV1Register Fail alsaHalCtlsMap==NULL"); - return NULL; - } - - // Create a zone to store HAL high/low level mapping - shareHallMap = calloc (EndHalCrlTag * sizeof(shareHallMap_T)); - for (int idx= 0; alsaHalCtlsMap[idx].numid != 0; idx++) { - shareHallMap[alsaHalCtlsMap[idx].]->numid = alsaHalCtlsMap[idx].numid; - } - - return alsaHalBinding; /* returns the description of the binding */ -} diff --git a/AlsaSound/HALayer/SharedHal/CMakeLists.txt b/AlsaSound/HALayer/SharedHal/CMakeLists.txt new file mode 100644 index 0000000..d789367 --- /dev/null +++ b/AlsaSound/HALayer/SharedHal/CMakeLists.txt @@ -0,0 +1,29 @@ +########################################################################### +# 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_DIRECTORIES(${include_dirs}) + +################################################## +# Shared HAL(Hardware Abstraction Layer) +################################################## +ADD_LIBRARY(sharedhal STATIC SharedHalLib.c) +SET_TARGET_PROPERTIES(sharedhal PROPERTIES OUTPUT_NAME sharedhal) +SET(link_libraries ${libefence_LIBRARIES} ${json-c_LIBRARIES}) +TARGET_LINK_LIBRARIES(sharedhal ${link_libraries}) + + diff --git a/AlsaSound/HALayer/SharedHal/SharedHalLib.c b/AlsaSound/HALayer/SharedHal/SharedHalLib.c new file mode 100644 index 0000000..0b39f29 --- /dev/null +++ b/AlsaSound/HALayer/SharedHal/SharedHalLib.c @@ -0,0 +1,341 @@ +/* + * 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. + * + * reference: + * amixer contents; amixer controls; + * http://www.tldp.org/HOWTO/Alsa-sound-6.html + */ +#define _GNU_SOURCE // needed for vasprintf +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SharedHalLib.h" + +typedef struct { + int index; + int numid; +} shareHallMap_T; + +static struct afb_service srvitf; +static const struct afb_binding_interface *afbIface; +static shareHallMap_T *shareHallMap; + +// Force specific HAL to depend on ShareHalLib +PUBLIC char* SharedHalLibVersion="1.0"; + +// This callback when api/alsacore/subscribe returns +STATIC void alsaSubcribeCB(void *handle, int iserror, struct json_object *result) { + struct afb_req request = afb_req_unstore(handle); + struct json_object *x, *resp = NULL; + const char *info = NULL; + + if (result) { + INFO(afbIface, "result=[%s]\n", json_object_to_json_string(result)); + if (json_object_object_get_ex(result, "request", &x) && json_object_object_get_ex(x, "info", &x)) + info = json_object_get_string(x); + if (!json_object_object_get_ex(result, "response", &resp)) resp = NULL; + } + + // push message respond + if (iserror) afb_req_fail_f(request, "Fail", info); + else afb_req_success(request, resp, info); + + // free calling request + afb_req_unref(request); +} + +// Create and subscribe to alsacore ctl events + +STATIC void halMonitor(struct afb_req request) { + + // save request in session as it might be used after return by callback + struct afb_req *handle = afb_req_store(request); + + // push request to low level binding + if (!handle) afb_req_fail(request, "error", "out of memory"); + else afb_service_call(srvitf, "alsacore", "subctl", json_object_get(afb_req_json(request)), alsaSubcribeCB, handle); + + // success/failure messages return from callback +} + +// Subscribe to AudioBinding events + +STATIC void halSubscribe(struct afb_req request) { + const char *devid = afb_req_value(request, "devid"); + if (devid == NULL) { + afb_req_fail_f(request, "devid-missing", "devid=hw:xxx missing"); + } +} + +// Call when all bindings are loaded and ready to accept request + +STATIC void halGetVol(struct afb_req request) { + + // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card + afb_req_success(request, NULL, NULL); + return; + +} + +STATIC void halSetVol(struct afb_req request) { + const char *arg; + const char *pcm; + + arg = afb_req_value(request, "vol"); + if (arg == NULL) { + afb_req_fail_f(request, "argument-missing", "vol=[0,100] missing"); + goto OnErrorExit; + } + + pcm = afb_req_value(request, "pcm"); + if (pcm == NULL) pcm = "Master"; + + // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card + afb_req_success(request, NULL, NULL); + return; + +OnErrorExit: + return; + +} + +// AudioLogic expect volume values to be 0-100% + +STATIC int NormaliseValue(const alsaHalCtlMapT *halCtls, int valuein) { + int valueout; + + if (valuein > halCtls->maxval) valuein= halCtls->maxval; + if (valuein < halCtls->minval) valuein= halCtls->minval; + + // volume are ajusted to 100% any else is ignored + switch (halCtls->group) { + case OUTVOL: + case PCMVOL: + case INVOL: + valueout = (valuein * 100) / (halCtls->maxval - halCtls->minval); + break; + default: + valueout = valuein; + } + return (valueout); +} + +// receive controls for LowLevel remap them to hight level before returning them + +STATIC void halGetControlCB(void *handle, int iserror, struct json_object *result) { + alsaHalMapT *halCtls = alsaHalSndCard.ctls; + struct json_object *response; + + // retrieve request and check for errors + struct afb_req request = afb_req_unstore(handle); + if (!cbCheckResponse(request, iserror, result)) goto OnExit; + + // Get response from object + json_object_object_get_ex(result, "response", &response); + if (!response) { + afb_req_fail_f(request, "response-notfound", "No Controls return from alsa/getcontrol result=[%s]", json_object_get_string(result)); + goto OnExit; + } + + // extract sounds controls information from received Object + struct json_object *ctls; + json_object_object_get_ex(result, "ctls", &ctls); + if (!ctls) { + afb_req_fail_f(request, "ctls-notfound", "No Controls return from alsa/getcontrol response=[%s]", json_object_get_string(response)); + goto OnExit; + } + + // make sure return controls have a valid type + if (json_object_get_type(ctls) != json_type_array) { + afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol response=[%s]", json_object_get_string(response)); + goto OnExit; + } + + // loop on array and store values into client context + for (int idx = 0; idx < json_object_array_length(ctls); idx++) { + struct json_object *ctl; + int rawvalue, numid; + + ctl = json_object_array_get_idx(ctls, idx); + if (json_object_array_length(ctl) != 2) { + afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from alsa/getcontrol ctl=[%s]", json_object_get_string(ctl)); + goto OnExit; + } + + // As HAL and Business logic use the same AlsaMixerHal.h direct conversion is not an issue + numid = (halCtlsEnumT) json_object_get_int(json_object_array_get_idx(ctl, 0)); + rawvalue = json_object_get_int(json_object_array_get_idx(ctl, 1)); + + for (int idx = 0; halCtls[idx].alsa.numid != 0; idx++) { + if (halCtls[idx].alsa.numid == numid) { + struct json_object *ctlAndValue = json_object_new_array(); + json_object_array_add(ctlAndValue, json_object_new_int(idx)); // idx == high level control + json_object_array_add(ctlAndValue, json_object_new_int(NormaliseValue(&halCtls[idx].alsa, rawvalue))); + break; + } + } + if (shareHallMap[idx].numid == 0) { + + afb_req_fail_f(request, "ctl-invalid", "Invalid Control numid from alsa/getcontrol ctl=[%s]", json_object_get_string(ctl)); + goto OnExit; + } + } + + OnExit: + return; +} + + +// Translate high level control to low level and call lower layer +STATIC void halGetCtls(struct afb_req request) { + + struct json_object *queryin, *queryout, *ctlsin, *devid; + struct json_object *ctlsout = json_object_new_array(); + + // get query from request + queryin = afb_req_json(request); + + // check devid was given + if (!json_object_object_get_ex (queryin, "devid", &devid)) { + afb_req_fail_f(request, "devid-notfound", "No DevID given query=[%s]", json_object_get_string(queryin)); + goto OnExit; + } + + // loop on requested controls + if (!json_object_object_get_ex(queryin, "ctls", &ctlsin) || json_object_array_length(ctlsin) <= 0) { + afb_req_fail_f(request, "ctlsin-notfound", "No Controls given query=[%s]", json_object_get_string(queryin)); + goto OnExit; + } + + // loop on controls + for (int idx = 0; idx < json_object_array_length(ctlsin); idx++) { + struct json_object *ctl; + halCtlsEnumT control; + + // each control should be halCtlsEnumT + ctl = json_object_array_get_idx(ctlsin, idx); + control = (halCtlsEnumT) json_object_get_int(ctl); + if (control >= EndHalCrlTag || control <= StartHalCrlTag) { + afb_req_fail_f(request, "ctl-invalid", "Invalid Control devid=%s sndcard=%s ctl=[%s] should be [%d-%d]" + , json_object_get_string(devid), alsaHalSndCard.name, json_object_get_string(ctl), StartHalCrlTag, EndHalCrlTag); + goto OnExit; + } + + // add corresponding lowlevel numid to querylist + json_object_array_add(ctlsout, json_object_new_int(shareHallMap[control].numid)); + } + + // register HAL with Alsa Low Level Binder devid=hw:0&numid=1&quiet=0 + queryout = json_object_new_object(); + json_object_object_add(queryout, "devid", devid); + json_object_object_add(queryout, "ctls", ctlsout); + + // save request context dans call lowlevel API + struct afb_req *handle = afb_req_store(request); + afb_service_call(srvitf, "alsacore", "getctl", queryout, halGetControlCB, handle); + +OnExit: + // Under normal situation success/failure is set from callback + return; +}; + +STATIC void halInitCB(void *handle, int iserror, struct json_object *result) { + struct json_object *queryurl = (json_object*) handle; + + if (iserror) NOTICE(afbIface, "halInitCB: registration alsaHAL query=[%s] Fail", json_object_to_json_string(queryurl)); + else DEBUG(afbIface, "halInitCB: registration alsaHAL card=[%s] Success", json_object_to_json_string(queryurl)); +} + +// This receive all event this binding subscribe to +PUBLIC void afbBindingV1ServiceEvent(const char *evtname, struct json_object *object) { + + NOTICE(afbIface, "afbBindingV1ServiceEvent evtname=%s [msg=%s]", evtname, json_object_to_json_string(object)); +} + +// this is call when after all bindings are loaded +PUBLIC int afbBindingV1ServiceInit(struct afb_service service) { + int rc=0; + srvitf = service; + struct json_object *queryurl; + + if (alsaHalSndCard.initCB) { + rc= (alsaHalSndCard.initCB) (afbIface, service); + if (rc != 0) goto OnExit; + } + + // register HAL with Alsa Low Level Binder + queryurl = json_object_new_object(); + json_object_object_add(queryurl, "prefix", json_object_new_string(alsaHalSndCard.prefix)); + json_object_object_add(queryurl, "name", json_object_new_string(alsaHalSndCard.name)); + afb_service_call(srvitf, "alsacore", "registerHal", queryurl, halInitCB, NULL); + + OnExit: + return (rc); +}; + +// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT +static const struct afb_verb_desc_v1 halSharedApi[] = { + /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ + { .name = "ping", .session = AFB_SESSION_NONE, .callback = pingtest, .info = "Ping Binding"}, + { .name = "getctls", .session = AFB_SESSION_NONE, .callback = halGetCtls, .info = "Get Control"}, + { .name = "setvol", .session = AFB_SESSION_NONE, .callback = halSetVol, .info = "Set Volume"}, + { .name = "getvol", .session = AFB_SESSION_NONE, .callback = halGetVol, .info = "Get Volume"}, + { .name = "subscribe", .session = AFB_SESSION_NONE, .callback = halSubscribe,.info = "Subscribe Alsa Events"}, + { .name = "monitor", .session = AFB_SESSION_NONE, .callback = halMonitor ,.info = "Monitor Alsa Events"}, + { .name = NULL} /* marker for end of the array */ +}; + + +static struct afb_binding alsaHalBinding = { + /* description conforms to VERSION 1 */ + .type= AFB_BINDING_VERSION_1, + .v1= { + .prefix= NULL, + .info = NULL, + .verbs = halSharedApi, + } +}; + +// Process HAL mapping from alsaHalSndCardT before registering HAL binder +PUBLIC const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) { + + afbIface = itf; // need to keep a static trace of binder interface for avances functions + // alsaHalBinding.v1.verbs = halSharedApi; // complete sndcard specific alsaHalBinding with standard HAL APIs + alsaHalMapT *halCtls = alsaHalSndCard.ctls; // Get sndcard specific HAL control mapping + + if (halCtls == NULL) { + ERROR(afbIface, "afbBindingV1Register Fail alsaHalCtlsMap==NULL"); + return NULL; + } + + // Create a zone to store HAL high/low level mapping + shareHallMap = malloc(EndHalCrlTag * sizeof (shareHallMap_T)); + for (int idx = 0; (halCtls[idx].alsa.numid != 0 || halCtls[idx].cb.callback != NULL); idx++) { + if (halCtls[idx].alsa.numid == 0) halCtls[idx].alsa.numid =-1; + shareHallMap[halCtls[idx].alsa.control].numid = halCtls[idx].alsa.numid; + } + + alsaHalBinding.v1.prefix= alsaHalSndCard.prefix; + alsaHalBinding.v1.info = alsaHalSndCard.info; + return &alsaHalBinding; /* returns the description of the binding */ +}; diff --git a/AlsaSound/HALayer/SharedHal/SharedHalLib.h b/AlsaSound/HALayer/SharedHal/SharedHalLib.h new file mode 100644 index 0000000..7cf4489 --- /dev/null +++ b/AlsaSound/HALayer/SharedHal/SharedHalLib.h @@ -0,0 +1,62 @@ +/* + * AlsaLibMapping -- provide low level interface with AUDIO lib (extracted from alsa-json-gateway code) + * Copyright (C) 2015,2016,2017, Fulup Ar Foll fulup@iot.bzh + * + * 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. + */ + +#ifndef SHAREHALLIB_H +#define SHAREHALLIB_H + +#include +#include "AudioCommonLib.h" + +typedef struct { + halCtlsEnumT control; + int numid; + halGroupEnumT group; + int values; + int minval; + int maxval; + int step; + halAclEnumT acl; +} alsaHalCtlMapT; + +// avoid compiler warning [Jose does not like typedef :) ] +typedef struct afb_service alsaHalServiceT; + +typedef struct { + struct json_object* (*callback)(alsaHalServiceT service, int control, int value, alsaHalCtlMapT *map, void* handle); + void* handle; +} alsaHalCbMapT; + +typedef struct { + alsaHalCtlMapT alsa; + alsaHalCbMapT cb; + char* info; +} alsaHalMapT; + +typedef struct { + const char *prefix; + const char *name; + const char *info; + alsaHalMapT *ctls; + int (*initCB) (const struct afb_binding_interface *itf, struct afb_service service); + +} alsaHalSndCardT; + +PUBLIC alsaHalSndCardT alsaHalSndCard; +PUBLIC char* SharedHalLibVersion; + +#endif /* SHAREHALLIB_H */ + diff --git a/AlsaSound/HALayer/include/AlsaHalCtls.h b/AlsaSound/HALayer/include/AlsaHalCtls.h deleted file mode 100644 index a17315a..0000000 --- a/AlsaSound/HALayer/include/AlsaHalCtls.h +++ /dev/null @@ -1,57 +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. - * - * reference: - * amixer contents; amixer controls; - * http://www.tldp.org/HOWTO/Alsa-sound-6.html - */ - - - -#ifndef ALSAMIXERMAP_H -#define ALSAMIXERMAP_H - -// Most controls are MIXER but some vendor specific are possible -typedef enum { - OUTVOL, - PCMVOL, - INVOL, - SWITCH, - ROUTE, - CARD, -} halGroupEnumT; - -typedef enum { - READ, - WRITE, - RW, -} halAclEnumT; - -typedef enum { - StartHalCrlTag=0, - - // HighLevel Audio Control List - Master_Playback_Volume, - PCM_Playback_Volume, - PCM_Playback_Switch, - Capture_Volume, - - EndHalCrlTag // used to compute number of ctls -} halControlEnumT; - - -#endif /* ALSAMIXERMAP_H */ - diff --git a/AlsaSound/HALayer/include/AlsaHalIface.h b/AlsaSound/HALayer/include/AlsaHalIface.h deleted file mode 100644 index 83ab57c..0000000 --- a/AlsaSound/HALayer/include/AlsaHalIface.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * AlsaLibMapping -- provide low level interface with AUDIO lib (extracted from alsa-json-gateway code) - * Copyright (C) 2015,2016,2017, Fulup Ar Foll fulup@iot.bzh - * - * 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. - */ - -#ifndef SHAREDHALLIB_H -#define SHAREDHALLIB_H - -#include -#include - -#include "MiscHelpers.h" -#include "AlsaHalCtls.h" - -typedef const struct { - halControlEnumT control; - int numid; - halGroupEnumT group; - int values; - int minval; - int maxval; - int step; - char* info; - halAclEnumT acl; - -} alsaHalCtlMapT; - -typedef struct { - const char *prefix; - const char *name; - const char *info; - alsaHalCtlMapT *ctls; - -} alsaHalSndCardT; - -PUBLIC alsaHalSndCardT alsaHalSndCard; -PUBLIC struct afb_binding alsaHalBinding; - -#endif /* SHAREDHALLIB_H */ - diff --git a/AlsaSound/HALayer/include/MiscHelpers.h b/AlsaSound/HALayer/include/MiscHelpers.h deleted file mode 100644 index 0181847..0000000 --- a/AlsaSound/HALayer/include/MiscHelpers.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * AlsaLibMapping -- provide low level interface with AUDIO lib (extracted from alsa-json-gateway code) - * Copyright (C) 2015,2016,2017, Fulup Ar Foll fulup@iot.bzh - * - * 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. - */ - -#ifndef MISCHELPER_H -#define MISCHELPER_H - -#ifndef PUBLIC - #define PUBLIC -#endif -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif -#define STATIC static - -#endif /* MISCHELPER_H */ - diff --git a/AudioLogic/AudioLogicBinding.c b/AudioLogic/AudioLogicBinding.c index 0c63d00..002ff42 100644 --- a/AudioLogic/AudioLogicBinding.c +++ b/AudioLogic/AudioLogicBinding.c @@ -30,10 +30,21 @@ PUBLIC const struct afb_binding_interface *afbIface; -STATIC void localping(struct afb_req request) { - json_object *query = afb_req_json(request); - afb_req_success(request, query, NULL); -} +// Map HAL Enum to Labels +typedef const struct { + halCtlsEnumT control; + char *label; +} LogicControlT; + +// High Level Control Mapping to String for JSON & HTML5 +STATIC LogicControlT LogicControl[] = { + {.control= Master_Playback_Volume,.label= "Master_Volume"}, + {.control= PCM_Playback_Volume, .label= "Playback_Volume"}, + {.control= PCM_Playback_Switch, .label= "Playback_Switch"}, + {.control= Capture_Volume, .label= "Capture_Volume"}, + + {.control= 0,.label= NULL} // closing convention +}; /* @@ -41,7 +52,7 @@ STATIC void localping(struct afb_req request) { */ STATIC const struct afb_verb_desc_v1 binding_verbs[] = { /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "ping" , .session= AFB_SESSION_NONE, .callback= localping, .info= "Ping Binding" }, + { .name= "ping" , .session= AFB_SESSION_NONE, .callback= pingtest, .info= "Ping Binding" }, { .name= "setvolume", .session= AFB_SESSION_CHECK, .callback= audioLogicSetVol, .info= "Set Volume" }, { .name= "getvolume", .session= AFB_SESSION_CHECK, .callback= audioLogicGetVol, .info= "Get Volume" }, { .name= "subscribe", .session= AFB_SESSION_CHECK, .callback= audioLogicSubscribe, .info= "Subscribe AudioBinding Events" }, diff --git a/AudioLogic/AudioLogicLib.c b/AudioLogic/AudioLogicLib.c index b45707a..e9d7104 100644 --- a/AudioLogic/AudioLogicLib.c +++ b/AudioLogic/AudioLogicLib.c @@ -30,45 +30,33 @@ static struct afb_service srvitf; -STATIC int cbCheckResponse (struct afb_req request, int iserror, struct json_object *result) { - struct json_object *response, *status, *info; - - if (iserror) { // on error proxy information we got from lower layer - if (result) { - if (json_object_object_get_ex(result, "request", &response)) { - json_object_object_get_ex(response, "info" , &info); - json_object_object_get_ex(response, "status", &status); - afb_req_fail(request, json_object_get_string(status), json_object_get_string(info)); - goto OnExit; - } - } else { - afb_req_fail(request, "cbCheckFail", "No Result inside API response" ); - } - goto OnErrorExit; - } - return (0); - - OnErrorExit: - return (1); -} +// This callback is fired when afb_service_call for api/alsacore/subctl returns +STATIC void audioLogicSetVolCB(void *handle, int iserror, struct json_object *result) { + struct afb_req request = afb_req_unstore(handle); + + if (!cbCheckResponse(request, iserror, result)) goto OnExit; +OnExit: + return; +} PUBLIC void audioLogicSetVol(struct afb_req request) { struct json_object *queryurl; - + int volume=0; // FULUP TBD !!!!!!!!!!!! + // keep request for callback to respond struct afb_req *handle = afb_req_store(request); - + // get client context AudioLogicCtxT *ctx = afb_req_context_get(request); - + const char *vol = afb_req_value(request, "vol"); if (vol == NULL) { - afb_req_fail_f (request, "argument-missing", "vol=+-%[0,100] missing"); + afb_req_fail_f(request, "argument-missing", "vol=+-%[0,100] missing"); goto OnExit; } - + switch (vol[0]) { case '+': break; @@ -76,99 +64,105 @@ PUBLIC void audioLogicSetVol(struct afb_req request) { break; case '%': break; - + default: - afb_req_fail_f (request, "value-invalid", "volume should be (+-%[0-100]xxx) vol=%s", vol); - goto OnExit; + afb_req_fail_f(request, "value-invalid", "volume should be (+-%[0-100]xxx) vol=%s", vol); + goto OnExit; } - + if (!ctx->halapi) { afb_req_fail_f(request, "context-invalid", "No valid halapi in client context"); goto OnExit; } - - // ********** Caluler le volume en % de manière intelligente - - queryurl=json_object_new_object(); - json_object_object_add(ctx->queryurl, "pcm",json_object_new_int(Master_Playback_Volume)); - json_object_object_add(ctx->queryurl, "value",json_object_new_int(volume)); - + + // ********** Caluler le volume en % de manière intelligente + queryurl = json_object_new_object(); + json_object_object_add(ctx->queryurl, "pcm", json_object_new_int(Master_Playback_Volume)); + json_object_object_add(ctx->queryurl, "value", json_object_new_int(volume)); + // subcontract HAL API to process volume afb_service_call(srvitf, ctx->halapi, "volume", queryurl, audioLogicSetVolCB, handle); - + // final success/failure messages handle from callback - OnExit: - return; +OnExit: + return; } // This callback is fired when afb_service_call for api/alsacore/subctl returns -STATIC void alsaSubcribeCB (void *handle, int iserror, struct json_object *result) { + +STATIC void alsaSubcribeCB(void *handle, int iserror, struct json_object *result) { struct afb_req request = afb_req_unstore(handle); - if (!cbCheckResponse (request, iserror, result)) goto OnExit - - OnExit: + if (!cbCheckResponse(request, iserror, result)) goto OnExit; + +OnExit: return; } // Create and subscribe to alsacore ctl events PUBLIC void audioLogicMonitor(struct afb_req request) { - - // keep request for callback to respond - struct afb_req *handle = afb_req_store(request); - + + // get client context AudioLogicCtxT *ctx = afb_req_context_get(request); - + if (!ctx) { + afb_req_fail_f(request, "ctx-notfound", "No Client Context HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); + goto OnExit; + } + // push request to low level binding - NOTICE (afbIface, "audioLogicMonitor ctx->devid=%s [ctx->queryurl=%s]", ctx->devid, json_object_to_json_string(ctx->queryurl)); - + NOTICE(afbIface, "audioLogicMonitor ctx->devid=%s [ctx->queryurl=%s]", ctx->devid, json_object_to_json_string(ctx->queryurl)); + if (ctx->queryurl) { - json_object_get (ctx->queryurl); // Make sure usage count does not fall to zero + json_object_get(ctx->queryurl); // Make sure usage count does not fall to zero + struct afb_req *handle = afb_req_store(request); afb_service_call(srvitf, "alsacore", "subscribe", ctx->queryurl, alsaSubcribeCB, handle); } - - else afb_req_fail_f(request, "context-invalid", "No valid queryurl in client context"); + else afb_req_fail_f(request, "context-invalid", "No valid queryurl in client context"); - // success/failure messages return from callback + // success/failure messages return from callback +OnExit: + return; } // Subscribe to AudioBinding events -PUBLIC void audioLogicSubscribe (struct afb_req request) { - - return; + +PUBLIC void audioLogicSubscribe(struct afb_req request) { + + return; } // Call when all bindings are loaded and ready to accept request PUBLIC void audioLogicGetVol(struct afb_req request) { - + // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card - afb_req_success (request, NULL, NULL); + afb_req_success(request, NULL, NULL); return; - + } // This callback is fired when afb_service_call for api/alsacore/subctl returns -STATIC void audioLogicOpenCB2 (void *handle, int iserror, struct json_object *result) { + +STATIC void audioLogicOpenCB2(void *handle, int iserror, struct json_object *result) { struct json_object *response; - + // Make sure we got a response from API struct afb_req request = afb_req_unstore(handle); - if (!cbCheckResponse (request, iserror, result)) goto OnExit + if (!cbCheckResponse(request, iserror, result)) goto OnExit; + + // get client context + AudioLogicCtxT *ctx = afb_req_context_get(request); + if (!ctx) { + afb_req_fail_f(request, "ctx-notfound", "No Client Context HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); + goto OnExit; + } // Get response from object json_object_object_get_ex(result, "response", &response); if (!response) { afb_req_fail_f(request, "response-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); - goto OnExit; - } - - // get client context - AudioLogicCtxT *ctx = afb_req_context_get(request); - if (!ctx) { - afb_req_fail_f(request, "ctx-notfound", "No Client Context HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); - goto OnExit; + goto OnExit; } // extract sounds controls information from received Object @@ -176,97 +170,97 @@ STATIC void audioLogicOpenCB2 (void *handle, int iserror, struct json_object *re json_object_object_get_ex(response, "ctls", &ctls); if (!ctls) { afb_req_fail_f(request, "ctls-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); - goto OnExit; + goto OnExit; } - + // make sure return controls have a valid type - if (json_object_get_type (ctls) != json_type_array ) { + if (json_object_get_type(ctls) != json_type_array) { afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); - goto OnExit; + goto OnExit; } - + // loop on array and store values into client context - for (int idx=0; idx < json_object_array_length (ctls), idx++) { + for (int idx = 0; idx < json_object_array_length(ctls); idx++) { struct json_object *ctl; - halControlEnumT control; + halCtlsEnumT control; int value; - + ctl = json_object_array_get_idx(ctls, idx); - if (json_object_array_length(ctl != 2)) { + if (json_object_array_length(ctl) != 2) { afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from HAL/getcontrol devid=[%] name=[%s] ctl=%s" - , ctx->devid, ctx->shortname, json_object_get_string(ctl)); - goto OnExit; + , ctx->devid, ctx->shortname, json_object_get_string(ctl)); + goto OnExit; } - + // As HAL and Business logic use the same AlsaMixerHal.h direct conversion is not an issue - control = (halControlEnumT)json_object_get_int(json_object_array_get_idx(ctl,0)); - value = json_object_get_int(json_object_array_get_idx(ctl,0)); - - switch(control) { + control = (halCtlsEnumT) json_object_get_int(json_object_array_get_idx(ctl, 0)); + value = json_object_get_int(json_object_array_get_idx(ctl, 1)); + + switch (control) { case Master_Playback_Volume: ctx->volumes.masterPlaybackVolume = value; break; - + case PCM_Playback_Volume: - ctx->volumes.pcmPlaybackVolume = value; + ctx->volumes.pcmPlaybackVolume = value; break; - + case PCM_Playback_Switch: ctx->volumes.pcmPlaybackSwitch = value; break; - + case Capture_Volume: ctx->volumes.captureVolume = value; break; - + default: - NOTICE (afbIface, "audioLogicOpenCB2 unknown HAL control=[%s]", json_object_get_string(ctl)): + NOTICE(afbIface, "audioLogicOpenCB2 unknown HAL control=[%s]", json_object_get_string(ctl)); } } - - OnExit: + +OnExit: afb_req_context_set(request, ctx, free); return; } // This callback is fired when afb_service_call for api/alsacore/subctl returns -STATIC void audioLogicOpenCB1 (void *handle, int iserror, struct json_object *result) { +STATIC void audioLogicOpenCB1(void *handle, int iserror, struct json_object *result) { struct json_object *response, *subobj; - + // Make sure we got a valid API response struct afb_req request = afb_req_unstore(handle); - if (!cbCheckResponse (request, iserror, result)) goto OnExit - + if (!cbCheckResponse(request, iserror, result)) goto OnExit; + // Get response from object json_object_object_get_ex(result, "response", &response); if (!response) { - afb_req_fail_f(request, "response-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); - goto OnExit; + afb_req_fail_f(request, "response-notfound", "No Controls return from HAL/getcontrol"); + goto OnExit; } // attach client context to session - AudioLogicCtxT *ctx = malloc (sizeof(AudioLogicCtxT)); + AudioLogicCtxT *ctx = malloc(sizeof (AudioLogicCtxT)); // extract information from Json Alsa Object json_object_object_get_ex(response, "cardid", &subobj); - if (subobj) ctx->cardid= json_object_get_int(subobj); + if (subobj) ctx->cardid = json_object_get_int(subobj); // store devid as an object for further alsa request json_object_object_get_ex(response, "devid", &subobj); - if (subobj) ctx->devid= strdup(json_object_get_string(subobj)); + if (subobj) ctx->devid = strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "halapi", &subobj); - if (subobj) ctx->halapi= strdup(json_object_get_string(subobj)); + if (subobj) ctx->halapi = strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "shortname", &subobj); - if (subobj)ctx->shortname=strdup(json_object_get_string(subobj)); + if (subobj)ctx->shortname = strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "longname", &subobj); - if (subobj)ctx->longname=strdup(json_object_get_string(subobj)); + if (subobj)ctx->longname = strdup(json_object_get_string(subobj)); // save queryurl with devid only for further ALSA request - ctx->queryurl=json_object_new_object(); - json_object_object_add(ctx->queryurl, "devid",json_object_new_string(ctx->devid)); + ctx->queryurl = json_object_new_object(); + json_object_object_add(ctx->queryurl, "devid", json_object_new_string(ctx->devid)); afb_req_context_set(request, ctx, free); @@ -274,25 +268,25 @@ STATIC void audioLogicOpenCB1 (void *handle, int iserror, struct json_object *re if (!ctx->halapi) { afb_req_fail_f(request, "hal-notfound", "No HAL found devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; - } - - struct json_object *queryurl =json_object_new_object(); - struct json_object *ctls =json_object_new_object(); + } + + struct json_object *queryurl = json_object_new_object(); + struct json_object *ctls = json_object_new_array(); // add sound controls we want to keep track of into client session context - json_object_object_add(ctls, json_object_new_int((int)Master_Playback_Volume)); - json_object_object_add(ctls, json_object_new_int((int)PCM_Playback_Volume)); - json_object_object_add(ctls, json_object_new_int((int)PCM_Playback_Switch)); - json_object_object_add(ctls, json_object_new_int((int)Capture_Volume)); + json_object_array_add(ctls, json_object_new_int((int) Master_Playback_Volume)); + json_object_array_add(ctls, json_object_new_int((int) PCM_Playback_Volume)); + json_object_array_add(ctls, json_object_new_int((int) PCM_Playback_Switch)); + json_object_array_add(ctls, json_object_new_int((int) Capture_Volume)); // send request to soundcard HAL binding - json_object_array_add(queryurl, ctx->queryurl); - json_object_object_add(queryurl, "ctls",ctls); + json_object_object_add(queryurl, "ctls", ctls); + handle = afb_req_store(request); // FULUP ???? Needed for 2nd Callback ???? afb_service_call(srvitf, ctx->halapi, "getControl", queryurl, audioLogicOpenCB2, handle); - + afb_req_success(request, response, NULL); - - OnExit: + +OnExit: // release original calling request afb_req_unref(request); return; @@ -303,21 +297,20 @@ PUBLIC void audioLogicOpen(struct afb_req request) { // Delegate query to lower level struct afb_req *handle = afb_req_store(request); - if (!handle) afb_req_fail(request, "error", "out of memory"); - else afb_service_call(srvitf, "alsacore", "getCardId", json_object_get(afb_req_json(request)), audioLogicOpenCB1, handle); + afb_service_call(srvitf, "alsacore", "getCardId", json_object_get(afb_req_json(request)), audioLogicOpenCB1, handle); } // Free client context create from audioLogicOpenCB -PUBLIC void audioLogicClose (struct afb_req request) { - +PUBLIC void audioLogicClose(struct afb_req request) { + // retrieve current client context to print debug info AudioLogicCtxT *ctx = (AudioLogicCtxT*) afb_req_context_get(request); - DEBUG (afbIface, "audioLogicClose cardid=%d devid=%s shortname=%s longname=%s", ctx->cardid, ctx->devid, ctx->shortname, ctx->longname); + DEBUG(afbIface, "audioLogicClose cardid=%d devid=%s shortname=%s longname=%s", ctx->cardid, ctx->devid, ctx->shortname, ctx->longname); } // this function is call after all binder are loaded and initialised -PUBLIC int audioLogicInit (struct afb_service service) { - srvitf = service; +PUBLIC int audioLogicInit(struct afb_service service) { + srvitf = service; return 0; } diff --git a/AudioLogic/AudioLogicLib.h b/AudioLogic/AudioLogicLib.h index 10e431d..77b8b7b 100644 --- a/AudioLogic/AudioLogicLib.h +++ b/AudioLogic/AudioLogicLib.h @@ -15,30 +15,14 @@ * limitations under the License. */ +#ifndef AUDIOLOGIC_H +#define AUDIOLOGIC_H -// few coding convention -typedef int BOOL; -#ifndef PUBLIC - #define PUBLIC -#endif -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif -#define STATIC static - -#ifndef AUDIOLIBMAPPING_H -#define AUDIOLIBMAPPING_H - +#include "AudioCommonLib.h" #include #include #include -#include "AlsaHalCtls.h" // Alsa Hardware Abstraction Layer Controls - - // import from AlsaAfbBinding extern const struct afb_binding_interface *afbIface; @@ -69,5 +53,5 @@ PUBLIC void audioLogicClose(struct afb_req request); PUBLIC void audioLogicSubscribe(struct afb_req request); PUBLIC int audioLogicInit (struct afb_service service); -#endif /* AUDIOLIBMAPPING_H */ +#endif /* AUDIOLOGIC_H */ diff --git a/CMakeLists.txt b/CMakeLists.txt index 006ff74..0bd639d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,18 +98,21 @@ ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) SET(include_dirs ${INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/AlsaSound/HALayer/include + ${CMAKE_CURRENT_SOURCE_DIR}/Common ${json-c_INCLUDE_DIRS} ${afb-daemon_INCLUDE_DIRS} ) -SET(link_libraries +# AudioCommon is linked statically with each audiobindings +SET(link_libraries + ${CMAKE_BINARY_DIR}/Common/libaudiocommon.a ${libefence_LIBRARIES} ${json-c_LIBRARIES} ) # Bindings to compile # -------------------- +add_subdirectory(Common) add_subdirectory(AlsaSound) add_subdirectory(AudioLogic) add_subdirectory(htdocs) diff --git a/Common/AudioCommonLib.c b/Common/AudioCommonLib.c new file mode 100644 index 0000000..0517e8e --- /dev/null +++ b/Common/AudioCommonLib.c @@ -0,0 +1,101 @@ +/* + * 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. + * + */ +#define _GNU_SOURCE // needed for vasprintf + +#include +#include +#include +#include +#include + +#include "AudioCommonLib.h" + +typedef struct { + int index; + int numid; +} shareHallMap_T; + + +PUBLIC int cbCheckResponse(struct afb_req request, int iserror, struct json_object *result) { + struct json_object *response, *status, *info; + + if (iserror) { // on error proxy information we got from lower layer + if (result) { + if (json_object_object_get_ex(result, "request", &response)) { + json_object_object_get_ex(response, "info", &info); + json_object_object_get_ex(response, "status", &status); + afb_req_fail(request, json_object_get_string(status), json_object_get_string(info)); + goto OnErrorExit; + } + } else { + afb_req_fail(request, "cbCheckFail", "No Result inside API response"); + } + goto OnErrorExit; + } + return (0); + +OnErrorExit: + return (-1); +} + +// This function should be part of Generic AGL Framework +PUBLIC json_object* afb_service_call_sync(struct afb_service srvitf, struct afb_req request, char* api, char* verb, struct json_object* queryurl, void *handle) { + json_object* response = NULL; + int status = 0; + sem_t semid; + + // Nested procedure are allow in GNU and allow us to keep caller stack valid + + void callback(void *handle, int iserror, struct json_object * result) { + + // Process Basic Error + if (!cbCheckResponse(request, iserror, result)) { + status = -1; + goto OnExitCB; + } + + // Get response from object + json_object_object_get_ex(result, "response", &response); + if (!response) { + afb_req_fail_f(request, "response-notfound", "No Controls return from alsa/getcontrol result=[%s]", json_object_get_string(result)); + goto OnExitCB; + } + +OnExitCB: + sem_post(&semid); + } + + // Create an exclusive semaphore + status = sem_init(&semid, 0, 0); + if (status < 0) { + afb_req_fail_f(request, "error:seminit", "Fail to allocate semaphore err=[%s]", strerror(status)); + goto OnExit; + } + + // Call service and wait for call back to finish before moving any further + afb_service_call(srvitf, "alsacore", "getctl", queryurl, callback, handle); + sem_wait(&semid); + +OnExit: + return (response); +} + +PUBLIC void pingtest(struct afb_req request) { + json_object *query = afb_req_json(request); + afb_req_success(request, query, NULL); +} diff --git a/Common/AudioCommonLib.h b/Common/AudioCommonLib.h new file mode 100644 index 0000000..ebee389 --- /dev/null +++ b/Common/AudioCommonLib.h @@ -0,0 +1,68 @@ +/* + * 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. + * + * reference: + * amixer contents; amixer controls; + * http://www.tldp.org/HOWTO/Alsa-sound-6.html + */ + +#ifndef AUDIOCOMMON_H +#define AUDIOCOMMON_H + +#include +#include +#include + +#ifndef PUBLIC + #define PUBLIC +#endif +#define STATIC static + +// Most controls are MIXER but some vendor specific are possible +typedef enum { + OUTVOL, + PCMVOL, + INVOL, + SWITCH, + ROUTE, + CARD, + ENUM, +} halGroupEnumT; + +typedef enum { + READ, + WRITE, + RW, +} halAclEnumT; + +typedef enum { + StartHalCrlTag=0, + + // HighLevel Audio Control List + Master_Playback_Volume, + PCM_Playback_Volume, + PCM_Playback_Switch, + Capture_Volume, + + EndHalCrlTag // used to compute number of ctls +} halCtlsEnumT; + +PUBLIC int cbCheckResponse(struct afb_req request, int iserror, struct json_object *result) ; +PUBLIC json_object* afb_service_call_sync(struct afb_service srvitf, struct afb_req request, char* api, char* verb, struct json_object* queryurl, void *handle); +PUBLIC void pingtest(struct afb_req request); + +#endif /* AUDIOCOMMON_H */ + diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt new file mode 100644 index 0000000..79ee8ac --- /dev/null +++ b/Common/CMakeLists.txt @@ -0,0 +1,31 @@ +########################################################################### +# 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. +########################################################################### + +PROJECT(audio-common C) + +INCLUDE_DIRECTORIES(${include_dirs}) + +################################################## +# AudioCommon is a local static Library +################################################## +ADD_LIBRARY(audiocommon STATIC AudioCommonLib.c) +SET_TARGET_PROPERTIES(audiocommon PROPERTIES OUTPUT_NAME audiocommon) +SET(link_libraries ${libefence_LIBRARIES} ${json-c_LIBRARIES}) +TARGET_LINK_LIBRARIES(audiocommon ${link_libraries}) + + diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index ee7b001..6db5cb9 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -6,7 +6,7 @@ AlsaHalIface.h - MiscHelpers.h + AudioCommonLib.h @@ -65,16 +65,34 @@ true + + + + - + + + + + + + + -- cgit 1.2.3-korg