/* * Copyright (C) 2018 "IoT.bzh" * Author Jonathan Aillet * * 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 #include #include #include #include #include #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 InternalHalGetCardInfo(afb_api_t apiHandle, json_object *requestJ, json_object **responseJ) { int errorToReturn = 0; char *returnedError = NULL, *returnedInfo = NULL; if(! apiHandle || ! requestJ || ! responseJ) { AFB_API_ERROR(apiHandle, "Invalid argument(s)"); return -1; } if(afb_api_call_sync(apiHandle, ALSACORE_API, ALSACORE_GETINFO_VERB, requestJ, 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 : "none", returnedInfo ? returnedInfo : "none"); errorToReturn = -2; } free(returnedError); free(returnedInfo); return errorToReturn; } int InternalHalSubscribeToAlsaCardEvent(afb_api_t apiHandle, char *cardId) { int err = 0, wrapRet; char *returnedError = NULL, *returnedInfo = NULL; json_object *subscribeQueryJ, *responseJ = NULL; wrapRet = wrap_json_pack(&subscribeQueryJ, "{s:s, s:s}", "event", ALSACORE_CARD_CONTROL_EVENT_NAME, "devid", cardId); if(wrapRet) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate subscription query json object"); return -1; } 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 = -2; } 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 wrapRet; 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 id (%i) are specified, control id will be used", currentAlsaCtl->name, currentAlsaCtl->numid); } if(currentAlsaCtl->numid > 0) { wrapRet = wrap_json_pack(&queryJ, "{s:s s:i s:i}", "devid", cardId, "ctl", currentAlsaCtl->numid, "mode", 3); } else if(currentAlsaCtl->name) { wrapRet = 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(wrapRet) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate get ALSA control query json object"); return -5; } 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 -6; } 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 -7; } *returnedDataJ = responseJ; return 0; } int InternalHalUpdateAlsaCtlProperties(afb_api_t apiHandle, char *cardId, struct InternalHalAlsaCtl *currentAlsaCtl) { int err; char *name; json_object *returnedDataJ = NULL; if(! currentAlsaCtl->alsaCtlProperties) { AFB_API_ERROR(apiHandle, "Data structure to store alsa control properties is not allocated"); return -8; } err = InternalHalGetAlsaCtlInfo(apiHandle, cardId, currentAlsaCtl, &returnedDataJ); if(err) { return err; } // TBD JAI : get dblinear/dbminmax/... values else if(wrap_json_unpack(returnedDataJ, "{s:i s:s s:{s?:i s?:i s?:i s?:i s?:i}}", "id", ¤tAlsaCtl->numid, "name", &name, "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 = -9; } else { currentAlsaCtl->name = strdup(name); if(! currentAlsaCtl->name) { AFB_API_ERROR(apiHandle, "Didn't succeed to store (allocate) control 'name' string"); err = -10; } } 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, wrapRet; 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; } wrapRet = wrap_json_pack(&queryJ, "{s:s s:{s:i s:o}}", "devid", cardId, "ctl", "id", ctlId, "val", json_object_get(valuesJ)); if(wrapRet) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate set ALSA control query json object"); return -5; } 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 = -6; } 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, wrapRet; 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 structure for control creation is not available"); return -4; } wrapRet = 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(wrapRet) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate create ALSA control query json object"); return -5; } 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 = -6; } 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 = -7; } 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 = -8; } 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) { int wrapRet; 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; apiHandle = afb_req_get_api(request); if(! apiHandle) { afb_req_fail(request, "api_handle", "Can't get current internal hal api handle"); return; } ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle); if(! ctrlConfig) { 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->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; } if(! currentAlsaCtl->ctl.alsaCtlProperties) { afb_req_fail(request, "alsa_control_not_available", "Alsa control is not available"); return; } snprintf(cardIdString, 6, "hw:%i", currentAlsaCtl->cardNb); if(InternalHalGetAlsaCtlValues(apiHandle, cardIdString, ¤tAlsaCtl->ctl, &previousControlValuesJ)) { afb_req_fail(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; } requestJson = afb_req_json(request); if(! requestJson) { wrapRet = wrap_json_pack(&answerJ, "{s:o}", "current", normalizedPreviousControlValuesJ); if(wrapRet) { afb_req_fail(request, "currentvalues_json_object", "Didn't succeed to allocate ALSA control current values response json object"); return; } 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, ¤tAlsaCtl->ctl, &appliedControlValuesJ)) { afb_req_fail(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); wrapRet = wrap_json_pack(&answerJ, "{s:o, s:o}", "previous", normalizedPreviousControlValuesJ, "current", normalizedAppliedControlValuesJ); if(wrapRet) { afb_req_fail(request, "values_json_object", "Didn't succeed to allocate ALSA control previous and current values response json object"); return; } afb_req_success(request, answerJ, "Values correctly applied on alsa control"); }