summaryrefslogtreecommitdiffstats
path: root/4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c
diff options
context:
space:
mode:
Diffstat (limited to '4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c')
-rw-r--r--4a-hal/4a-internals-hal/4a-internals-hal-alsacore-link.c633
1 files changed, 633 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