diff options
Diffstat (limited to 'HAL-afb')
-rw-r--r-- | HAL-afb/CMakeLists.txt | 28 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/CMakeLists.txt | 37 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-interface.c | 341 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-interface.h | 62 | ||||
-rw-r--r-- | HAL-afb/HDA-intel/CMakeLists.txt | 41 | ||||
-rw-r--r-- | HAL-afb/HDA-intel/IntelHdaHAL.c | 68 |
6 files changed, 577 insertions, 0 deletions
diff --git a/HAL-afb/CMakeLists.txt b/HAL-afb/CMakeLists.txt new file mode 100644 index 0000000..c36f530 --- /dev/null +++ b/HAL-afb/CMakeLists.txt @@ -0,0 +1,28 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: 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. +########################################################################### + + +# Include any directory starting with a Capital letter +# ----------------------------------------------------- +file(GLOB filelist "[A-Z]*") +foreach(filename ${filelist}) + if(EXISTS "${filename}/CMakeLists.txt") + add_subdirectory(${filename}) + endif(EXISTS "${filename}/CMakeLists.txt") +endforeach() + diff --git a/HAL-afb/HAL-interface/CMakeLists.txt b/HAL-afb/HAL-interface/CMakeLists.txt new file mode 100644 index 0000000..0ea298f --- /dev/null +++ b/HAL-afb/HAL-interface/CMakeLists.txt @@ -0,0 +1,37 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: 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. +########################################################################### + + +# Add target to project dependency list +PROJECT_TARGET_ADD(hal-interface) + + # Define targets + ADD_LIBRARY(hal-interface STATIC hal-interface.c) + + # Library properties + SET_TARGET_PROPERTIES(hal-interface PROPERTIES OUTPUT_NAME halinterface) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(hal-interface + audio-interface + ) + + # Define target includes + TARGET_INCLUDE_DIRECTORIES(hal-interface + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) diff --git a/HAL-afb/HAL-interface/hal-interface.c b/HAL-afb/HAL-interface/hal-interface.c new file mode 100644 index 0000000..7876715 --- /dev/null +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author 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. + * + * reference: + * amixer contents; amixer controls; + * http://www.tldp.org/HOWTO/Alsa-sound-6.html + */ +#define _GNU_SOURCE // needed for vasprintf +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <fcntl.h> +#include <math.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "hal-interface.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, queryurl); + + 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/HAL-afb/HAL-interface/hal-interface.h b/HAL-afb/HAL-interface/hal-interface.h new file mode 100644 index 0000000..cebbd21 --- /dev/null +++ b/HAL-afb/HAL-interface/hal-interface.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 <stdio.h> +#include "audio-interface.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/HAL-afb/HDA-intel/CMakeLists.txt b/HAL-afb/HDA-intel/CMakeLists.txt new file mode 100644 index 0000000..26be621 --- /dev/null +++ b/HAL-afb/HDA-intel/CMakeLists.txt @@ -0,0 +1,41 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: 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. +########################################################################### + + +# Add target to project dependency list +PROJECT_TARGET_ADD(hal-intel-hda) + + # Define project Targets + ADD_LIBRARY(hal-intel-hda MODULE IntelHdaHAL.c) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(hal-intel-hda PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/export.map" + OUTPUT_NAME intel-hda-binding + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(hal-intel-hda + hal-interface + audio-interface + ) + + # installation directory + INSTALL(TARGETS hal-intel-hda + LIBRARY DESTINATION ${binding_install_dir}) diff --git a/HAL-afb/HDA-intel/IntelHdaHAL.c b/HAL-afb/HDA-intel/IntelHdaHAL.c new file mode 100644 index 0000000..bc8fc43 --- /dev/null +++ b/HAL-afb/HDA-intel/IntelHdaHAL.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author 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. + */ +#define _GNU_SOURCE +#include "hal-interface.h" +#include "audio-interface.h" + +// 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 + * aplay -l + * amixer -D hw:xx controls + * amixer -D hw:xx contents + * amixer -D "hw:3" cget numid=xx + * + * 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 */ +} ; + +/*********************************************************************************** + * 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 alsaHalSndCardT alsaHalSndCard = { + .name = "HDA Intel PCH", + .info = "Hardware Abstraction Layer for IntelHDA sound card", + .ctls = alsaHalMap, + .prefix="intel-hda", + .initCB=IntelHalInit, // if NULL no initcallback +}; |