summaryrefslogtreecommitdiffstats
path: root/4a-hal/4a-internals-hal
diff options
context:
space:
mode:
Diffstat (limited to '4a-hal/4a-internals-hal')
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c633
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.h53
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-api-loader.c243
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-api-loader.h31
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-cb.c790
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-cb.h45
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.c389
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.h70
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-value-handler.c354
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-value-handler.h53
10 files changed, 2661 insertions, 0 deletions
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c b/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c
new file mode 100644
index 0000000..d092ddf
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 <stdio.h>
+#include <string.h>
+
+#include <wrap-json.h>
+
+#include <alsa/asoundlib.h>
+
+#include <afb/afb-binding.h>
+
+#include <ctl-config.h>
+
+#include "4a-hal-utilities-alsa-data.h"
+#include "4a-hal-utilities-data.h"
+
+#include "4a-internals-hal-alsacore-link.h"
+#include "4a-internals-hal-value-handler.h"
+
+/*******************************************************************************
+ * Map to alsa control types *
+ ******************************************************************************/
+
+static const char *const snd_ctl_elem_type_names[] = {
+ [SND_CTL_ELEM_TYPE_NONE]= "NONE",
+ [SND_CTL_ELEM_TYPE_BOOLEAN]= "BOOLEAN",
+ [SND_CTL_ELEM_TYPE_INTEGER]="INTEGER",
+ [SND_CTL_ELEM_TYPE_ENUMERATED]="ENUMERATED",
+ [SND_CTL_ELEM_TYPE_BYTES]="BYTES",
+ [SND_CTL_ELEM_TYPE_IEC958]="IEC958",
+ [SND_CTL_ELEM_TYPE_INTEGER64]="INTEGER64",
+};
+
+/*******************************************************************************
+ * Alsa control types map from string function *
+ ******************************************************************************/
+
+snd_ctl_elem_type_t InternalHalMapsAlsaTypeToEnum(const char *label)
+{
+ int idx;
+ static int length = sizeof(snd_ctl_elem_type_names) / sizeof(char *);
+
+ for(idx = 0; idx < length; idx++) {
+ if(! strcasecmp(label, snd_ctl_elem_type_names[idx]))
+ return (snd_ctl_elem_type_t) idx;
+ }
+
+ return SND_CTL_ELEM_TYPE_NONE;
+}
+
+/*******************************************************************************
+ * Internals HAL - Alsacore calls funtions *
+ ******************************************************************************/
+
+int InternalHalGetCardIdByCardPath(afb_api_t apiHandle, char *devPath)
+{
+ int errorToReturn, cardId;
+
+ char *returnedError = NULL, *returnedInfo = NULL;
+
+ json_object *toSendJ, *responseJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Api handle not available");
+ return -1;
+ }
+
+ if(! devPath) {
+ AFB_API_ERROR(apiHandle, "Dev path is not available");
+ return -2;
+ }
+
+ wrap_json_pack(&toSendJ, "{s:s}", "cardPath", devPath);
+
+ if(afb_api_call_sync(apiHandle,
+ ALSACORE_API,
+ ALSACORE_GETINFO_VERB,
+ toSendJ,
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ ALSACORE_GETINFO_VERB,
+ ALSACORE_API,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ errorToReturn = -3;
+ }
+ else if(! responseJ) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but no response was returned",
+ ALSACORE_GETINFO_VERB,
+ ALSACORE_API);
+ errorToReturn = -4;
+ }
+ else if(! json_object_is_type(responseJ, json_type_object)) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but the returned response is invalid",
+ ALSACORE_GETINFO_VERB,
+ ALSACORE_API);
+ errorToReturn = -5;
+ }
+ else if(wrap_json_unpack(responseJ, "{s:i}", "cardNb", &cardId)) {
+ AFB_API_WARNING(apiHandle, "Response card id is not present/valid");
+ errorToReturn = -6;
+ }
+ else {
+ return cardId;
+ }
+
+ if(responseJ)
+ json_object_put(responseJ);
+
+ free(returnedError);
+ free(returnedInfo);
+
+ return errorToReturn;
+}
+
+int InternalHalSubscribeToAlsaCardEvent(afb_api_t apiHandle, char *cardId)
+{
+ int err = 0;
+
+ char *returnedError = NULL, *returnedInfo = NULL;
+
+ json_object *subscribeQueryJ, *responseJ = NULL;
+
+ wrap_json_pack(&subscribeQueryJ, "{s:s}", "devid", cardId);
+ if(afb_api_call_sync(apiHandle,
+ ALSACORE_API,
+ ALSACORE_SUBSCRIBE_VERB,
+ subscribeQueryJ,
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ ALSACORE_SUBSCRIBE_VERB,
+ ALSACORE_API,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ err = -1;
+ }
+
+ if(responseJ)
+ json_object_put(responseJ);
+
+ free(returnedError);
+ free(returnedInfo);
+
+ return err;
+}
+
+int InternalHalGetAlsaCtlInfo(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *currentAlsaCtl, json_object **returnedDataJ)
+{
+ int err = 0;
+
+ char *returnedError = NULL, *returnedInfo = NULL;
+
+ json_object *queryJ, *responseJ = NULL;
+
+ *returnedDataJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Api handle not available");
+ return -1;
+ }
+
+ if(! cardId) {
+ AFB_API_ERROR(apiHandle, "Card id is not available");
+ return -2;
+ }
+
+ if(! currentAlsaCtl) {
+ AFB_API_ERROR(apiHandle, "Alsa control data structure is not available");
+ return -3;
+ }
+
+ if(currentAlsaCtl->name && currentAlsaCtl->numid > 0) {
+ AFB_API_DEBUG(apiHandle,
+ "Both a control name (%s) and a control uid (%i) are specified, control uid will be used",
+ currentAlsaCtl->name,
+ currentAlsaCtl->numid);
+ }
+
+ if(currentAlsaCtl->numid > 0) {
+ wrap_json_pack(&queryJ, "{s:s s:i s:i}", "devid", cardId, "ctl", currentAlsaCtl->numid, "mode", 3);
+ }
+ else if(currentAlsaCtl->name) {
+ wrap_json_pack(&queryJ, "{s:s s:s s:i}", "devid", cardId, "ctl", currentAlsaCtl->name, "mode", 3);
+ }
+ else {
+ AFB_API_ERROR(apiHandle, "Need at least a control name or a control id");
+ return -4;
+ }
+
+ if(afb_api_call_sync(apiHandle,
+ ALSACORE_API,
+ ALSACORE_CTLGET_VERB,
+ queryJ,
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ ALSACORE_CTLGET_VERB,
+ ALSACORE_API,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ free(returnedError);
+ free(returnedInfo);
+ return -5;
+ }
+ else if(! responseJ) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but no response was returned",
+ ALSACORE_CTLGET_VERB,
+ ALSACORE_API);
+ return -6;
+ }
+ else if(currentAlsaCtl->name && wrap_json_unpack(responseJ, "{s:i}", "id", &currentAlsaCtl->numid)) {
+ AFB_API_ERROR(apiHandle, "Can't find alsa control 'id' from control 'name': '%s' on device '%s'", currentAlsaCtl->name, cardId);
+ json_object_put(responseJ);
+ return -7;
+ }
+
+ *returnedDataJ = responseJ;
+
+ return err;
+}
+
+int InternalHalUpdateAlsaCtlProperties(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *currentAlsaCtl)
+{
+ int err = 0;
+
+ json_object *returnedDataJ;
+
+ err = InternalHalGetAlsaCtlInfo(apiHandle, cardId, currentAlsaCtl, &returnedDataJ);
+ if(err) {
+ return err;
+ }
+ // TBD JAI : get dblinear/dbminmax/... values
+ else if(wrap_json_unpack(returnedDataJ,
+ "{s:{s?:i s?:i s?:i s?:i s?:i}}",
+ "ctl",
+ "type", (int *) &currentAlsaCtl->alsaCtlProperties.type,
+ "count", &currentAlsaCtl->alsaCtlProperties.count,
+ "min", &currentAlsaCtl->alsaCtlProperties.minval,
+ "max", &currentAlsaCtl->alsaCtlProperties.maxval,
+ "step", &currentAlsaCtl->alsaCtlProperties.step)) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to get control %i properties on device '%s' : '%s'",
+ currentAlsaCtl->numid,
+ cardId,
+ json_object_get_string(returnedDataJ));
+
+ err = -8;
+ }
+
+ if(returnedDataJ)
+ json_object_put(returnedDataJ);
+
+ return err;
+}
+
+int InternalHalGetAlsaCtlValues(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *currentAlsaCtl, json_object **returnedValuesJ)
+{
+ int err = 0;
+
+ json_object *returnedDataJ = NULL, *returnedValuesArrayJ;
+
+ *returnedValuesJ = NULL;
+
+ err = InternalHalGetAlsaCtlInfo(apiHandle, cardId, currentAlsaCtl, &returnedDataJ);
+ if(err) {
+ return err;
+ }
+ else if(wrap_json_unpack(returnedDataJ, "{s:o}", "val", &returnedValuesArrayJ)) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to get control %i values on device '%s' : '%s'",
+ currentAlsaCtl->numid,
+ cardId,
+ json_object_get_string(returnedValuesArrayJ));
+
+ err = -8;
+ }
+ else if(! json_object_is_type(returnedValuesArrayJ, json_type_array)) {
+ AFB_API_ERROR(apiHandle,
+ "Json returned by control %i values on device '%s' are not an array ('%s')",
+ currentAlsaCtl->numid,
+ cardId,
+ json_object_get_string(returnedValuesArrayJ));
+
+ err = -9;
+ }
+
+ if(! err)
+ *returnedValuesJ = json_object_get(returnedValuesArrayJ);
+
+ if(returnedDataJ)
+ json_object_put(returnedDataJ);
+
+ return err;
+}
+
+int InternalHalSetAlsaCtlValue(afb_api_t apiHandle, char *cardId, int ctlId, json_object *valuesJ)
+{
+ int err = 0;
+
+ char *returnedError = NULL, *returnedInfo = NULL;
+
+ json_object *queryJ, *responseJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Api handle not available");
+ return -1;
+ }
+
+ if(! cardId) {
+ AFB_API_ERROR(apiHandle, "Card id is not available");
+ return -2;
+ }
+
+ if(ctlId <= 0) {
+ AFB_API_ERROR(apiHandle, "Alsa control id is not valid");
+ return -3;
+ }
+
+ if(! valuesJ) {
+ AFB_API_ERROR(apiHandle, "Values to set json is not available");
+ return -4;
+ }
+
+ wrap_json_pack(&queryJ, "{s:s s:{s:i s:o}}", "devid", cardId, "ctl", "id", ctlId, "val", json_object_get(valuesJ));
+
+ if(afb_api_call_sync(apiHandle,
+ ALSACORE_API,
+ ALSACORE_CTLSET_VERB,
+ queryJ,
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ ALSACORE_CTLSET_VERB,
+ ALSACORE_API,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ err = -5;
+ }
+
+ if(responseJ)
+ json_object_put(responseJ);
+
+ free(returnedError);
+ free(returnedInfo);
+
+ return err;
+}
+
+int InternalHalCreateAlsaCtl(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *alsaCtlToCreate)
+{
+ int err = 0;
+
+ char *returnedError = NULL, *returnedInfo = NULL;
+
+ json_object *queryJ, *responseJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Api handle not available");
+ return -1;
+ }
+
+ if(! cardId) {
+ AFB_API_ERROR(apiHandle, "Card id is not available");
+ return -2;
+ }
+
+ if(! alsaCtlToCreate) {
+ AFB_API_ERROR(apiHandle, "Alsa control data structure is not available");
+ return -3;
+ }
+
+ if(! alsaCtlToCreate->alsaCtlCreation) {
+ AFB_API_ERROR(apiHandle, "Alsa control data for creation structure is not available");
+ return -4;
+ }
+
+ wrap_json_pack(&queryJ, "{s:s s:{s:i s:s s?:i s?:i s?:i s:i s:i}}",
+ "devid", cardId,
+ "ctl",
+ "ctl", -1,
+ "name", alsaCtlToCreate->name,
+ "min", alsaCtlToCreate->alsaCtlCreation->minval,
+ "max", alsaCtlToCreate->alsaCtlCreation->maxval,
+ "step", alsaCtlToCreate->alsaCtlCreation->step,
+ "type", (int) alsaCtlToCreate->alsaCtlCreation->type,
+ "count", alsaCtlToCreate->alsaCtlCreation->count);
+
+ if(afb_api_call_sync(apiHandle,
+ ALSACORE_API,
+ ALSACORE_ADDCTL_VERB,
+ queryJ,
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ ALSACORE_ADDCTL_VERB,
+ ALSACORE_API,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ err = -5;
+ }
+ else if(! responseJ) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but no response was returned",
+ ALSACORE_ADDCTL_VERB,
+ ALSACORE_API);
+ err = -6;
+ }
+ else if(wrap_json_unpack(responseJ, "{s:i}", "id", &alsaCtlToCreate->numid)) {
+ AFB_API_ERROR(apiHandle,
+ "Can't get create id from %s of %s api",
+ ALSACORE_ADDCTL_VERB,
+ ALSACORE_API);
+ err = -7;
+ }
+ else if(wrap_json_unpack(responseJ, "{s:o}", "ctl", NULL)) {
+ AFB_API_WARNING(apiHandle, "Control %s was already present but has been updated", alsaCtlToCreate->name);
+ }
+
+ if(responseJ)
+ json_object_put(responseJ);
+
+ free(returnedError);
+ free(returnedInfo);
+
+ return err;
+}
+
+/*******************************************************************************
+ * Internals HAL - Alsacore controls request callback *
+ ******************************************************************************/
+
+void InternalHalActionOnAlsaCtl(afb_req_t request)
+{
+ char cardIdString[6];
+
+ afb_api_t apiHandle;
+
+ CtlConfigT *ctrlConfig;
+
+ struct HalData *currentHalData;
+ struct InternalHalAlsaMap *currentAlsaCtl;
+
+ json_object *requestJson,
+ *valueJ,
+ *convertedJ,
+ *answerJ,
+ *previousControlValuesJ,
+ *normalizedPreviousControlValuesJ,
+ *appliedControlValuesJ,
+ *normalizedAppliedControlValuesJ;
+
+ if(! (apiHandle = afb_req_get_api(request))) {
+ afb_req_fail(request, "api_handle", "Can't get current hal controller api handle");
+ return;
+ }
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle))) {
+ afb_req_fail(request, "hal_controller_config", "Can't get current hal controller config");
+ return;
+ }
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData) {
+ afb_req_fail(request, "hal_controller_data", "Can't get current hal controller data");
+ return;
+ }
+
+ if(currentHalData->status == HAL_STATUS_UNAVAILABLE) {
+ afb_req_fail(request, "hal_unavailable", "Seems that hal is not available");
+ return;
+ }
+
+ currentAlsaCtl = (struct InternalHalAlsaMap *) afb_req_get_vcbdata(request);
+ if(! currentAlsaCtl) {
+ afb_req_fail(request, "alsa_control_data", "Can't get current alsa control data");
+ return;
+ }
+
+ if(currentAlsaCtl->ctl.numid <= 0) {
+ afb_req_fail(request, "alsa_control_id", "Alsa control id is not valid");
+ return;
+ }
+
+ snprintf(cardIdString, 6, "hw:%i", currentHalData->sndCardId);
+
+ if(InternalHalGetAlsaCtlValues(apiHandle, cardIdString, &currentAlsaCtl->ctl, &previousControlValuesJ)) {
+ afb_req_fail_f(request, "previous_values", "Error when trying to get unchanged alsa control values");
+ return;
+ }
+ else if(InternalHalConvertJsonValues(apiHandle,
+ &currentAlsaCtl->ctl.alsaCtlProperties,
+ previousControlValuesJ,
+ &normalizedPreviousControlValuesJ,
+ CONVERSION_ALSACORE_TO_NORMALIZED)) {
+ afb_req_fail_f(request,
+ "request_json",
+ "Error when trying to normalize unchanged alsa control values json '%s'",
+ json_object_get_string(previousControlValuesJ));
+ json_object_put(previousControlValuesJ);
+ return;
+ }
+
+ if(! (requestJson = afb_req_json(request))) {
+ wrap_json_pack(&answerJ,
+ "{s:o}",
+ "current", normalizedPreviousControlValuesJ);
+ afb_req_success(request, answerJ, "Current controls values");
+ json_object_put(previousControlValuesJ);
+ return;
+ }
+
+ if(! json_object_is_type(requestJson, json_type_object)) {
+ afb_req_fail_f(request, "request_json", "Request json is not valid '%s'", json_object_get_string(requestJson));
+ json_object_put(previousControlValuesJ);
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+
+ if(wrap_json_unpack(requestJson, "{s:o}", "value", &valueJ)) {
+ afb_req_fail_f(request,
+ "request_json", "Error when trying to get request value object inside request '%s'",
+ json_object_get_string(requestJson));
+ json_object_put(previousControlValuesJ);
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+
+ if((! json_object_is_type(valueJ, json_type_string)) &&
+ InternalHalConvertJsonValues(apiHandle,
+ &currentAlsaCtl->ctl.alsaCtlProperties,
+ valueJ,
+ &convertedJ,
+ CONVERSION_NORMALIZED_TO_ALSACORE)) {
+ afb_req_fail_f(request,
+ "request_json",
+ "Error when trying to convert request values '%s'",
+ json_object_get_string(valueJ));
+ json_object_put(previousControlValuesJ);
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+ else if(json_object_is_type(valueJ, json_type_string) &&
+ InternalHalChangePreviousValuesUsingJson(apiHandle,
+ &currentAlsaCtl->ctl.alsaCtlProperties,
+ valueJ,
+ previousControlValuesJ,
+ &convertedJ)) {
+ afb_req_fail_f(request,
+ "previous_values",
+ "Error when trying to generate changed alsa control values (values : '%s', previous :'%s')",
+ json_object_get_string(valueJ),
+ json_object_get_string(previousControlValuesJ));
+ json_object_put(previousControlValuesJ);
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+
+ json_object_put(previousControlValuesJ);
+
+ if(InternalHalSetAlsaCtlValue(apiHandle, cardIdString, currentAlsaCtl->ctl.numid, convertedJ)) {
+ afb_req_fail_f(request,
+ "alsa_control_call_error",
+ "Error while trying to set value on alsa control %i, device '%s', converted message '%s'",
+ currentAlsaCtl->ctl.numid,
+ cardIdString,
+ json_object_get_string(convertedJ));
+ json_object_put(convertedJ);
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+
+ json_object_put(convertedJ);
+
+ if(InternalHalGetAlsaCtlValues(apiHandle, cardIdString, &currentAlsaCtl->ctl, &appliedControlValuesJ)) {
+ afb_req_fail_f(request, "applied_values", "Error when trying to get applied alsa control values");
+ json_object_put(normalizedPreviousControlValuesJ);
+ return;
+ }
+ else if(InternalHalConvertJsonValues(apiHandle,
+ &currentAlsaCtl->ctl.alsaCtlProperties,
+ appliedControlValuesJ,
+ &normalizedAppliedControlValuesJ,
+ CONVERSION_ALSACORE_TO_NORMALIZED)) {
+ afb_req_fail_f(request,
+ "request_json",
+ "Error when trying to normalize applied values json '%s'",
+ json_object_get_string(appliedControlValuesJ));
+ json_object_put(normalizedPreviousControlValuesJ);
+ json_object_put(appliedControlValuesJ);
+ return;
+ }
+
+ json_object_put(appliedControlValuesJ);
+
+ wrap_json_pack(&answerJ,
+ "{s:o, s:o}",
+ "previous", normalizedPreviousControlValuesJ,
+ "current", normalizedAppliedControlValuesJ);
+
+ afb_req_success(request, answerJ, "Values correctly applied on alsa control");
+} \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.h b/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.h
new file mode 100644
index 0000000..065e017
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.h
@@ -0,0 +1,53 @@
+/*
+* Copyright (C) 2018 "IoT.bzh"
+* Author Jonathan Aillet <jonathan.aillet@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 _INTERNALS_HAL_ALSACORE_LINK_INCLUDE_
+#define _INTERNALS_HAL_ALSACORE_LINK_INCLUDE_
+
+#include <stdio.h>
+
+#include <wrap-json.h>
+
+#include <alsa/asoundlib.h>
+
+#include <afb/afb-binding.h>
+
+#include <ctl-config.h>
+
+#include "4a-hal-utilities-alsa-data.h"
+
+#define ALSACORE_API "alsacore"
+#define ALSACORE_SUBSCRIBE_VERB "subscribe"
+#define ALSACORE_GETINFO_VERB "infoget"
+#define ALSACORE_CTLGET_VERB "ctlget"
+#define ALSACORE_CTLSET_VERB "ctlset"
+#define ALSACORE_ADDCTL_VERB "addcustomctl"
+
+// Alsa control types map from string function
+snd_ctl_elem_type_t InternalHalMapsAlsaTypeToEnum(const char *label);
+
+// Internals HAL alsacore calls funtions
+int InternalHalGetCardIdByCardPath(afb_api_t apiHandle, char *devPath);
+int InternalHalSubscribeToAlsaCardEvent(afb_api_t apiHandle, char *cardId);
+int InternalHalUpdateAlsaCtlProperties(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *currentAlsaCtl);
+int InternalHalSetAlsaCtlValue(afb_api_t apiHandle, char *cardId, int ctlId, json_object *valuesJ);
+int InternalHalCreateAlsaCtl(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *alsaCtlToCreate);
+
+// Internals HAL alsacore controls request callback
+void InternalHalActionOnAlsaCtl(afb_req_t request);
+
+#endif /* _INTERNALS_HAL_ALSACORE_LINK_INCLUDE_ */ \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.c b/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.c
new file mode 100644
index 0000000..94891b8
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 <stdio.h>
+#include <string.h>
+
+#include <filescan-utils.h>
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include <ctl-config.h>
+
+#include "4a-hal-utilities-verbs-loader.h"
+#include "4a-internals-hal-api-loader.h"
+#include "4a-internals-hal-alsacore-link.h"
+#include "4a-internals-hal-cb.h"
+#include "4a-internals-hal-mixer-link.h"
+
+// Default api to print log when apihandle not available
+afb_api_t AFB_default;
+
+/*******************************************************************************
+ * Json parsing functions using controller *
+ ******************************************************************************/
+
+// Config Section definition
+static CtlSectionT ctrlSectionsDefault[] =
+{
+ { .key = "resources", .loadCB = PluginConfig },
+ { .key = "halmixer", .loadCB = InternalHalHalMixerConfig },
+ { .key = "onload", .loadCB = OnloadConfig },
+ { .key = "controls", .loadCB = ControlConfig },
+ { .key = "events", .loadCB = EventConfig },
+ { .key = "halmap", .loadCB = InternalHalHalMapConfig },
+ { .key = NULL }
+};
+
+/*******************************************************************************
+ * Dynamic HAL verbs' functions *
+ ******************************************************************************/
+
+// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT
+static afb_verb_t InternalHalApiStaticVerbs[] =
+{
+ /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */
+ { .verb = "info", .callback = InternalHalInfo, .info = "List available streams/playbacks/captures/controls for this api" },
+ { .verb = "subscribe", .callback = InternalHalSubscribe, .info = "Subscribe to event(s) for values changes (streams/playbacks/captures/controls) for this api" },
+ { .verb = "unsubscribe", .callback = InternalHalUnsubscribe, .info = "Unsubscribe to event(s) for values changes (streams/playbacks/captures/controls) for this api" },
+ { .verb = NULL } // Marker for end of the array
+};
+
+/*******************************************************************************
+ * Dynamic API functions for internals hal *
+ ******************************************************************************/
+
+static int InternalHalInitOneApi(afb_api_t apiHandle)
+{
+ CtlConfigT *ctrlConfig;
+ struct HalData *currentHalData;
+
+ if(! apiHandle)
+ return -1;
+
+ // Hugely hack to make all V2 AFB_DEBUG to work in fileutils
+ AFB_default = apiHandle;
+
+ // Retrieve section config from api handle
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle)))
+ return -2;
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData)
+ return -3;
+
+ // Fill HalDatadata structure
+ currentHalData->internal = 1;
+
+ currentHalData->sndCardPath = (char *) ctrlConfig->uid;
+ currentHalData->info = (char *) ctrlConfig->info;
+
+ currentHalData->author = (char *) ctrlConfig->author;
+ currentHalData->version = (char *) ctrlConfig->version;
+ currentHalData->date = (char *) ctrlConfig->date;
+
+ currentHalData->internalHalData->apiHandle = apiHandle;
+ currentHalData->internalHalData->ctrlConfig = ctrlConfig;
+
+ currentHalData->sndCardId = InternalHalGetCardIdByCardPath(apiHandle, currentHalData->sndCardPath);
+
+ currentHalData->internalHalData->streamUpdates = afb_api_make_event(apiHandle, HAL_STREAM_UPDATES_EVENT_NAME);
+ if(! currentHalData->internalHalData->streamUpdates)
+ return -4;
+
+ if(currentHalData->sndCardId < 0)
+ currentHalData->status = HAL_STATUS_UNAVAILABLE;
+ else
+ currentHalData->status = HAL_STATUS_AVAILABLE;
+
+ // TBD JAI: handle refresh of hal status for dynamic card (/dev/by-id)
+
+ return CtlConfigExec(apiHandle, ctrlConfig);
+}
+
+static int InternalHalLoadOneApi(void *cbdata, afb_api_t apiHandle)
+{
+ int err;
+ CtlConfigT *ctrlConfig;
+ CtlSectionT *ctrlCurrentSections;
+
+ if(! cbdata || ! apiHandle)
+ return -1;
+
+ ctrlConfig = (CtlConfigT*) cbdata;
+
+ // Save closure as api's data context
+ afb_api_set_userdata(apiHandle, ctrlConfig);
+
+ // Add static controls verbs
+ if(HalUtlLoadVerbs(apiHandle, InternalHalApiStaticVerbs)) {
+ AFB_API_ERROR(apiHandle, "Load Section : fail to register static V2 verbs");
+ return 1;
+ }
+
+ ctrlCurrentSections = malloc(sizeof(ctrlSectionsDefault));
+ memcpy(ctrlCurrentSections, ctrlSectionsDefault, sizeof(ctrlSectionsDefault));
+
+ // Load section for corresponding Api
+ if((err = CtlLoadSections(apiHandle, ctrlConfig, ctrlCurrentSections)))
+ return err;
+
+ // Declare an event manager for this Api
+ afb_api_on_event(apiHandle, InternalHalDispatchApiEvent);
+
+ // Init Api function (does not receive user closure ???)
+ afb_api_on_init(apiHandle, InternalHalInitOneApi);
+
+ return 0;
+}
+
+int InternalHalCreateApi(afb_api_t apiHandle, char *path, struct HalMgrData *halMgrData)
+{
+ CtlConfigT *ctrlConfig;
+ struct HalData *currentHalData;
+
+ if(! apiHandle || ! path || ! halMgrData)
+ return -1;
+
+ // Create one Api per file
+ ctrlConfig = CtlLoadMetaData(apiHandle, path);
+ if(! ctrlConfig) {
+ AFB_API_ERROR(apiHandle, "No valid control config file in:\n-- %s", path);
+ return -2;
+ }
+
+ if(! ctrlConfig->api) {
+ AFB_API_ERROR(apiHandle, "API Missing from metadata in:\n-- %s", path);
+ return -3;
+ }
+
+ // Allocation of current hal controller data
+ currentHalData = HalUtlAddHalToHalList(&halMgrData->halDataList);
+ if(! currentHalData)
+ return -4;
+
+ currentHalData->apiName = (char *) ctrlConfig->api;
+
+ // Stores hal data in controller config
+ setExternalData(ctrlConfig, (void *) currentHalData);
+
+ // Allocation of the structure that will be used to store internal hal data
+ currentHalData->internalHalData = calloc(1, sizeof(struct InternalHalData));
+
+ // Create one API
+ if(! afb_api_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, InternalHalLoadOneApi, ctrlConfig))
+ return -5;
+
+ return 0;
+}
+
+int InternalHalCreateAllApi(afb_api_t apiHandle, struct HalMgrData *halMgrData)
+{
+ int index, status = 0;
+ char *dirList, *fileName, *fullPath;
+ char filePath[CONTROL_MAXPATH_LEN];
+
+ filePath[CONTROL_MAXPATH_LEN - 1] = '\0';
+
+ json_object *configJ, *entryJ;
+
+ if(! apiHandle || ! halMgrData)
+ return -1;
+
+ // Hugely hack to make all V2 AFB_DEBUG to work in fileutils
+ AFB_default = apiHandle;
+
+ AFB_API_NOTICE(apiHandle, "Begining to create all APIs");
+
+ dirList = getenv("CONTROL_CONFIG_PATH");
+ if(! dirList)
+ dirList = CONTROL_CONFIG_PATH;
+
+ configJ = CtlConfigScan(dirList, "hal");
+ if(! configJ) {
+ AFB_API_WARNING(apiHandle, "No hal-(binder-middle-name)*.json config file(s) found in %s, 4a-hal-manager will only works with external hal", dirList);
+ return 0;
+ }
+
+ // We load 1st file others are just warnings
+ for(index = 0; index < (int) json_object_array_length(configJ); index++) {
+ entryJ = json_object_array_get_idx(configJ, index);
+
+ if(wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullPath, "filename", &fileName)) {
+ AFB_API_ERROR(apiHandle, "HOOPs invalid JSON entry = %s", json_object_get_string(entryJ));
+ return -2;
+ }
+
+ strncpy(filePath, fullPath, sizeof(filePath) - 1);
+ strncat(filePath, "/", sizeof(filePath) - 1);
+ strncat(filePath, fileName, sizeof(filePath) - 1);
+
+ if(InternalHalCreateApi(apiHandle, filePath, halMgrData) < 0)
+ status--;
+ }
+
+ return status;
+} \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.h b/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.h
new file mode 100644
index 0000000..fa878e2
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-api-loader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 _INTERNALS_HAL_API_LOADER_INCLUDE_
+#define _INTERNALS_HAL_API_LOADER_INCLUDE_
+
+#include <stdio.h>
+
+#include <afb/afb-binding.h>
+
+#include "4a-hal-utilities-data.h"
+
+// Verbs that can be use to create api
+int InternalHalCreateApi(afb_api_t apiHandle, char *path, struct HalMgrData *halMgrData);
+int InternalHalCreateAllApi(afb_api_t apiHandle, struct HalMgrData *halMgrData);
+
+#endif /* _INTERNALS_HAL_API_LOADER_INCLUDE_ */ \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-cb.c b/4a-hal/4a-internals-hal/4a-internals-hal-cb.c
new file mode 100644
index 0000000..0102d8d
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-cb.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 <stdio.h>
+#include <string.h>
+
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include "4a-hal-utilities-data.h"
+
+#include "4a-internals-hal-cb.h"
+#include "4a-internals-hal-alsacore-link.h"
+#include "4a-internals-hal-mixer-link.h"
+#include "4a-internals-hal-value-handler.h"
+
+/*******************************************************************************
+ * Internals HAL Event handler function *
+ ******************************************************************************/
+
+void InternalHalDispatchApiEvent(afb_api_t apiHandle, const char *evtLabel, json_object *eventJ)
+{
+ int numid, idx = 0, cardidx;
+
+ CtlConfigT *ctrlConfig;
+ CtlSourceT source;
+
+ struct HalData *currentHalData;
+ struct InternalHalAlsaMapT *currentHalAlsaCtlsT;
+
+ json_object *valuesJ, *normalizedValuesJ;
+
+ AFB_API_DEBUG(apiHandle, "Evtname=%s [msg=%s]", evtLabel, json_object_get_string(eventJ));
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle))) {
+ AFB_API_ERROR(apiHandle, "Can't get current internal hal controller config");
+ return;
+ }
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData) {
+ AFB_API_WARNING(apiHandle, "Can't get current internal hal controller data");
+ return;
+ }
+
+ // Extract sound card index from event
+ while(evtLabel[idx] != '\0' && evtLabel[idx] != ':')
+ idx++;
+
+ if(evtLabel[idx] != '\0' &&
+ sscanf(&evtLabel[idx + 1], "%d", &cardidx) == 1 &&
+ currentHalData->sndCardId == cardidx) {
+ if(wrap_json_unpack(eventJ, "{s:i s:o !}", "id", &numid, "val", &valuesJ)) {
+ AFB_API_ERROR(apiHandle, "Invalid Alsa Event label=%s value=%s", evtLabel, json_object_get_string(eventJ));
+ return;
+ }
+
+ currentHalAlsaCtlsT = currentHalData->internalHalData->alsaMapT;
+
+ // Search for corresponding numid in halCtls, if found, launch callback (if available)
+ for(idx = 0; idx < currentHalAlsaCtlsT->ctlsCount; idx++) {
+ if(currentHalAlsaCtlsT->ctls[idx].ctl.numid == numid) {
+ if(currentHalAlsaCtlsT->ctls[idx].action) {
+ source.uid = currentHalAlsaCtlsT->ctls[idx].action->uid;
+ source.api = currentHalAlsaCtlsT->ctls[idx].action->api;
+ source.request = NULL;
+
+ (void) ActionExecOne(&source, currentHalAlsaCtlsT->ctls[idx].action, valuesJ);
+ }
+ else {
+ AFB_API_NOTICE(apiHandle,
+ "The alsa control id '%i' is corresponding to a known control but without any action registered",
+ numid);
+ }
+
+ if((! currentHalAlsaCtlsT->ctls[idx].alsaControlEvent) ||
+ InternalHalConvertJsonValues(apiHandle,
+ &currentHalAlsaCtlsT->ctls[idx].ctl.alsaCtlProperties,
+ valuesJ,
+ &normalizedValuesJ,
+ CONVERSION_ALSACORE_TO_NORMALIZED) ||
+ (afb_event_push(currentHalAlsaCtlsT->ctls[idx].alsaControlEvent, normalizedValuesJ) < 0)) {
+ AFB_API_ERROR(apiHandle,
+ "Couldn't generate an event for known halmap %s (alsa control id %i)",
+ currentHalAlsaCtlsT->ctls[idx].uid,
+ currentHalAlsaCtlsT->ctls[idx].ctl.numid);
+ }
+
+ return;
+ }
+ }
+
+ AFB_API_WARNING(apiHandle,
+ "Alsacore event with an unrecognized numid: %i, evtname=%s [msg=%s]",
+ numid,
+ evtLabel,
+ json_object_get_string(eventJ));
+
+ return;
+ }
+
+ AFB_API_INFO(apiHandle,
+ "Not an alsacore event '%s' [msg=%s]",
+ evtLabel,
+ json_object_get_string(eventJ));
+
+ CtrlDispatchApiEvent(apiHandle, evtLabel, eventJ);
+}
+
+/*******************************************************************************
+ * Internals HAL - 'halmixer' section parsing/handling functions *
+ ******************************************************************************/
+
+int InternalHalHalMixerConfig(afb_api_t apiHandle, CtlSectionT *section, json_object *MixerJ)
+{
+ int err = 0;
+
+ CtlConfigT *ctrlConfig;
+ struct HalData *currentHalData;
+
+ if(! apiHandle || ! section)
+ return -1;
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle)))
+ return -2;
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData)
+ return -3;
+
+ if(MixerJ) {
+ if(json_object_is_type(MixerJ, json_type_object))
+ currentHalData->internalHalData->halMixerJ = MixerJ;
+ else
+ return -4;
+
+ if(wrap_json_unpack(MixerJ, "{s:s}", "mixerapi", &currentHalData->internalHalData->mixerApiName))
+ return -5;
+
+ wrap_json_unpack(MixerJ, "{s?:s}", "prefix", &currentHalData->internalHalData->prefix);
+ }
+ else if(currentHalData->status == HAL_STATUS_AVAILABLE) {
+ err = InternalHalAttachToMixer(apiHandle);
+ if(err) {
+ AFB_API_ERROR(apiHandle, "Error %i while attaching to mixer", err);
+ return -6;
+ }
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
+ * Internals HAL - 'halmap' section parsing/handling functions *
+ ******************************************************************************/
+
+int InternalHalProcessOneHalMapObject(afb_api_t apiHandle, struct InternalHalAlsaMap *alsaMap, json_object *AlsaMapJ)
+{
+ char *action = NULL, *typename = NULL;
+
+ json_object *alsaJ = NULL, *createAlsaCtlJ = NULL;
+
+ AFB_API_DEBUG(apiHandle, "AlsaMapJ=%s", json_object_get_string(AlsaMapJ));
+
+ if(wrap_json_unpack(AlsaMapJ, "{s:s s?:s s:o s?:s !}",
+ "uid", &alsaMap->uid,
+ "info", &alsaMap->info,
+ "alsa", &alsaJ,
+ "action", &action)) {
+ AFB_API_ERROR(apiHandle, "Parsing error, map should only contains [label]|[uid]|[tag]|[info]|[alsa]|[action] in:\n-- %s", json_object_get_string(AlsaMapJ));
+ return -1;
+ }
+
+ if(wrap_json_unpack(alsaJ, "{s?:s s?:i s?:i s?:o !}",
+ "name", &alsaMap->ctl.name,
+ "numid", &alsaMap->ctl.numid,
+ "value", &alsaMap->ctl.value,
+ "create", &createAlsaCtlJ)) {
+ AFB_API_ERROR(apiHandle, "Parsing error, alsa json should only contains [name]|[numid]||[value]|[create] in:\n-- %s", json_object_get_string(alsaJ));
+ return -2;
+ }
+
+ if(createAlsaCtlJ) {
+ alsaMap->ctl.alsaCtlCreation = &alsaMap->ctl.alsaCtlProperties;
+
+ if(wrap_json_unpack(createAlsaCtlJ,
+ "{s:s s:i s:i s:i s:i !}",
+ "type", &typename,
+ "count", &alsaMap->ctl.alsaCtlCreation->count,
+ "minval", &alsaMap->ctl.alsaCtlCreation->minval,
+ "maxval", &alsaMap->ctl.alsaCtlCreation->maxval,
+ "step", &alsaMap->ctl.alsaCtlCreation->step)) {
+ AFB_API_ERROR(apiHandle, "Parsing error, alsa creation json should only contains [type]|[count]|[minval]|[maxval]|[step] in:\n-- %s", json_object_get_string(alsaJ));
+ return -3;
+ }
+
+ if(typename) {
+ alsaMap->ctl.alsaCtlCreation->type = InternalHalMapsAlsaTypeToEnum(typename);
+ if(alsaMap->ctl.alsaCtlCreation->type == SND_CTL_ELEM_TYPE_NONE) {
+ AFB_API_ERROR(apiHandle, "Couldn't get alsa type from string %s in:\n-- %s", typename, json_object_get_string(alsaJ));
+ return -4;
+ }
+ }
+
+ if(! alsaMap->ctl.name)
+ alsaMap->ctl.name = (char *) alsaMap->uid;
+ }
+ else if(alsaMap->ctl.name && alsaMap->ctl.numid > 0) {
+ AFB_API_ERROR(apiHandle,
+ "Can't have both a control name (%s) and a control uid (%i) in alsa object:\n-- %s",
+ alsaMap->ctl.name,
+ alsaMap->ctl.numid,
+ json_object_get_string(alsaJ));
+ return -5;
+ }
+ else if(! alsaMap->ctl.name && alsaMap->ctl.numid <= 0) {
+ AFB_API_ERROR(apiHandle,
+ "Need at least a control name or a control uid in alsa object:\n-- %s",
+ json_object_get_string(alsaJ));
+ return -6;
+ }
+
+ if(action)
+ alsaMap->actionJ = AlsaMapJ;
+
+ return 0;
+}
+
+int InternalHalHandleOneHalMapObject(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaMap *alsaMap)
+{
+ int err;
+
+ json_object *valueJ, *convertedValueJ = NULL;
+
+ if(! (alsaMap->alsaControlEvent = afb_api_make_event(apiHandle, alsaMap->uid))) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to create event for current alsa control to load action using alsa object:\n-- %s",
+ json_object_get_string(alsaMap->actionJ));
+ return -1;
+ }
+
+ if(alsaMap->ctl.alsaCtlCreation) {
+ if(InternalHalCreateAlsaCtl(apiHandle, cardId, &alsaMap->ctl)) {
+ AFB_API_ERROR(apiHandle, "An error happened when trying to create a new alsa control");
+ return -2;
+ }
+ }
+ else if(InternalHalUpdateAlsaCtlProperties(apiHandle, cardId, &alsaMap->ctl)) {
+ AFB_API_ERROR(apiHandle, "An error happened when trying to get existing alsa control info");
+ return -3;
+ }
+
+ if(alsaMap->ctl.value) {
+ // TBD JAI : handle alsa controls type
+ valueJ = json_object_new_int(alsaMap->ctl.value);
+ err = 0;
+
+ if(InternalHalConvertJsonValues(apiHandle, &alsaMap->ctl.alsaCtlProperties, valueJ, &convertedValueJ, CONVERSION_NORMALIZED_TO_ALSACORE)) {
+ AFB_API_ERROR(apiHandle, "Error when trying to convert initiate value json '%s'", json_object_get_string(valueJ));
+ err = -4;
+ }
+ else if(InternalHalSetAlsaCtlValue(apiHandle, cardId, alsaMap->ctl.numid, convertedValueJ)) {
+ AFB_API_ERROR(apiHandle,
+ "Error while trying to set initial value on alsa control %i, device '%s', value '%s'",
+ alsaMap->ctl.numid,
+ cardId,
+ json_object_get_string(valueJ));
+ err = -5;
+ }
+
+ json_object_put(valueJ);
+
+ if(convertedValueJ)
+ json_object_put(convertedValueJ);
+
+ if(err)
+ return err;
+ }
+
+ if(alsaMap->actionJ) {
+ alsaMap->action = calloc(1, sizeof(CtlActionT));
+ if(ActionLoadOne(apiHandle, alsaMap->action, alsaMap->actionJ, 0)) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to load action using alsa object:\n-- %s",
+ json_object_get_string(alsaMap->actionJ));
+ return -6;
+ }
+ }
+
+ if(afb_api_add_verb(apiHandle, alsaMap->uid, alsaMap->info, InternalHalActionOnAlsaCtl, (void *) alsaMap, NULL, 0, 0)) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to create verb for current alsa control to load action using alsa object:\n-- %s",
+ json_object_get_string(alsaMap->actionJ));
+ return -7;
+ }
+
+ return 0;
+}
+
+int InternalHalProcessAllHalMap(afb_api_t apiHandle, json_object *AlsaMapJ, struct InternalHalAlsaMapT *currentInternalHalAlsaMapT)
+{
+ int idx, err = 0;
+
+ struct InternalHalAlsaMap *ctlMaps;
+
+ json_type alsaMapType;
+
+ alsaMapType = json_object_get_type(AlsaMapJ);
+ switch(alsaMapType) {
+ case json_type_array:
+ currentInternalHalAlsaMapT->ctlsCount = (unsigned int) json_object_array_length(AlsaMapJ);
+ break;
+
+ case json_type_object:
+ currentInternalHalAlsaMapT->ctlsCount = 1;
+ break;
+
+ default:
+ currentInternalHalAlsaMapT->ctlsCount = 0;
+ currentInternalHalAlsaMapT->ctls = NULL;
+ AFB_API_WARNING(apiHandle, "Couldn't get content of 'halmap' section in : '%s'", json_object_get_string(AlsaMapJ));
+ return -1;
+ }
+
+ ctlMaps = calloc(currentInternalHalAlsaMapT->ctlsCount, sizeof(struct InternalHalAlsaMap));
+
+ for(idx = 0; idx < currentInternalHalAlsaMapT->ctlsCount; idx++)
+ err += InternalHalProcessOneHalMapObject(apiHandle, &ctlMaps[idx], alsaMapType == json_type_array ? json_object_array_get_idx(AlsaMapJ, idx) : AlsaMapJ);
+
+ currentInternalHalAlsaMapT->ctls = ctlMaps;
+
+ return err;
+}
+
+int InternalHalHandleAllHalMap(afb_api_t apiHandle, int sndCardId, struct InternalHalAlsaMapT *currentInternalHalAlsaMapT)
+{
+ int idx, err = 0;
+
+ char cardIdString[6];
+
+ snprintf(cardIdString, 6, "hw:%i", sndCardId);
+
+ InternalHalSubscribeToAlsaCardEvent(apiHandle, cardIdString);
+
+ for(idx = 0; idx < currentInternalHalAlsaMapT->ctlsCount; idx++)
+ err += InternalHalHandleOneHalMapObject(apiHandle, cardIdString, &currentInternalHalAlsaMapT->ctls[idx]);
+
+ return err;
+}
+
+int InternalHalHalMapConfig(afb_api_t apiHandle, CtlSectionT *section, json_object *AlsaMapJ)
+{
+ CtlConfigT *ctrlConfig;
+ struct HalData *currentHalData;
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle)))
+ return -1;
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData)
+ return -2;
+
+ if(AlsaMapJ) {
+ currentHalData->internalHalData->alsaMapT = calloc(1, sizeof(struct InternalHalAlsaMapT));
+
+ if(InternalHalProcessAllHalMap(apiHandle, AlsaMapJ, currentHalData->internalHalData->alsaMapT)) {
+ AFB_API_ERROR(apiHandle, "Failed to process 'halmap' section");
+ return -3;
+ }
+ }
+ else if(currentHalData->status == HAL_STATUS_UNAVAILABLE) {
+ AFB_API_WARNING(apiHandle, "Hal is unavailable, 'halmap' section data can't be handle");
+ return 1;
+ }
+ else if(currentHalData->sndCardId < 0) {
+ AFB_API_ERROR(apiHandle, "Hal alsa card id is not valid, 'halmap' section data can't be handle");
+ return -6;
+ }
+ else if(! currentHalData->internalHalData->alsaMapT) {
+ AFB_API_WARNING(apiHandle, "'halmap' section data is empty");
+ return 2;
+ }
+ else if(! (currentHalData->internalHalData->alsaMapT->ctlsCount > 0)) {
+ AFB_API_WARNING(apiHandle, "No alsa controls defined in 'halmap' section");
+ return 3;
+ }
+ else if(InternalHalHandleAllHalMap(apiHandle, currentHalData->sndCardId, currentHalData->internalHalData->alsaMapT)) {
+ AFB_API_ERROR(apiHandle, "Failed to handle 'halmap' section");
+ return -9;
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
+ * Internals HAL verbs functions *
+ ******************************************************************************/
+
+json_object *InternalHalGetJsonArrayForMixerDataTable(afb_api_t apiHandle,
+ struct InternalHalMixerData **mixerDataList,
+ enum MixerDataType dataType)
+{
+ json_object *mixerDataArrayJ, *currentMixerDataJ;
+
+ struct InternalHalMixerData *currentMixerData;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Api handle is not valid");
+ return NULL;
+ }
+
+ mixerDataArrayJ = json_object_new_array();
+ if(! mixerDataArrayJ) {
+ AFB_API_ERROR(apiHandle, "Can't generate json mixer data array");
+ return NULL;
+ }
+
+ currentMixerData = *mixerDataList;
+
+ while(currentMixerData) {
+ switch(dataType) {
+ case MIXER_DATA_STREAMS:
+ wrap_json_pack(&currentMixerDataJ,
+ "{s:s s:s}",
+ "name", currentMixerData->verb,
+ "cardId", currentMixerData->streamCardId);
+ break;
+
+ case MIXER_DATA_PLAYBACKS:
+ case MIXER_DATA_CAPTURES :
+ wrap_json_pack(&currentMixerDataJ,
+ "{s:s s:s}",
+ "name", currentMixerData->verb,
+ "mixer-name", currentMixerData->verbToCall,
+ "uid", currentMixerData->streamCardId ? currentMixerData->streamCardId : "none");
+ break;
+
+ default:
+ json_object_put(mixerDataArrayJ);
+ return NULL;
+ }
+ json_object_array_add(mixerDataArrayJ, currentMixerDataJ);
+
+ currentMixerData = currentMixerData->next;
+ }
+
+ return mixerDataArrayJ;
+}
+
+json_object *InternalHalGetJsonArrayForControls(afb_api_t apiHandle, struct InternalHalAlsaMapT *currentAlsaMapDataT)
+{
+ unsigned int idx;
+
+ json_object *alsaMapDataArray, *currentAlsaMapData;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Can't get current internal hal api handle");
+ return NULL;
+ }
+
+ if(! currentAlsaMapDataT) {
+ AFB_API_ERROR(apiHandle, "Can't get Alsa map data table to handle");
+ return NULL;
+ }
+
+ if(! (alsaMapDataArray = json_object_new_array())) {
+ AFB_API_ERROR(apiHandle, "Can't generate json mixer data array");
+ return NULL;
+ }
+
+ for(idx = 0; idx < currentAlsaMapDataT->ctlsCount; idx++) {
+ wrap_json_pack(&currentAlsaMapData,
+ "{s:s s:s}",
+ "name", currentAlsaMapDataT->ctls[idx].uid,
+ "info", currentAlsaMapDataT->ctls[idx].info ? currentAlsaMapDataT->ctls[idx].info : "none");
+
+ json_object_array_add(alsaMapDataArray, currentAlsaMapData);
+ }
+
+ return alsaMapDataArray;
+}
+
+void InternalHalInfo(afb_req_t request)
+{
+ char *apiToCall, *returnedError = NULL, *returnedInfo = NULL;
+
+ afb_api_t apiHandle;
+ CtlConfigT *ctrlConfig;
+
+ struct HalData *currentHalData;
+
+ json_object *requestJson, *toReturnJ = NULL, *requestAnswer, *streamsArray, *playbacksArray, *capturesArray, *controlsArray;
+
+ if(! (apiHandle = afb_req_get_api(request))) {
+ afb_req_fail(request, "api_handle", "Can't get current internal hal api handle");
+ return;
+ }
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle))) {
+ afb_req_fail(request, "hal_controller_config", "Can't get current internal hal controller config");
+ return;
+ }
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData) {
+ afb_req_fail(request, "hal_controller_data", "Can't get current internal hal controller data");
+ return;
+ }
+
+ if(! (requestJson = afb_req_json(request))) {
+ AFB_REQ_NOTICE(request, "Can't get request json");
+ }
+ else if(json_object_is_type(requestJson, json_type_object) && json_object_get_object(requestJson)->count > 0) {
+ apiToCall = currentHalData->internalHalData->mixerApiName;
+ if(! apiToCall) {
+ afb_req_fail(request, "mixer_api", "Can't get mixer api");
+ return;
+ }
+
+ if(InternalHalGetInfoFromMixer(apiHandle, apiToCall, requestJson, &toReturnJ, &returnedError, &returnedInfo)) {
+ afb_req_fail_f(request,
+ "mixer_info",
+ "Call to mixer info verb didn't succeed with status '%s' and info '%s'",
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ return;
+ }
+
+ afb_req_success(request, toReturnJ, "Mixer requested data");
+ return;
+ }
+
+ streamsArray = InternalHalGetJsonArrayForMixerDataTable(apiHandle,
+ &currentHalData->internalHalData->streamsData,
+ MIXER_DATA_STREAMS);
+ if(! streamsArray) {
+ afb_req_fail(request, "streams_data", "Didn't succeed to generate streams data array");
+ return;
+ }
+
+ playbacksArray = InternalHalGetJsonArrayForMixerDataTable(apiHandle,
+ &currentHalData->internalHalData->playbacksData,
+ MIXER_DATA_PLAYBACKS);
+ if(! playbacksArray) {
+ afb_req_fail(request, "playbacks_data", "Didn't succeed to generate playbacks data array");
+ return;
+ }
+
+ capturesArray = InternalHalGetJsonArrayForMixerDataTable(apiHandle,
+ &currentHalData->internalHalData->capturesData,
+ MIXER_DATA_CAPTURES);
+ if(! capturesArray) {
+ afb_req_fail(request, "captures_data", "Didn't succeed to generate captures data array");
+ return;
+ }
+
+ controlsArray = InternalHalGetJsonArrayForControls(apiHandle,
+ currentHalData->internalHalData->alsaMapT);
+ if(! controlsArray) {
+ afb_req_fail(request, "controls_data", "Didn't succeed to generate controls data array");
+ return;
+ }
+
+ wrap_json_pack(&requestAnswer,
+ "{s:o s:o s:o s:o}",
+ "streams", streamsArray,
+ "playbacks", playbacksArray,
+ "captures", capturesArray,
+ "controls", controlsArray);
+
+ afb_req_success(request, requestAnswer, "Requested data");
+}
+
+void InternalHalSubscribeUnsubscribe(afb_req_t request, enum SubscribeUnsubscribeType subscribeUnsubscribeType)
+{
+ int arrayIdx, searchIdx, count, subscriptionFound, subscriptionDoneNb = 0;
+
+ char *currentSubscriptionString;
+
+ afb_api_t apiHandle;
+ CtlConfigT *ctrlConfig;
+
+ struct HalData *currentHalData;
+ struct InternalHalMixerData *currentStreamData;
+ struct InternalHalAlsaMapT *InternalHalAlsaMapT;
+
+ json_object *requestJson, *requestedSubscriptionsJ, *requestedSubscriptionJ = NULL;
+ json_type requestJsonType;
+
+ if(! (apiHandle = afb_req_get_api(request))) {
+ afb_req_fail(request, "api_handle", "Can't get current internal hal api handle");
+ return;
+ }
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle))) {
+ afb_req_fail(request, "hal_controller_config", "Can't get current internal hal controller config");
+ return;
+ }
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData) {
+ afb_req_fail(request, "hal_controller_data", "Can't get current internal hal controller data");
+ return;
+ }
+
+ if(! currentHalData->internalHalData) {
+ afb_req_fail(request, "hal_controller_data", "Current internal hal data is not valid");
+ return;
+ }
+
+ InternalHalAlsaMapT = currentHalData->internalHalData->alsaMapT;
+
+ if(! (requestJson = afb_req_json(request))) {
+ afb_req_fail(request, "request_json", "Can't get request json");
+ return;
+ }
+
+ if(wrap_json_unpack(requestJson, "{s:o}", "events", &requestedSubscriptionsJ)) {
+ afb_req_fail(request, "request_json", "Request json invalid");
+ return;
+ }
+
+ requestJsonType = json_object_get_type(requestedSubscriptionsJ);
+ switch(requestJsonType) {
+ case json_type_string:
+ count = 1;
+ requestedSubscriptionJ = requestedSubscriptionsJ;
+ break;
+
+ case json_type_array:
+ count = (int) json_object_array_length(requestedSubscriptionsJ);
+ break;
+
+ default:
+ afb_req_fail(request, "request_json", "Request json invalid");
+ return;
+ }
+
+ for(arrayIdx = 0; arrayIdx < count; arrayIdx++) {
+ if(requestJsonType == json_type_array) {
+ requestedSubscriptionJ = json_object_array_get_idx(requestedSubscriptionsJ, arrayIdx);
+ if(! json_object_is_type(requestedSubscriptionJ, json_type_string)) {
+ afb_req_fail_f(request, "request_json", "Request json number %i in array invalid", arrayIdx);
+ return;
+ }
+ }
+
+ subscriptionFound = 0;
+ currentSubscriptionString = (char *) json_object_get_string(requestedSubscriptionJ);
+
+ if(! strcasecmp(currentSubscriptionString, HAL_STREAM_UPDATES_EVENT_NAME)) {
+ if(currentHalData->internalHalData->streamUpdates &&
+ subscribeUnsubscribeType == SUBSCRIPTION &&
+ afb_req_subscribe(request, currentHalData->internalHalData->streamUpdates)) {
+ afb_req_fail_f(request,
+ "request_stream_list_updates_event",
+ "Error while trying to subscribe to stream list updates event");
+ return;
+ }
+ else if(currentHalData->internalHalData->streamUpdates &&
+ subscribeUnsubscribeType == UNSUBSCRIPTION &&
+ afb_req_unsubscribe(request, currentHalData->internalHalData->streamUpdates)) {
+ afb_req_fail_f(request,
+ "request_stream_list_updates_event",
+ "Error while trying to unsubscribe to stream list updates event");
+ return;
+ }
+
+ subscriptionFound = 1;
+ subscriptionDoneNb++;
+ }
+
+ currentStreamData = currentHalData->internalHalData->streamsData;
+ while(currentStreamData &&
+ (! subscriptionFound)) {
+ if(! strcasecmp(currentSubscriptionString, currentStreamData->verb)) {
+ if(currentStreamData->event &&
+ subscribeUnsubscribeType == SUBSCRIPTION &&
+ afb_req_subscribe(request, currentStreamData->event)) {
+ afb_req_fail_f(request,
+ "request_stream_event",
+ "Error while trying to subscribe to %s stream events",
+ currentStreamData->verb);
+ return;
+ }
+ else if(currentStreamData->event &&
+ subscribeUnsubscribeType == UNSUBSCRIPTION &&
+ afb_req_unsubscribe(request, currentStreamData->event)) {
+ afb_req_fail_f(request,
+ "request_stream_event",
+ "Error while trying to unsubscribe to %s stream events",
+ currentStreamData->verb);
+ return;
+ }
+
+ subscriptionFound = 1;
+ subscriptionDoneNb++;
+
+ break;
+ }
+
+ currentStreamData = currentStreamData->next;
+ }
+
+ searchIdx = 0;
+ while((searchIdx < (InternalHalAlsaMapT ? InternalHalAlsaMapT->ctlsCount : 0)) &&
+ (! subscriptionFound)) {
+ if(! strcasecmp(currentSubscriptionString, InternalHalAlsaMapT->ctls[searchIdx].uid)) {
+ if(InternalHalAlsaMapT->ctls[searchIdx].alsaControlEvent &&
+ subscribeUnsubscribeType == SUBSCRIPTION &&
+ afb_req_subscribe(request, InternalHalAlsaMapT->ctls[searchIdx].alsaControlEvent)) {
+ afb_req_fail_f(request,
+ "request_control_event",
+ "Error while trying to subscribe to %s halmap controls events",
+ InternalHalAlsaMapT->ctls[searchIdx].uid);
+ return;
+ }
+ else if(InternalHalAlsaMapT->ctls[searchIdx].alsaControlEvent &&
+ subscribeUnsubscribeType == UNSUBSCRIPTION &&
+ afb_req_unsubscribe(request, InternalHalAlsaMapT->ctls[searchIdx].alsaControlEvent)) {
+ afb_req_fail_f(request,
+ "request_stream_event",
+ "Error while trying to unsubscribe to %s halmap controls events",
+ InternalHalAlsaMapT->ctls[searchIdx].uid);
+ return;
+ }
+
+ subscriptionFound = 1;
+ subscriptionDoneNb++;
+
+ break;
+ }
+
+ searchIdx++;
+ }
+ }
+
+ if(subscriptionDoneNb == 0)
+ afb_req_fail_f(request,
+ "events_not_found",
+ "%s failed, event(s) were not found",
+ subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription");
+ if(subscriptionDoneNb == count)
+ afb_req_success_f(request,
+ json_object_new_int(subscriptionDoneNb),
+ "%s succeed for all the %i events requested",
+ subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription",
+ subscriptionDoneNb);
+ else if(subscriptionDoneNb < count)
+ afb_req_success_f(request,
+ json_object_new_int(subscriptionDoneNb),
+ "%s succeed but only to %i events requested out of %i",
+ subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription",
+ subscriptionDoneNb,
+ count);
+ else
+ afb_req_success_f(request,
+ json_object_new_int(subscriptionDoneNb),
+ "%s succeed but to more events than requested (%i out of %i)",
+ subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription",
+ subscriptionDoneNb,
+ count);
+}
+
+void InternalHalSubscribe(afb_req_t request)
+{
+ InternalHalSubscribeUnsubscribe(request, SUBSCRIPTION);
+}
+
+void InternalHalUnsubscribe(afb_req_t request)
+{
+ InternalHalSubscribeUnsubscribe(request, UNSUBSCRIPTION);
+}
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-cb.h b/4a-hal/4a-internals-hal/4a-internals-hal-cb.h
new file mode 100644
index 0000000..ff35656
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-cb.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 _INTERNALS_HAL_CB_INCLUDE_
+#define _INTERNALS_HAL_CB_INCLUDE_
+
+#include <stdio.h>
+
+#include <afb/afb-binding.h>
+
+#include <ctl-config.h>
+
+// Enum for the type of subscription/subscription
+enum SubscribeUnsubscribeType {
+ SUBSCRIPTION = 1,
+ UNSUBSCRIPTION = 2
+};
+
+// Internals HAL event handler function
+void InternalHalDispatchApiEvent(afb_api_t apiHandle, const char *evtLabel, json_object *eventJ);
+
+// Internals HAL sections parsing functions
+int InternalHalHalMixerConfig(afb_api_t apiHandle, CtlSectionT *section, json_object *MixerJ);
+int InternalHalHalMapConfig(afb_api_t apiHandle, CtlSectionT *section, json_object *AlsaMapJ);
+
+// Internals HAL verbs functions
+void InternalHalInfo(afb_req_t request);
+void InternalHalSubscribe(afb_req_t request);
+void InternalHalUnsubscribe(afb_req_t request);
+
+#endif /* _HALMGR_CB_INCLUDE_ */ \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.c b/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.c
new file mode 100644
index 0000000..95e9099
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 <stdio.h>
+#include <string.h>
+
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include "4a-hal-utilities-data.h"
+
+#include "4a-hal-utilities-hal-streams-handler.h"
+#include "4a-hal-utilities-verbs-loader.h"
+
+#include "../4a-hal-manager/4a-hal-manager.h"
+
+#include "4a-internals-hal-mixer-link.h"
+#include "4a-internals-hal-cb.h"
+
+/*******************************************************************************
+ * Internals HAL handle mixer calls functions *
+ ******************************************************************************/
+
+int InternalHalHandleMixerData(afb_api_t apiHandle,
+ struct InternalHalMixerData **mixerDataList,
+ json_object *currentDataJ,
+ enum MixerDataType dataType)
+{
+ int idx, mixerDataNb, verbStart, size;
+ int err = (int) MIXER_NO_ERROR;
+
+ char *currentDataVerbName, *currentStreamCardId;
+
+ json_type currentDataType;
+ json_object *currentJ;
+
+ struct InternalHalMixerData *currentMixerData;
+
+ currentDataType = json_object_get_type(currentDataJ);
+ switch(currentDataType) {
+ case json_type_object:
+ mixerDataNb = 1;
+ break;
+ case json_type_array:
+ mixerDataNb = (unsigned int) json_object_array_length(currentDataJ);
+ break;
+ default:
+ mixerDataNb = 0;
+ AFB_API_ERROR(apiHandle, "No data returned");
+ return (int) MIXER_ERROR_DATA_EMPTY;
+ }
+
+ for(idx = 0; idx < mixerDataNb; idx++) {
+ if(currentDataType == json_type_array)
+ currentJ = json_object_array_get_idx(currentDataJ, (int) idx);
+ else
+ currentJ = currentDataJ;
+
+ if(wrap_json_unpack(currentJ, "{s:s}", "verb", &currentDataVerbName)) {
+ AFB_API_ERROR(apiHandle, "Can't find verb in current data object");
+ err += (int) MIXER_ERROR_DATA_NAME_UNAVAILABLE;
+ }
+ else if(dataType == MIXER_DATA_STREAMS && wrap_json_unpack(currentJ, "{s:s}", "alsa", &currentStreamCardId)) {
+ AFB_API_ERROR(apiHandle, "Can't find card id in current data object");
+ err += (int) MIXER_ERROR_DATA_CARDID_UNAVAILABLE;
+ }
+ else {
+ switch(dataType) {
+ case MIXER_DATA_STREAMS:
+ size = (int) strlen(currentDataVerbName);
+ for(verbStart = 0; verbStart < size; verbStart++) {
+ if(currentDataVerbName[verbStart] == '#') {
+ verbStart++;
+ break;
+ }
+ }
+
+ if(verbStart == size)
+ verbStart = 0;
+
+ if(! HalUtlAddStreamDataAndCreateStreamVerb(apiHandle,
+ &currentDataVerbName[verbStart],
+ currentDataVerbName,
+ currentStreamCardId)) {
+ AFB_API_ERROR(apiHandle,
+ "Error while adding stream '%s'",
+ currentDataVerbName);
+ err += (int) MIXER_ERROR_STREAM_NOT_ADDED;
+ }
+
+ break;
+
+ case MIXER_DATA_PLAYBACKS:
+ case MIXER_DATA_CAPTURES:
+ currentMixerData = HalUtlAddMixerDataToMixerDataList(mixerDataList);
+
+ currentMixerData->verb = strdup((dataType == MIXER_DATA_PLAYBACKS) ? HAL_PLAYBACK_ID : HAL_CAPTURE_ID);
+ currentMixerData->verbToCall = strdup(currentDataVerbName);
+
+ if((! currentMixerData->verb) ||
+ (! currentMixerData->verbToCall)) {
+ HalUtlRemoveSelectedMixerData(mixerDataList, currentMixerData);
+ err += (int) MIXER_ERROR_STREAM_ALLOCATION_FAILED;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if(dataType == MIXER_DATA_PLAYBACKS) {
+ if(afb_api_add_verb(apiHandle,
+ HAL_PLAYBACK_ID,
+ "Playback action transferred to mixer",
+ HalUtlActionOnPlayback,
+ (void *) *mixerDataList,
+ NULL,
+ 0,
+ 0)) {
+ AFB_API_ERROR(apiHandle, "Error while creating verb for playbacks : '%s'", HAL_PLAYBACK_ID);
+ err += (int) MIXER_ERROR_PLAYBACK_VERB_NOT_CREATED;
+ }
+ }
+
+ if(dataType == MIXER_DATA_CAPTURES) {
+ if(afb_api_add_verb(apiHandle,
+ HAL_CAPTURE_ID,
+ "Capture action transferred to mixer",
+ HalUtlActionOnCapture,
+ (void *) *mixerDataList,
+ NULL,
+ 0,
+ 0)) {
+ AFB_API_ERROR(apiHandle, "Error while creating verb for captures : '%s'", HAL_CAPTURE_ID);
+ err += (int) MIXER_ERROR_CAPTURE_VERB_NOT_CREATED;
+ }
+ }
+
+ return err;
+}
+
+int InternalHalHandleMixerAttachResponse(afb_api_t apiHandle, struct InternalHalData *currentHalSpecificData, json_object *mixerResponseJ)
+{
+ int err = (int) MIXER_NO_ERROR;
+
+ json_object *mixerStreamsJ = NULL, *mixerPlaybacksJ = NULL, *mixerCapturesJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Can't get current hal api handle");
+ return (int) MIXER_ERROR_API_UNAVAILABLE;
+ }
+
+ if(wrap_json_unpack(mixerResponseJ, "{s?:o s?:o s?:o}", "streams", &mixerStreamsJ, "playbacks", &mixerPlaybacksJ, "captures", &mixerCapturesJ)) {
+ AFB_API_ERROR(apiHandle, "Can't get streams|playbacks|captures object in '%s'", json_object_get_string(mixerResponseJ));
+ return (int) MIXER_ERROR_DATA_UNAVAILABLE;
+ }
+
+ if(mixerStreamsJ) {
+ err += InternalHalHandleMixerData(apiHandle, &currentHalSpecificData->streamsData, mixerStreamsJ, MIXER_DATA_STREAMS);
+ if(err)
+ AFB_API_ERROR(apiHandle, "Error during handling response mixer streams data '%s'", json_object_get_string(mixerStreamsJ));
+ }
+
+ if(mixerPlaybacksJ) {
+ err += InternalHalHandleMixerData(apiHandle, &currentHalSpecificData->playbacksData, mixerPlaybacksJ, MIXER_DATA_PLAYBACKS);
+ if(err)
+ AFB_API_ERROR(apiHandle, "Error during handling response mixer playbacks data '%s'", json_object_get_string(mixerPlaybacksJ));
+ }
+
+ if(mixerCapturesJ) {
+ err += InternalHalHandleMixerData(apiHandle, &currentHalSpecificData->capturesData, mixerCapturesJ, MIXER_DATA_CAPTURES);
+ if(err)
+ AFB_API_ERROR(apiHandle, "Error during handling response mixer captures data '%s'", json_object_get_string(mixerCapturesJ));
+ }
+
+ if(! currentHalSpecificData->streamsData) {
+ AFB_API_WARNING(apiHandle, "No stream detected in mixer response, %s verb won't be created", HAL_ALL_STREAMS_VERB);
+ }
+ else if(afb_api_add_verb(apiHandle,
+ HAL_ALL_STREAMS_VERB,
+ "Send a stream action on all streams",
+ HalUtlActionOnAllStream,
+ (void *) currentHalSpecificData->streamsData,
+ NULL,
+ 0,
+ 0)) {
+ AFB_API_ERROR(apiHandle, "Error while creating verb for all streams : '%s'", HAL_ALL_STREAMS_VERB);
+ return (int) MIXER_ERROR_ALL_STREAMS_VERB_NOT_CREATED;
+ }
+
+ return err;
+}
+
+int InternalHalAttachToMixer(afb_api_t apiHandle)
+{
+ int err = 0, mixerError;
+
+ char *apiToCall, *returnedError = NULL, *returnedInfo = NULL;
+
+ CtlConfigT *ctrlConfig;
+
+ struct HalData *currentHalData, *concurentHalData = NULL;
+
+ json_object *responseJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Can't get current internal hal api handle");
+ return -1;
+ }
+
+ if(! (ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle))) {
+ AFB_API_ERROR(apiHandle, "Can't get current hal controller config");
+ return -2;
+ }
+
+ currentHalData = (struct HalData *) getExternalData(ctrlConfig);
+ if(! currentHalData) {
+ AFB_API_ERROR(apiHandle, "Can't get current hal controller data");
+ return -3;
+ }
+
+ switch(currentHalData->status) {
+ case HAL_STATUS_UNAVAILABLE:
+ AFB_API_ERROR(apiHandle, "Seems that the hal corresponding card was not found by alsacore at startup");
+ return -4;
+
+ case HAL_STATUS_READY:
+ AFB_API_NOTICE(apiHandle, "Seems that the hal mixer is already initialized");
+ return 1;
+
+ case HAL_STATUS_AVAILABLE:
+ break;
+ }
+
+ concurentHalData = HalUtlSearchReadyHalDataByCardId(HalMngGetHalDataList(), currentHalData->sndCardId);
+ if(concurentHalData) {
+ AFB_API_ERROR(apiHandle,
+ "Trying to attach mixer for hal '%s' but the alsa device %i is already in use with mixer by hal '%s'",
+ currentHalData->apiName,
+ currentHalData->sndCardId,
+ concurentHalData->apiName);
+ return -5;
+ }
+
+ apiToCall = currentHalData->internalHalData->mixerApiName;
+ if(! apiToCall) {
+ AFB_API_ERROR(apiHandle, "Can't get mixer api");
+ return -6;
+ }
+
+ if(afb_api_call_sync(apiHandle,
+ apiToCall,
+ MIXER_ATTACH_VERB,
+ json_object_get(currentHalData->internalHalData->halMixerJ),
+ &responseJ,
+ &returnedError,
+ &returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ MIXER_ATTACH_VERB,
+ apiToCall,
+ returnedError ? returnedError : "not returned",
+ returnedInfo ? returnedInfo : "not returned");
+ err = -7;
+ }
+ else if(! responseJ) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but no response was returned",
+ MIXER_ATTACH_VERB,
+ apiToCall);
+ err = -8;
+ }
+ else {
+ mixerError = InternalHalHandleMixerAttachResponse(apiHandle, currentHalData->internalHalData, responseJ);
+ if(mixerError != (int) MIXER_NO_ERROR) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but this warning was risen by response decoder : %i '%s'",
+ MIXER_ATTACH_VERB,
+ apiToCall,
+ mixerError,
+ json_object_get_string(responseJ));
+ err = -9;
+ }
+ else {
+ AFB_API_NOTICE(apiHandle,
+ "Seems that %s call to api %s succeed with no warning raised : '%s'",
+ MIXER_ATTACH_VERB,
+ apiToCall,
+ json_object_get_string(responseJ));
+ currentHalData->status = HAL_STATUS_READY;
+ }
+ }
+
+ if(responseJ)
+ json_object_put(responseJ);
+
+ free(returnedError);
+ free(returnedInfo);
+
+ return err;
+}
+
+int InternalHalGetInfoFromMixer(afb_api_t apiHandle,
+ char *apiToCall,
+ json_object *requestJson,
+ json_object **toReturnJ,
+ char **returnedError,
+ char **returnedInfo)
+{
+ json_object *responseJ = NULL;
+
+ if(! apiHandle) {
+ AFB_API_ERROR(apiHandle, "Can't get current hal api handle");
+ return -1;
+ }
+
+ if(! apiToCall) {
+ AFB_API_ERROR(apiHandle, "Can't get mixer api");
+ return -2;
+ }
+
+ if(! requestJson) {
+ AFB_API_ERROR(apiHandle, "Can't get request json");
+ return -3;
+ }
+
+ if(*returnedError || *returnedInfo) {
+ AFB_API_ERROR(apiHandle, "'returnedError' and 'returnedInfo' strings should be empty and set to 'NULL'");
+ return -4;
+ }
+
+ if(*toReturnJ) {
+ AFB_API_ERROR(apiHandle, "'toReturnJ' should be empty and set to 'NULL'");
+ return -5;
+ }
+
+ if(afb_api_call_sync(apiHandle,
+ apiToCall,
+ MIXER_INFO_VERB,
+ json_object_get(requestJson),
+ &responseJ,
+ returnedError,
+ returnedInfo)) {
+ AFB_API_ERROR(apiHandle,
+ "Something went wrong during call to verb '%s' of api '%s' with error '%s' and info '%s'",
+ apiToCall,
+ MIXER_INFO_VERB,
+ *returnedError ? *returnedError : "not returned",
+ *returnedInfo ? *returnedInfo : "not returned");
+ return -6;
+ }
+ else if(! responseJ) {
+ AFB_API_ERROR(apiHandle,
+ "Seems that %s call to api %s succeed but no response was returned",
+ MIXER_INFO_VERB,
+ apiToCall);
+ json_object_put(responseJ);
+ return -7;
+ }
+ else {
+ AFB_API_NOTICE(apiHandle,
+ "Seems that %s call to api %s succeed with no warning raised : '%s'",
+ MIXER_INFO_VERB,
+ apiToCall,
+ json_object_get_string(responseJ));
+ *toReturnJ = responseJ;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.h b/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.h
new file mode 100644
index 0000000..6c1f827
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-mixer-link.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 _INTERNALS_HAL_SOFTMIXER_LINK_INCLUDE_
+#define _INTERNALS_HAL_SOFTMIXER_LINK_INCLUDE_
+
+#include <stdio.h>
+
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include <afb-helpers-utils.h>
+
+#include "4a-hal-utilities-data.h"
+
+#define MIXER_ATTACH_VERB "attach"
+#define MIXER_INFO_VERB "info"
+
+#define HAL_PLAYBACK_ID "playback"
+#define HAL_CAPTURE_ID "capture"
+
+#define HAL_ALL_STREAMS_VERB "all-streams"
+
+// Enum for the type of object sent back by the mixer
+enum MixerDataType {
+ MIXER_DATA_STREAMS = 1,
+ MIXER_DATA_PLAYBACKS = 2,
+ MIXER_DATA_CAPTURES = 3
+};
+
+// Enum for the type of error detected
+enum MixerStatus {
+ MIXER_NO_ERROR=0,
+ MIXER_ERROR_API_UNAVAILABLE=-1,
+ MIXER_ERROR_DATA_UNAVAILABLE=-2,
+ MIXER_ERROR_DATA_EMPTY =-3,
+ MIXER_ERROR_PLAYBACK_VERB_NOT_CREATED =-4,
+ MIXER_ERROR_CAPTURE_VERB_NOT_CREATED =-5,
+ MIXER_ERROR_ALL_STREAMS_VERB_NOT_CREATED =-6,
+ MIXER_ERROR_DATA_NAME_UNAVAILABLE=-10,
+ MIXER_ERROR_DATA_CARDID_UNAVAILABLE=-100,
+ MIXER_ERROR_STREAM_NOT_ADDED =-1000,
+ MIXER_ERROR_STREAM_ALLOCATION_FAILED =-10000
+};
+
+// Internals HAL handle mixer calls functions
+int InternalHalAttachToMixer(afb_api_t apiHandle);
+int InternalHalGetInfoFromMixer(afb_api_t apiHandle,
+ char *apiToCall,
+ json_object *requestJson,
+ json_object **toReturnJ,
+ char **returnedError,
+ char **returnedInfo);
+
+#endif /* _INTERNALS_HAL_SOFTMIXER_LINK_INCLUDE_ */ \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.c b/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.c
new file mode 100644
index 0000000..64083a9
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 <stdio.h>
+#include <string.h>
+
+#include <limits.h>
+#include <math.h>
+
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include "4a-internals-hal-value-handler.h"
+#include "4a-internals-hal-alsacore-link.h"
+
+/*******************************************************************************
+ * Simple conversion value to/from percentage functions *
+ ******************************************************************************/
+
+int InternalHalConvertValueToPercentage(double val, double min, double max)
+{
+ double range;
+
+ range = max - min;
+ if(range <= 0)
+ return -INT_MAX;
+
+ val -= min;
+
+ return (int) round(val / range * 100);
+}
+
+int InternalHalConvertPercentageToValue(int percentage, int min, int max)
+{
+ int range;
+
+ range = max - min;
+ if(range <= 0)
+ return -INT_MAX;
+
+ return (int) round((double) percentage * (double) range * 0.01 + (double) min);
+}
+
+/*******************************************************************************
+ * Convert json object from percentage to value *
+ ******************************************************************************/
+
+int InternalHalConvertJsonValueForIntegerControl(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *toConvertJ,
+ json_object **ConvertedJ,
+ enum ConversionType requestedConversion)
+{
+ int initialValue, convertedValue;
+
+ if(! json_object_is_type(toConvertJ, json_type_int)) {
+ AFB_API_ERROR(apiHandle,
+ "Can't convert json value, unrecognized json format (must be an integer) : '%s'",
+ json_object_get_string(toConvertJ));
+ return -1;
+ }
+
+ initialValue = json_object_get_int(toConvertJ);
+
+ switch(requestedConversion) {
+ case CONVERSION_NORMALIZED_TO_ALSACORE:
+ if(initialValue < 0 || initialValue > 100) {
+ AFB_API_ERROR(apiHandle,
+ "Cannot convert '%i' value, value should be between 0 and 100 ('%s')",
+ initialValue,
+ json_object_get_string(toConvertJ));
+ return -2;
+ }
+
+ convertedValue = InternalHalConvertPercentageToValue(initialValue,
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ if(convertedValue == -INT_MAX) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to convert %i (using min %i et max %i)",
+ initialValue,
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ return -3;
+ }
+
+ if(alsaCtlProperties->step) {
+ // Round value to the nearest step
+ convertedValue = (int) round((double) convertedValue / (double) alsaCtlProperties->step);
+ convertedValue *= alsaCtlProperties->step;
+ }
+ break;
+
+ case CONVERSION_ALSACORE_TO_NORMALIZED:
+ convertedValue = InternalHalConvertValueToPercentage(initialValue,
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ if(convertedValue == -INT_MAX) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to convert %i (using min %i et max %i)",
+ initialValue,
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ return -4;
+ }
+
+ break;
+
+ default:
+ AFB_API_ERROR(apiHandle,
+ "Can't convert '%i' value, unrecognized conversion type : '%i'",
+ initialValue,
+ (int) requestedConversion);
+ *ConvertedJ = NULL;
+ return -5;
+ }
+
+ *ConvertedJ = json_object_new_int(convertedValue);
+
+ return 0;
+}
+
+int InternalHalConvertJsonValueForBooleanControl(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *toConvertJ,
+ json_object **ConvertedJ,
+ enum ConversionType requestedConversion)
+{
+ int initialValue;
+
+ switch(json_object_get_type(toConvertJ)) {
+ case json_type_int:
+ initialValue = json_object_get_int(toConvertJ);
+ break;
+
+ case json_type_boolean:
+ initialValue = json_object_get_boolean(toConvertJ);
+ break;
+
+ default:
+ AFB_API_ERROR(apiHandle,
+ "Can't convert json value, unrecognized format (must be an integer or a boolean) : '%s'",
+ json_object_get_string(toConvertJ));
+ return -1;
+ }
+
+ if(initialValue < 0 || initialValue > 1) {
+ AFB_API_ERROR(apiHandle,
+ "Cannot convert '%i' value, value should be 0 or 1 ('%s')",
+ initialValue,
+ json_object_get_string(toConvertJ));
+ return -2;
+ }
+
+ switch(requestedConversion) {
+ case CONVERSION_NORMALIZED_TO_ALSACORE:
+ *ConvertedJ = json_object_new_int(initialValue);
+ break;
+
+ case CONVERSION_ALSACORE_TO_NORMALIZED:
+ *ConvertedJ = json_object_new_boolean(initialValue);
+ break;
+
+ default:
+ AFB_API_ERROR(apiHandle,
+ "Can't convert '%i' value, unrecognized conversion type : '%i'",
+ initialValue,
+ (int) requestedConversion);
+ *ConvertedJ = NULL;
+ return -3;
+ }
+
+ return 0;
+}
+
+int InternalHalConvertJsonValues(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *toConvertJ,
+ json_object **ConvertedJ,
+ enum ConversionType requestedConversion)
+{
+ int conversionError = 0, idx, count;
+
+ json_type toConvertType;
+ json_object *toConvertObjectJ, *convertedValueJ, *convertedArrayJ;
+
+ *ConvertedJ = NULL;
+
+ toConvertType = json_object_get_type(toConvertJ);
+ count = (toConvertType == json_type_array) ? (int) json_object_array_length(toConvertJ) : 1;
+
+ convertedArrayJ = json_object_new_array();
+
+ for(idx = 0; idx < count; idx++) {
+ if(toConvertType == json_type_array)
+ toConvertObjectJ = json_object_array_get_idx(toConvertJ, idx);
+ else
+ toConvertObjectJ = toConvertJ;
+
+ switch(alsaCtlProperties->type) {
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ conversionError = InternalHalConvertJsonValueForIntegerControl(apiHandle,
+ alsaCtlProperties,
+ toConvertObjectJ,
+ &convertedValueJ,
+ requestedConversion);
+ if(conversionError) {
+ AFB_API_ERROR(apiHandle,
+ "Error %i happened in when trying to convert index %i for integer control ('%s')",
+ conversionError,
+ idx,
+ json_object_get_string(toConvertObjectJ));
+ json_object_put(convertedArrayJ);
+ return -(idx + 1);
+ }
+ break;
+
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ conversionError = InternalHalConvertJsonValueForBooleanControl(apiHandle,
+ alsaCtlProperties,
+ toConvertObjectJ,
+ &convertedValueJ,
+ requestedConversion);
+ if(conversionError) {
+ AFB_API_ERROR(apiHandle,
+ "Error %i happened in when trying to convert index %i for boolean control ('%s')",
+ conversionError,
+ idx,
+ json_object_get_string(toConvertObjectJ));
+ json_object_put(convertedArrayJ);
+ return -(idx + 1);
+ }
+ break;
+
+ default:
+ AFB_API_ERROR(apiHandle,
+ "Conversion not handle for the alsa control type %i",
+ (int) alsaCtlProperties->type);
+ json_object_put(convertedArrayJ);
+ return -(idx + 1);
+ }
+
+ json_object_array_put_idx(convertedArrayJ, idx, convertedValueJ);
+ }
+
+ *ConvertedJ = convertedArrayJ;
+
+ return 0;
+}
+
+int InternalHalChangePreviousValuesUsingJson(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *requestedPercentageVariationJ,
+ json_object *previousControlValuesJ,
+ json_object **ChangedJ)
+{
+ int requestedPercentageVariation, requestedVariation, toChangeValue, changedValue, idx, count;
+
+ char *requestedPercentageVariationString, *conversionEnd;
+
+ json_object *toChangeObjectJ, *changedArrayJ;
+
+ *ChangedJ = NULL;
+
+ requestedPercentageVariationString = (char *) json_object_get_string(requestedPercentageVariationJ);
+
+ requestedPercentageVariation = (int) strtol(requestedPercentageVariationString, &conversionEnd, 10);
+ if(conversionEnd == requestedPercentageVariationString) {
+ AFB_API_ERROR(apiHandle,
+ "Tried to increase/decrease an integer control \
+ but string sent in json is not a increase/decrease string : '%s'",
+ json_object_get_string(requestedPercentageVariationJ));
+ return -1;
+ }
+
+ if(alsaCtlProperties->type != SND_CTL_ELEM_TYPE_INTEGER &&
+ alsaCtlProperties->type != SND_CTL_ELEM_TYPE_INTEGER64) {
+ AFB_API_ERROR(apiHandle,
+ "Tried to increase/decrease values on a incompatible \
+ control type (%i), control type must be an integer",
+ alsaCtlProperties->type);
+ return -2;
+ }
+
+ if(requestedPercentageVariation < -100 || requestedPercentageVariation > 100) {
+ AFB_API_ERROR(apiHandle,
+ "Tried to increase/decrease values but specified change is \
+ not a valid percentage, it should be between -100 and 100");
+ return -3;
+ }
+
+ requestedVariation = InternalHalConvertPercentageToValue((int) abs(requestedPercentageVariation),
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ if(requestedVariation == -INT_MAX) {
+ AFB_API_ERROR(apiHandle,
+ "Didn't succeed to convert %i (using min %i et max %i)",
+ requestedPercentageVariation,
+ alsaCtlProperties->minval,
+ alsaCtlProperties->maxval);
+ return -4;
+ }
+
+ if(requestedPercentageVariation < 0)
+ requestedVariation = -requestedVariation;
+
+ count = (int) json_object_array_length(previousControlValuesJ);
+
+ changedArrayJ = json_object_new_array();
+
+ for(idx = 0; idx < count; idx++) {
+ toChangeObjectJ = json_object_array_get_idx(previousControlValuesJ, idx);
+
+ if(! json_object_is_type(toChangeObjectJ, json_type_int)) {
+ AFB_API_ERROR(apiHandle,
+ "Current json object %s is not an integer",
+ json_object_get_string(toChangeObjectJ));
+ return -(10 + idx);
+ }
+
+ toChangeValue = json_object_get_int(toChangeObjectJ);
+
+ if((toChangeValue + requestedVariation) < alsaCtlProperties->minval)
+ changedValue = alsaCtlProperties->minval;
+ else if((toChangeValue + requestedVariation) > alsaCtlProperties->maxval)
+ changedValue = alsaCtlProperties->maxval;
+ else
+ changedValue = toChangeValue + requestedVariation;
+
+ json_object_array_put_idx(changedArrayJ, idx, json_object_new_int(changedValue));
+ }
+
+ *ChangedJ = changedArrayJ;
+
+ return 0;
+} \ No newline at end of file
diff --git a/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.h b/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.h
new file mode 100644
index 0000000..f0288ac
--- /dev/null
+++ b/4a-hal/4a-internals-hal/4a-internals-hal-value-handler.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Jonathan Aillet <jonathan.aillet@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 _INTERNALS_HAL_VALUE_CONVERSION_INCLUDE_
+#define _INTERNALS_HAL_VALUE_CONVERSION_INCLUDE_
+
+#include <stdio.h>
+
+#include <wrap-json.h>
+
+#include <afb/afb-binding.h>
+
+#include "4a-internals-hal-alsacore-link.h"
+
+// Enum for the type of conversion requested
+enum ConversionType {
+ CONVERSION_NORMALIZED_TO_ALSACORE = 1,
+ CONVERSION_ALSACORE_TO_NORMALIZED = 2
+};
+
+// Simple conversion value to/from percentage functions
+int InternalHalConvertValueToPercentage(double val, double min, double max);
+int InternalHalConvertPercentageToValue(int percentage, int min, int max);
+
+// Convert json object from percentage to value
+int InternalHalConvertJsonValues(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *toConvertJ,
+ json_object **ConvertedJ,
+ enum ConversionType requestedConversion);
+
+// Increase/Decrease previous values using percentage passed in Json
+int InternalHalChangePreviousValuesUsingJson(afb_api_t apiHandle,
+ struct InternalHalAlsaCtlProperties *alsaCtlProperties,
+ json_object *requestedPercentageVariationJ,
+ json_object *previousControlValuesJ,
+ json_object **ChangedJ);
+
+#endif /* _INTERNALS_HAL_VALUE_CONVERSION_INCLUDE_ */ \ No newline at end of file