/* * 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 "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-hal-controllers-mixer-link.h" #include "4a-hal-controllers-cb.h" /******************************************************************************* * HAL controllers handle mixer calls functions * ******************************************************************************/ int HalCtlsHandleMixerData(afb_api_t apiHandle, struct CtlHalMixerData **firstMixerData, 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 CtlHalMixerData *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", ¤tDataVerbName)) { 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", ¤tStreamCardId)) { 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, ¤tDataVerbName[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(firstMixerData); currentMixerData->verb = strdup((dataType == MIXER_DATA_PLAYBACKS) ? HAL_PLAYBACK_ID : HAL_CAPTURE_ID); currentMixerData->verbToCall = strdup(currentDataVerbName); if((! currentMixerData->verb) || (! currentMixerData->verbToCall)) { HalUtlRemoveSelectedMixerData(firstMixerData, 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 *) *firstMixerData, 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 *) *firstMixerData, 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 HalCtlsHandleMixerAttachResponse(afb_api_t apiHandle, struct CtlHalSpecificData *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 += HalCtlsHandleMixerData(apiHandle, ¤tHalSpecificData->ctlHalStreamsData, mixerStreamsJ, MIXER_DATA_STREAMS))) AFB_API_ERROR(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_API_ERROR(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_API_ERROR(apiHandle, "Error during handling response mixer captures data '%s'", json_object_get_string(mixerCapturesJ)); if(! currentHalSpecificData->ctlHalStreamsData) { 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->ctlHalStreamsData, 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 HalCtlsAttachToMixer(afb_api_t apiHandle) { int err = 0, mixerError; char *apiToCall, *returnedError = NULL, *returnedInfo = NULL; CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData, *concurentHalData = NULL; struct SpecificHalData **firstHalData; json_object *responseJ = NULL; if(! apiHandle) { AFB_API_ERROR(apiHandle, "Can't get current 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; } if(! (currentCtlHalData = (struct SpecificHalData *) getExternalData(ctrlConfig))) { AFB_API_ERROR(apiHandle, "Can't get current hal controller data"); return -3; } switch(currentCtlHalData->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; } firstHalData = HalMngGetFirstHalData(); if((concurentHalData = HalUtlSearchReadyHalDataByCardId(firstHalData, currentCtlHalData->sndCardId))) { 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'", currentCtlHalData->apiName, currentCtlHalData->sndCardId, concurentHalData->apiName); return -5; } if(! (apiToCall = currentCtlHalData->ctlHalSpecificData->mixerApiName)) { AFB_API_ERROR(apiHandle, "Can't get mixer api"); return -6; } if(afb_api_call_sync(apiHandle, apiToCall, MIXER_ATTACH_VERB, json_object_get(currentCtlHalData->ctlHalSpecificData->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 if((mixerError = HalCtlsHandleMixerAttachResponse(apiHandle, currentCtlHalData->ctlHalSpecificData, responseJ)) != (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)); currentCtlHalData->status = HAL_STATUS_READY; } if(responseJ) json_object_put(responseJ); free(returnedError); free(returnedInfo); return err; } int HalCtlsGetInfoFromMixer(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; }