diff options
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.c | 633 |
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", ¤tAlsaCtl->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 *) ¤tAlsaCtl->alsaCtlProperties.type, + "count", ¤tAlsaCtl->alsaCtlProperties.count, + "min", ¤tAlsaCtl->alsaCtlProperties.minval, + "max", ¤tAlsaCtl->alsaCtlProperties.maxval, + "step", ¤tAlsaCtl->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, ¤tAlsaCtl->ctl, &previousControlValuesJ)) { + afb_req_fail_f(request, "previous_values", "Error when trying to get unchanged alsa control values"); + return; + } + else if(InternalHalConvertJsonValues(apiHandle, + ¤tAlsaCtl->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, + ¤tAlsaCtl->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, + ¤tAlsaCtl->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, ¤tAlsaCtl->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, + ¤tAlsaCtl->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 |