/* * 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 "../4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h" #include "../4a-hal-utilities/4a-hal-utilities-data.h" #include "../4a-hal-utilities/4a-hal-utilities-verbs-loader.h" #include "../4a-hal-manager/4a-hal-manager.h" #include "4a-hal-controllers-mixer-link.h" #include "4a-hal-controllers-cb.h" /******************************************************************************* * HAL controllers handle mixer calls functions * ******************************************************************************/ int HalCtlsHandleMixerData(AFB_ApiT apiHandle, struct CtlHalMixerDataT *currentMixerDataT, json_object *currentDataJ, enum MixerDataType dataType) { int idx, verbStart, size; int err = (int) MIXER_NO_ERROR; char *currentDataVerbName, *currentStreamCardId; json_type currentDataType; json_object *currentJ; currentDataType = json_object_get_type(currentDataJ); switch(currentDataType) { case json_type_object: currentMixerDataT->count = 1; break; case json_type_array: currentMixerDataT->count = (unsigned int) json_object_array_length(currentDataJ); break; default: currentMixerDataT->count = 0; AFB_ApiError(apiHandle, "No data returned"); return (int) MIXER_ERROR_DATA_EMPTY; } currentMixerDataT->data = (struct CtlHalMixerData *) calloc(currentMixerDataT->count, sizeof(struct CtlHalMixerData)); for(idx = 0; idx < currentMixerDataT->count; 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", ¤tDataVerbName)) { AFB_ApiError(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", ¤tStreamCardId)) { AFB_ApiError(apiHandle, "Can't find card id in current data object"); err += (int) MIXER_ERROR_DATA_CARDID_UNAVAILABLE; } else { currentMixerDataT->data[idx].verbToCall = strdup(currentDataVerbName); if(dataType == MIXER_DATA_STREAMS) { currentMixerDataT->data[idx].streamCardId = strdup(currentStreamCardId); size = (int) strlen(currentDataVerbName); for(verbStart = 0; verbStart < size; verbStart++) { if(currentDataVerbName[verbStart] == '#') { verbStart++; break; } } if(verbStart == size) verbStart = 0; currentMixerDataT->data[idx].verb = strdup(¤tDataVerbName[verbStart]); if(afb_dynapi_add_verb(apiHandle, currentMixerDataT->data[idx].verb, "Stream action transferred to mixer", HalCtlsActionOnStream, (void *) ¤tMixerDataT->data[idx], NULL, 0)) { AFB_ApiError(apiHandle, "Error while creating verbs for stream : '%s'", currentMixerDataT->data[idx].verb); err += (int) MIXER_ERROR_STREAM_VERB_NOT_CREATED; } } else if(dataType == MIXER_DATA_PLAYBACKS) { currentMixerDataT->data[idx].verb = strdup(HAL_PLAYBACK_ID); } else if(dataType == MIXER_DATA_CAPTURES) { currentMixerDataT->data[idx].verb = strdup(HAL_CAPTURE_ID); } } } if(dataType == MIXER_DATA_PLAYBACKS) { if(afb_dynapi_add_verb(apiHandle, HAL_PLAYBACK_ID, "Playback action transferred to mixer", HalCtlsActionOnPlayback, (void *) currentMixerDataT, NULL, 0)) { AFB_ApiError(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_dynapi_add_verb(apiHandle, HAL_CAPTURE_ID, "Capture action transferred to mixer", HalCtlsActionOnCapture, (void *) currentMixerDataT, NULL, 0)) { AFB_ApiError(apiHandle, "Error while creating verb for captures : '%s'", HAL_CAPTURE_ID); err += (int) MIXER_ERROR_CAPTURE_VERB_NOT_CREATED; } } return err; } int HalCtlsHandleMixerAttachResponse(AFB_ApiT apiHandle, struct CtlHalSpecificData *currentHalSpecificData, json_object *mixerResponseJ) { int err = (int) MIXER_NO_ERROR; json_object *mixerStreamsJ = NULL, *mixerPlaybacksJ = NULL, *mixerCapturesJ = NULL; if(! apiHandle) { AFB_ApiError(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_ApiError(apiHandle, "Can't get streams|playbacks|captures object in '%s'", json_object_get_string(mixerResponseJ)); return (int) MIXER_ERROR_DATA_UNAVAILABLE; } if(mixerStreamsJ && (err += HalCtlsHandleMixerData(apiHandle, ¤tHalSpecificData->ctlHalStreamsData, mixerStreamsJ, MIXER_DATA_STREAMS))) AFB_ApiError(apiHandle, "Error during handling response mixer streams data '%s'", json_object_get_string(mixerStreamsJ)); if(mixerPlaybacksJ && (err += HalCtlsHandleMixerData(apiHandle, ¤tHalSpecificData->ctlHalPlaybacksData, mixerPlaybacksJ, MIXER_DATA_PLAYBACKS))) AFB_ApiError(apiHandle, "Error during handling response mixer playbacks data '%s'", json_object_get_string(mixerPlaybacksJ)); if(mixerCapturesJ && (err += HalCtlsHandleMixerData(apiHandle, ¤tHalSpecificData->ctlHalCapturesData, mixerCapturesJ, MIXER_DATA_CAPTURES))) AFB_ApiError(apiHandle, "Error during handling response mixer captures data '%s'", json_object_get_string(mixerCapturesJ)); if(! currentHalSpecificData->ctlHalStreamsData.count) { AFB_ApiWarning(apiHandle, "No stream detected in mixer response, %s verb won't be created", HAL_ALL_STREAMS_VERB); } else if(afb_dynapi_add_verb(apiHandle, HAL_ALL_STREAMS_VERB, "Send a stream action on all streams", HalCtlsActionOnAllStream, (void *) ¤tHalSpecificData->ctlHalStreamsData, NULL, 0)) { AFB_ApiError(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 HalCtlsAttachToMixer(AFB_ApiT apiHandle) { int err = 0, mixerError; char *apiToCall, *returnedStatus = NULL, *returnedInfo = NULL; enum CallError returnedError; CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData, *concurentHalData = NULL; struct SpecificHalData **firstHalData; json_object *returnJ = NULL, *toReturnJ; if(! apiHandle) { AFB_ApiError(apiHandle, "Can't get current hal api handle"); return -1; } ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) { AFB_ApiError(apiHandle, "Can't get current hal controller config"); return -2; } currentCtlHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentCtlHalData) { AFB_ApiError(apiHandle, "Can't get current hal controller data"); return -3; } switch(currentCtlHalData->status) { case HAL_STATUS_UNAVAILABLE: AFB_ApiError(apiHandle, "Seems that the hal corresponding card was not found by alsacore at startup"); return -4; case HAL_STATUS_READY: AFB_ApiNotice(apiHandle, "Seems that the hal mixer is already initialized"); return 1; case HAL_STATUS_AVAILABLE: break; } firstHalData = HalMngGetFirstHalData(); if((concurentHalData = HalUtlSearchReadyHalDataByCarId(firstHalData, currentCtlHalData->sndCardId))) { AFB_ApiError(apiHandle, "Trying to attach mixer for hal '%s' but the alsa device %i is already in use with mixer by hal '%s'", currentCtlHalData->apiName, currentCtlHalData->sndCardId, concurentHalData->apiName); return -5; } apiToCall = currentCtlHalData->ctlHalSpecificData->mixerApiName; if(! apiToCall) { AFB_ApiError(apiHandle, "Can't get mixer api"); return -6; } if(AFB_ServiceSync(apiHandle, apiToCall, MIXER_ATTACH_VERB, json_object_get(currentCtlHalData->ctlHalSpecificData->halMixerJ), &returnJ)) { returnedError = HalUtlHandleAppFwCallError(apiHandle, apiToCall, MIXER_ATTACH_VERB, returnJ, &returnedStatus, &returnedInfo); AFB_ApiError(apiHandle, "Error %i during call to verb %s of %s api with status '%s' and info '%s'", (int) returnedError, MIXER_ATTACH_VERB, apiToCall, returnedStatus ? returnedStatus : "not returned", returnedInfo ? returnedInfo : "not returned"); err = -7; } else if(! json_object_object_get_ex(returnJ, "response", &toReturnJ)) { AFB_ApiError(apiHandle, "Seems that %s call to api %s succeed, but response is not valid : '%s'", MIXER_ATTACH_VERB, apiToCall, json_object_get_string(returnJ)); err = -8; } else if((mixerError = HalCtlsHandleMixerAttachResponse(apiHandle, currentCtlHalData->ctlHalSpecificData, toReturnJ)) != (int) MIXER_NO_ERROR) { AFB_ApiError(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(toReturnJ)); err = -9; } else { AFB_ApiNotice(apiHandle, "Seems that %s call to api %s succeed with no warning raised : '%s'", MIXER_ATTACH_VERB, apiToCall, json_object_get_string(toReturnJ)); currentCtlHalData->status = HAL_STATUS_READY; } if(returnJ) json_object_put(returnJ); return err; } int HalCtlsGetInfoFromMixer(AFB_ApiT apiHandle, char *apiToCall, json_object *requestJson, json_object **toReturnJ, char **returnedStatus, char **returnedInfo) { int err = 0; enum CallError returnedError; json_object *returnJ, *responseJ; if(! apiHandle) { AFB_ApiError(apiHandle, "Can't get current hal api handle"); return -1; } if(! apiToCall) { AFB_ApiError(apiHandle, "Can't get mixer api"); return -2; } if(! requestJson) { AFB_ApiError(apiHandle, "Can't get request json"); return -3; } if(AFB_ServiceSync(apiHandle, apiToCall, MIXER_INFO_VERB, json_object_get(requestJson), &returnJ)) { returnedError = HalUtlHandleAppFwCallError(apiHandle, apiToCall, MIXER_INFO_VERB, returnJ, returnedStatus, returnedInfo); AFB_ApiError(apiHandle, "Error %i during call to verb %s of %s api with status '%s' and info '%s'", (int) returnedError, apiToCall, MIXER_INFO_VERB, *returnedStatus ? *returnedStatus : "not returned", *returnedInfo ? *returnedInfo : "not returned"); err = -4; } else if(! json_object_object_get_ex(returnJ, "response", &responseJ)) { AFB_ApiError(apiHandle, "Seems that %s call to api %s succeed, but response is not valid : '%s'", MIXER_INFO_VERB, apiToCall, json_object_get_string(returnJ)); err = -5; } else { AFB_ApiNotice(apiHandle, "Seems that %s call to api %s succeed with no warning raised : '%s'", MIXER_INFO_VERB, apiToCall, json_object_get_string(responseJ)); *toReturnJ = json_object_get(responseJ); } json_object_put(returnJ); return err; }