/* * 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-data.h" #include "../4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h" #include "4a-hal-controllers-cb.h" #include "4a-hal-controllers-alsacore-link.h" #include "4a-hal-controllers-mixer-link.h" #include "4a-hal-controllers-value-handler.h" /******************************************************************************* * HAL controller event handler function * ******************************************************************************/ void HalCtlsDispatchApiEvent(afb_dynapi *apiHandle, const char *evtLabel, json_object *eventJ) { int numid, idx = 0, cardidx; CtlConfigT *ctrlConfig; CtlSourceT source; struct SpecificHalData *currentHalData; struct CtlHalAlsaMapT *currentHalAlsaCtlsT; json_object *valuesJ, *normalizedValuesJ; AFB_ApiDebug(apiHandle, "Evtname=%s [msg=%s]", evtLabel, json_object_get_string(eventJ)); ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) { AFB_ApiError(apiHandle, "Can't get current hal controller config"); return; } currentHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentHalData) { AFB_ApiWarning(apiHandle, "Can't get current hal controller data"); return; } // Extract sound card index from event while(evtLabel[idx] != '\0' && evtLabel[idx] != ':') idx++; if(evtLabel[idx] != '\0' && sscanf(&evtLabel[idx + 1], "%d", &cardidx) == 1 && currentHalData->sndCardId == cardidx) { if(wrap_json_unpack(eventJ, "{s:i s:o !}", "id", &numid, "val", &valuesJ)) { AFB_ApiError(apiHandle, "Invalid Alsa Event label=%s value=%s", evtLabel, json_object_get_string(eventJ)); return; } currentHalAlsaCtlsT = currentHalData->ctlHalSpecificData->ctlHalAlsaMapT; // Search for corresponding numid in halCtls, if found, launch callback (if available) for(idx = 0; idx < currentHalAlsaCtlsT->ctlsCount; idx++) { if(currentHalAlsaCtlsT->ctls[idx].ctl.numid == numid) { if(currentHalAlsaCtlsT->ctls[idx].action) { source.uid = currentHalAlsaCtlsT->ctls[idx].action->uid; source.api = currentHalAlsaCtlsT->ctls[idx].action->api; source.request = NULL; (void) ActionExecOne(&source, currentHalAlsaCtlsT->ctls[idx].action, valuesJ); } else { AFB_ApiNotice(apiHandle, "The alsa control id '%i' is corresponding to a known control but without any action registered", numid); } if((! currentHalAlsaCtlsT->ctls[idx].alsaControlEvent) || HalCtlsConvertJsonValues(apiHandle, ¤tHalAlsaCtlsT->ctls[idx].ctl.alsaCtlProperties, valuesJ, &normalizedValuesJ, CONVERSION_ALSACORE_TO_NORMALIZED) || (AFB_EventPush(currentHalAlsaCtlsT->ctls[idx].alsaControlEvent, normalizedValuesJ) < 0)) { AFB_ApiError(apiHandle, "Couldn't generate an event for known halmap %s (alsa control id %i)", currentHalAlsaCtlsT->ctls[idx].uid, currentHalAlsaCtlsT->ctls[idx].ctl.numid); } return; } } AFB_ApiWarning(apiHandle, "Alsacore event with an unrecognized numid: %i, evtname=%s [msg=%s]", numid, evtLabel, json_object_get_string(eventJ)); return; } AFB_ApiInfo(apiHandle, "Not an alsacore event '%s' [msg=%s]", evtLabel, json_object_get_string(eventJ)); CtrlDispatchApiEvent(apiHandle, evtLabel, eventJ); } /******************************************************************************* * HAL controllers sections parsing functions * ******************************************************************************/ int HalCtlsHalMixerConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *MixerJ) { int err = 0; CtlConfigT *ctrlConfig; struct SpecificHalData *currentHalData; if(! apiHandle || ! section) return -1; ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) return -2; currentHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentHalData) return -3; if(MixerJ) { if(json_object_is_type(MixerJ, json_type_object)) currentHalData->ctlHalSpecificData->halMixerJ = MixerJ; else return -4; if(wrap_json_unpack(MixerJ, "{s:s}", "mixerapi", ¤tHalData->ctlHalSpecificData->mixerApiName)) return -5; wrap_json_unpack(MixerJ, "{s?:s}", "prefix", ¤tHalData->ctlHalSpecificData->prefix); } else if(currentHalData->status == HAL_STATUS_AVAILABLE && (err = HalCtlsAttachToMixer(apiHandle))) { AFB_ApiError(apiHandle, "%s: Error %i while attaching to mixer", __func__, err); return -6; } return 0; } int HalCtlsProcessOneHalMapObject(AFB_ApiT apiHandle, struct CtlHalAlsaMap *alsaMap, json_object *AlsaMapJ) { char *action = NULL, *typename = NULL; json_object *alsaJ = NULL, *createAlsaCtlJ = NULL; AFB_ApiDebug(apiHandle, "AlsaMapJ=%s", json_object_get_string(AlsaMapJ)); if(wrap_json_unpack(AlsaMapJ, "{s:s s?:s s:o s?:s !}", "uid", &alsaMap->uid, "info", &alsaMap->info, "alsa", &alsaJ, "action", &action)) { AFB_ApiError(apiHandle, "Parsing error, map should only contains [label]|[uid]|[tag]|[info]|[alsa]|[action] in:\n-- %s", json_object_get_string(AlsaMapJ)); return -1; } if(wrap_json_unpack(alsaJ, "{s?:s s?:i s?:i s?:o !}", "name", &alsaMap->ctl.name, "numid", &alsaMap->ctl.numid, "value", &alsaMap->ctl.value, "create", &createAlsaCtlJ)) { AFB_ApiError(apiHandle, "Parsing error, alsa json should only contains [name]|[numid]||[value]|[create] in:\n-- %s", json_object_get_string(alsaJ)); return -2; } if(createAlsaCtlJ) { alsaMap->ctl.alsaCtlCreation = &alsaMap->ctl.alsaCtlProperties; if(wrap_json_unpack(createAlsaCtlJ, "{s:s s:i s:i s:i s:i !}", "type", &typename, "count", &alsaMap->ctl.alsaCtlCreation->count, "minval", &alsaMap->ctl.alsaCtlCreation->minval, "maxval", &alsaMap->ctl.alsaCtlCreation->maxval, "step", &alsaMap->ctl.alsaCtlCreation->step)) { AFB_ApiError(apiHandle, "Parsing error, alsa creation json should only contains [type]|[count]|[minval]|[maxval]|[step] in:\n-- %s", json_object_get_string(alsaJ)); return -3; } if(typename && (alsaMap->ctl.alsaCtlCreation->type = HalCtlsMapsAlsaTypeToEnum(typename)) == SND_CTL_ELEM_TYPE_NONE) { AFB_ApiError(apiHandle, "Couldn't get alsa type from string %s in:\n-- %s", typename, json_object_get_string(alsaJ)); return -4; } if(! alsaMap->ctl.name) alsaMap->ctl.name = (char *) alsaMap->uid; } else if(alsaMap->ctl.name && alsaMap->ctl.numid > 0) { AFB_ApiError(apiHandle, "Can't have both a control name (%s) and a control uid (%i) in alsa object:\n-- %s", alsaMap->ctl.name, alsaMap->ctl.numid, json_object_get_string(alsaJ)); return -5; } else if(! alsaMap->ctl.name && alsaMap->ctl.numid <= 0) { AFB_ApiError(apiHandle, "Need at least a control name or a control uid in alsa object:\n-- %s", json_object_get_string(alsaJ)); return -6; } if(action) alsaMap->actionJ = AlsaMapJ; return 0; } int HalCtlsHandleOneHalMapObject(AFB_ApiT apiHandle, char *cardId, struct CtlHalAlsaMap *alsaMap) { int err; json_object *valueJ, *convertedValueJ = NULL; if(alsaMap->ctl.alsaCtlCreation) { if(HalCtlsCreateAlsaCtl(apiHandle, cardId, &alsaMap->ctl)) { AFB_ApiError(apiHandle, "An error happened when trying to create a new alsa control"); return -1; } } else if(HalCtlsUpdateAlsaCtlProperties(apiHandle, cardId, &alsaMap->ctl)) { AFB_ApiError(apiHandle, "An error happened when trying to get existing alsa control info"); return -2; } if(alsaMap->ctl.value) { // TBD JAI : handle alsa controls type valueJ = json_object_new_int(alsaMap->ctl.value); err = 0; if(HalCtlsConvertJsonValues(apiHandle, &alsaMap->ctl.alsaCtlProperties, valueJ, &convertedValueJ, CONVERSION_NORMALIZED_TO_ALSACORE)) { AFB_ApiError(apiHandle, "Error when trying to convert initiate value json '%s'", json_object_get_string(valueJ)); err = -3; } else if(HalCtlsSetAlsaCtlValue(apiHandle, cardId, alsaMap->ctl.numid, convertedValueJ)) { AFB_ApiError(apiHandle, "Error while trying to set initial value on alsa control %i, device '%s', value '%s'", alsaMap->ctl.numid, cardId, json_object_get_string(valueJ)); err = -4; } json_object_put(valueJ); if(convertedValueJ) json_object_put(convertedValueJ); if(err) return err; } if(alsaMap->actionJ) { alsaMap->action = calloc(1, sizeof(CtlActionT)); if(ActionLoadOne(apiHandle, alsaMap->action, alsaMap->actionJ, 0)) { AFB_ApiError(apiHandle, "Didn't succeed to load action using alsa object:\n-- %s", json_object_get_string(alsaMap->actionJ)); return -5; } } alsaMap->alsaControlEvent = AFB_EventMake(apiHandle, alsaMap->uid); if(! AFB_EventIsValid(alsaMap->alsaControlEvent)) { AFB_ApiError(apiHandle, "Didn't succeed to create event for current alsa control to load action using alsa object:\n-- %s", json_object_get_string(alsaMap->actionJ)); return -6; } if(afb_dynapi_add_verb(apiHandle, alsaMap->uid, alsaMap->info, HalCtlsActionOnAlsaCtl, (void *) alsaMap, NULL, 0)) { AFB_ApiError(apiHandle, "Didn't succeed to create verb for current alsa control to load action using alsa object:\n-- %s", json_object_get_string(alsaMap->actionJ)); return -7; } return 0; } int HalCtlsProcessAllHalMap(AFB_ApiT apiHandle, json_object *AlsaMapJ, struct CtlHalAlsaMapT *currentCtlHalAlsaMapT) { int idx, err = 0; struct CtlHalAlsaMap *ctlMaps; json_type alsaMapType; alsaMapType = json_object_get_type(AlsaMapJ); switch(alsaMapType) { case json_type_array: currentCtlHalAlsaMapT->ctlsCount = (unsigned int) json_object_array_length(AlsaMapJ); break; case json_type_object: currentCtlHalAlsaMapT->ctlsCount = 1; break; default: currentCtlHalAlsaMapT->ctlsCount = 0; currentCtlHalAlsaMapT->ctls = NULL; AFB_ApiWarning(apiHandle, "Couldn't get content of 'halmap' section in:\n-- %s", json_object_get_string(AlsaMapJ)); return -1; } ctlMaps = calloc(currentCtlHalAlsaMapT->ctlsCount, sizeof(struct CtlHalAlsaMap)); for(idx = 0; idx < currentCtlHalAlsaMapT->ctlsCount; idx++) err += HalCtlsProcessOneHalMapObject(apiHandle, &ctlMaps[idx], alsaMapType == json_type_array ? json_object_array_get_idx(AlsaMapJ, idx) : AlsaMapJ); currentCtlHalAlsaMapT->ctls = ctlMaps; return err; } int HalCtlsHandleAllHalMap(AFB_ApiT apiHandle, int sndCardId, struct CtlHalAlsaMapT *currentCtlHalAlsaMapT) { int idx, err = 0; char cardIdString[6]; snprintf(cardIdString, 6, "hw:%i", sndCardId); HalCtlsSubscribeToAlsaCardEvent(apiHandle, cardIdString); for(idx = 0; idx < currentCtlHalAlsaMapT->ctlsCount; idx++) err += HalCtlsHandleOneHalMapObject(apiHandle, cardIdString, ¤tCtlHalAlsaMapT->ctls[idx]); return err; } int HalCtlsHalMapConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *AlsaMapJ) { CtlConfigT *ctrlConfig; struct SpecificHalData *currentHalData; ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) return -1; currentHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentHalData) return -2; if(AlsaMapJ) { currentHalData->ctlHalSpecificData->ctlHalAlsaMapT = calloc(1, sizeof(struct CtlHalAlsaMapT)); if(HalCtlsProcessAllHalMap(apiHandle, AlsaMapJ, currentHalData->ctlHalSpecificData->ctlHalAlsaMapT)) { AFB_ApiError(apiHandle, "Failed to process 'halmap' section"); return -3; } } else if(currentHalData->status == HAL_STATUS_UNAVAILABLE) { AFB_ApiWarning(apiHandle, "Hal is unavailable, 'halmap' section data can't be handle"); return 1; } else if(currentHalData->sndCardId < 0) { AFB_ApiError(apiHandle, "Hal alsa card id is not valid, 'halmap' section data can't be handle"); return -6; } else if(! currentHalData->ctlHalSpecificData->ctlHalAlsaMapT) { AFB_ApiWarning(apiHandle, "'halmap' section data is empty"); return 2; } else if(! (currentHalData->ctlHalSpecificData->ctlHalAlsaMapT->ctlsCount > 0)) { AFB_ApiWarning(apiHandle, "No alsa controls defined in 'halmap' section"); return 3; } else if(HalCtlsHandleAllHalMap(apiHandle, currentHalData->sndCardId, currentHalData->ctlHalSpecificData->ctlHalAlsaMapT)) { AFB_ApiError(apiHandle, "Failed to handle 'halmap' section"); return -9; } return 0; } /******************************************************************************* * HAL controllers verbs functions * ******************************************************************************/ void HalCtlsActionOnMixer(AFB_ReqT request, enum ActionOnMixerType actionType) { int idx, count; char *apiToCall; AFB_ApiT apiHandle; CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData; struct CtlHalMixerData *currentMixerData = NULL; struct CtlHalMixerDataT *currentMixerDataT = NULL; json_object *requestJson, *returnJ = NULL, *responseJ, *toReturnJ = NULL; apiHandle = (AFB_ApiT) afb_request_get_dynapi(request); if(! apiHandle) { AFB_ReqFail(request, "api_handle", "Can't get current hal controller api handle"); return; } ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) { AFB_ReqFail(request, "hal_controller_config", "Can't get current hal controller config"); return; } currentMixerData = (struct CtlHalMixerData *) afb_request_get_vcbdata(request); if(! currentMixerData) { AFB_ReqFail(request, "hal_call_data", "Can't get current call data"); return; } currentCtlHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentCtlHalData) { AFB_ReqFail(request, "hal_controller_data", "Can't get current hal controller data"); return; } requestJson = AFB_ReqJson(request); if(! requestJson) { AFB_ReqFail(request, "request_json", "Can't get request json"); return; } if(json_object_is_type(requestJson, json_type_object) && json_object_get_object(requestJson)->count > 0) json_object_object_add(requestJson, "verbose", json_object_new_boolean(1)); apiToCall = currentCtlHalData->ctlHalSpecificData->mixerApiName; if(! apiToCall) { AFB_ReqFail(request, "mixer_api", "Can't get mixer api"); return; } if(currentCtlHalData->status != HAL_STATUS_READY) { AFB_ReqFail(request, "hal_not_ready", "Seems that hal is not ready"); return; } switch(actionType) { case ACTION_ON_MIXER_STREAM: currentMixerData = (struct CtlHalMixerData *) AFB_ReqVCBData(request); if(! currentMixerData) { AFB_ReqFail(request, "hal_call_data", "Can't get current call data"); return; } count = 1; break; case ACTION_ON_MIXER_PLAYBACK: case ACTION_ON_MIXER_CAPTURE: case ACTION_ON_MIXER_ALL_STREAM: currentMixerDataT = (struct CtlHalMixerDataT *) AFB_ReqVCBData(request); if(! currentMixerDataT) { AFB_ReqFail(request, "hal_call_data", "Can't get current call data"); return; } count = currentMixerDataT->count; toReturnJ = json_object_new_object(); break; default: AFB_ReqFail(request, "hal_call_data", "Can't get current call data"); return; } for(idx = 0; idx < count; idx++) { switch(actionType) { case ACTION_ON_MIXER_PLAYBACK: case ACTION_ON_MIXER_CAPTURE: case ACTION_ON_MIXER_ALL_STREAM: currentMixerData = ¤tMixerDataT->data[idx]; break; default: break; } if(AFB_ServiceSync(apiHandle, apiToCall, currentMixerData->verbToCall, json_object_get(requestJson), &returnJ)) { HalUtlHandleAppFwCallErrorInRequest(request, apiToCall, currentMixerData->verbToCall, returnJ, "call_action"); json_object_put(returnJ); if(toReturnJ) json_object_put(toReturnJ); return; } if(wrap_json_unpack(returnJ, "{s:o}", "response", &responseJ)) { AFB_ReqFailF(request, "Seems that %s call to api %s succeed, but no response was found in : '%s'", currentMixerData->verbToCall, apiToCall, json_object_get_string(returnJ)); json_object_put(returnJ); if(toReturnJ) json_object_put(toReturnJ); return; } // TBD JAI : When mixer events will be available, use them instead of generating events at calls if((actionType == ACTION_ON_MIXER_STREAM || actionType == ACTION_ON_MIXER_ALL_STREAM) && ((! currentMixerData->event) || (AFB_EventPush(currentMixerData->event, json_object_get(responseJ)) < 0))) { AFB_ApiError(apiHandle, "Couldn't generate an event for stream %s", currentMixerData->verb); } switch(actionType) { case ACTION_ON_MIXER_STREAM: toReturnJ = json_object_get(responseJ); break; case ACTION_ON_MIXER_PLAYBACK: case ACTION_ON_MIXER_CAPTURE: json_object_object_add(toReturnJ, currentMixerData->verbToCall, json_object_get(responseJ)); break; case ACTION_ON_MIXER_ALL_STREAM: json_object_object_add(toReturnJ, currentMixerData->verb, json_object_get(responseJ)); break; default: break; } json_object_put(returnJ); } switch(actionType) { case ACTION_ON_MIXER_STREAM: AFB_ReqSuccessF(request, toReturnJ, "Action %s correctly transferred to %s without any error raised", currentMixerData->verbToCall, apiToCall); break; case ACTION_ON_MIXER_PLAYBACK: AFB_ReqSuccess(request, toReturnJ, "Actions correctly transferred to all playbacks without any error raised"); break; case ACTION_ON_MIXER_CAPTURE: AFB_ReqSuccess(request, toReturnJ, "Actions correctly transferred to all captures without any error raised"); break; case ACTION_ON_MIXER_ALL_STREAM: AFB_ReqSuccess(request, toReturnJ, "Actions correctly transferred to all streams without any error raised"); break; default: break; } } void HalCtlsActionOnStream(AFB_ReqT request) { HalCtlsActionOnMixer(request, ACTION_ON_MIXER_STREAM); } void HalCtlsActionOnPlayback(AFB_ReqT request) { HalCtlsActionOnMixer(request, ACTION_ON_MIXER_PLAYBACK); } void HalCtlsActionOnCapture(AFB_ReqT request) { HalCtlsActionOnMixer(request, ACTION_ON_MIXER_CAPTURE); } void HalCtlsActionOnAllStream(AFB_ReqT request) { HalCtlsActionOnMixer(request, ACTION_ON_MIXER_ALL_STREAM); } json_object *HalCtlsGetJsonArrayForMixerDataTable(AFB_ApiT apiHandle, struct CtlHalMixerDataT *currentMixerDataT, enum MixerDataType dataType) { unsigned int idx; json_object *mixerDataArray, *currentMixerData; if(! apiHandle) { AFB_ApiError(apiHandle, "Can't get current hal controller api handle"); return NULL; } if(! currentMixerDataT) { AFB_ApiError(apiHandle, "Can't get mixer data table to handle"); return NULL; } mixerDataArray = json_object_new_array(); if(! mixerDataArray) { AFB_ApiError(apiHandle, "Can't generate json mixer data array"); return NULL; } for(idx = 0; idx < currentMixerDataT->count; idx++) { switch(dataType) { case MIXER_DATA_STREAMS: wrap_json_pack(¤tMixerData, "{s:s s:s}", "name", currentMixerDataT->data[idx].verb, "cardId", currentMixerDataT->data[idx].streamCardId); break; case MIXER_DATA_PLAYBACKS: case MIXER_DATA_CAPTURES : wrap_json_pack(¤tMixerData, "{s:s s:s}", "name", currentMixerDataT->data[idx].verb, "mixer-name", currentMixerDataT->data[idx].verbToCall, "uid", currentMixerDataT->data[idx].streamCardId ? currentMixerDataT->data[idx].streamCardId : "none"); break; default: json_object_put(mixerDataArray); return NULL; } json_object_array_add(mixerDataArray, currentMixerData); } return mixerDataArray; } json_object *HalCtlsGetJsonArrayForControls(AFB_ApiT apiHandle, struct CtlHalAlsaMapT *currentAlsaMapDataT) { unsigned int idx; json_object *alsaMapDataArray, *currentAlsaMapData; if(! apiHandle) { AFB_ApiError(apiHandle, "Can't get current hal controller api handle"); return NULL; } if(! currentAlsaMapDataT) { AFB_ApiError(apiHandle, "Can't get Alsa map data table to handle"); return NULL; } alsaMapDataArray = json_object_new_array(); if(! alsaMapDataArray) { AFB_ApiError(apiHandle, "Can't generate json mixer data array"); return NULL; } for(idx = 0; idx < currentAlsaMapDataT->ctlsCount; idx++) { wrap_json_pack(¤tAlsaMapData, "{s:s s:s}", "name", currentAlsaMapDataT->ctls[idx].uid, "info", currentAlsaMapDataT->ctls[idx].info ? currentAlsaMapDataT->ctls[idx].info : "none"); json_object_array_add(alsaMapDataArray, currentAlsaMapData); } return alsaMapDataArray; } void HalCtlsInfo(AFB_ReqT request) { char *apiToCall, *returnedStatus = NULL, *returnedInfo = NULL; AFB_ApiT apiHandle; CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData; json_object *requestJson, *toReturnJ = NULL, *requestAnswer, *streamsArray, *playbacksArray, *capturesArray, *controlsArray; apiHandle = (AFB_ApiT) afb_request_get_dynapi(request); if(! apiHandle) { AFB_ReqFail(request, "api_handle", "Can't get current hal controller api handle"); return; } ctrlConfig = (CtlConfigT *) afb_dynapi_get_userdata(apiHandle); if(! ctrlConfig) { AFB_ReqFail(request, "hal_controller_config", "Can't get current hal controller config"); return; } currentCtlHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentCtlHalData) { AFB_ReqFail(request, "hal_controller_data", "Can't get current hal controller data"); return; } requestJson = AFB_ReqJson(request); if(! requestJson) { AFB_ReqNotice(request, "Can't get request json"); } else if(json_object_is_type(requestJson, json_type_object) && json_object_get_object(requestJson)->count > 0) { apiToCall = currentCtlHalData->ctlHalSpecificData->mixerApiName; if(! apiToCall) { AFB_ReqFail(request, "mixer_api", "Can't get mixer api"); return; } if(HalCtlsGetInfoFromMixer(apiHandle, apiToCall, requestJson, &toReturnJ, &returnedStatus, &returnedInfo)) { if(returnedStatus && returnedInfo) { AFB_ReqFailF(request, "mixer_info", "Call to mixer info verb didn't succeed with status '%s' and info '%s'", returnedStatus, returnedInfo); } else { AFB_ReqFail(request, "mixer_info", "Call to mixer info verb didn't succeed"); } return; } AFB_ReqSuccess(request, toReturnJ, "Mixer requested data"); return; } if(! (streamsArray = HalCtlsGetJsonArrayForMixerDataTable(apiHandle, ¤tCtlHalData->ctlHalSpecificData->ctlHalStreamsData, MIXER_DATA_STREAMS))) { AFB_ReqFail(request, "streams_data", "Didn't succeed to generate streams data array"); return; } if(! (playbacksArray = HalCtlsGetJsonArrayForMixerDataTable(apiHandle, ¤tCtlHalData->ctlHalSpecificData->ctlHalPlaybacksData, MIXER_DATA_PLAYBACKS))) { AFB_ReqFail(request, "playbacks_data", "Didn't succeed to generate playbacks data array"); return; } if(! (capturesArray = HalCtlsGetJsonArrayForMixerDataTable(apiHandle, ¤tCtlHalData->ctlHalSpecificData->ctlHalCapturesData, MIXER_DATA_CAPTURES))) { AFB_ReqFail(request, "captures_data", "Didn't succeed to generate captures data array"); return; } if(! (controlsArray = HalCtlsGetJsonArrayForControls(apiHandle, currentCtlHalData->ctlHalSpecificData->ctlHalAlsaMapT))) { AFB_ReqFail(request, "controls_data", "Didn't succeed to generate controls data array"); return; } wrap_json_pack(&requestAnswer, "{s:o s:o s:o s:o}", "streams", streamsArray, "playbacks", playbacksArray, "captures", capturesArray, "controls", controlsArray); AFB_ReqSuccess(request, requestAnswer, "Requested data"); } void HalCtlsSubscribeUnsubscribe(AFB_ReqT request, enum SubscribeUnsubscribeType subscribeUnsubscribeType) { int arrayIdx, searchIdx, count, subscriptionFound, subscriptionDoneNb = 0; char *currentSubscriptionString; AFB_ApiT apiHandle; CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData; struct CtlHalMixerDataT *halStreamsData; struct CtlHalAlsaMapT *halAlsaMapT; json_object *requestJson, *requestedSubscriptionsJ, *requestedSubscriptionJ = NULL; json_type requestJsonType; apiHandle = (AFB_ApiT) AFB_ReqGetApi(request); if(! apiHandle) { AFB_ReqFail(request, "api_handle", "Can't get current hal controller api handle"); return; } ctrlConfig = (CtlConfigT *) AFB_ApiGetUserData(apiHandle); if(! ctrlConfig) { AFB_ReqFail(request, "hal_controller_config", "Can't get current hal controller config"); return; } currentCtlHalData = (struct SpecificHalData *) ctrlConfig->external; if(! currentCtlHalData) { AFB_ReqFail(request, "hal_controller_data", "Can't get current hal controller data"); return; } if(! currentCtlHalData->ctlHalSpecificData) { AFB_ReqFail(request, "hal_controller_data", "Can't get current hal specific data"); return; } halStreamsData = ¤tCtlHalData->ctlHalSpecificData->ctlHalStreamsData; halAlsaMapT = currentCtlHalData->ctlHalSpecificData->ctlHalAlsaMapT; requestJson = AFB_ReqJson(request); if(! requestJson) { AFB_ReqFail(request, "request_json", "Can't get request json"); } if(wrap_json_unpack(requestJson, "{s:o}", "events", &requestedSubscriptionsJ)) { AFB_ReqFail(request, "request_json", "Request json invalid"); return; } requestJsonType = json_object_get_type(requestedSubscriptionsJ); switch(requestJsonType) { case json_type_string: count = 1; requestedSubscriptionJ = requestedSubscriptionsJ; break; case json_type_array: count = (int) json_object_array_length(requestedSubscriptionsJ); break; default: AFB_ReqFail(request, "request_json", "Request json invalid"); return; } for(arrayIdx = 0; arrayIdx < count; arrayIdx++) { if(requestJsonType == json_type_array) { requestedSubscriptionJ = json_object_array_get_idx(requestedSubscriptionsJ, arrayIdx); if(! json_object_is_type(requestedSubscriptionJ, json_type_string)) { AFB_ReqFailF(request, "request_json", "Request json number %i in array invalid", arrayIdx); return; } } subscriptionFound = 0; currentSubscriptionString = (char *) json_object_get_string(requestedSubscriptionJ); searchIdx = 0; while((searchIdx < halStreamsData->count) && (! subscriptionFound)) { if(! strcasecmp(currentSubscriptionString, halStreamsData->data[searchIdx].verb)) { if(halStreamsData->data[searchIdx].event && subscribeUnsubscribeType == SUBSCRIPTION && afb_req_subscribe(request, halStreamsData->data[searchIdx].event)) { AFB_ReqFailF(request, "request_stream_event", "Error while trying to subscribe to %s stream events", halStreamsData->data[searchIdx].verb); return; } else if(halStreamsData->data[searchIdx].event && subscribeUnsubscribeType == UNSUBSCRIPTION && afb_req_unsubscribe(request, halStreamsData->data[searchIdx].event)) { AFB_ReqFailF(request, "request_stream_event", "Error while trying to unsubscribe to %s stream events", halStreamsData->data[searchIdx].verb); return; } subscriptionFound = 1; subscriptionDoneNb++; break; } searchIdx++; } searchIdx = 0; while((searchIdx < (halAlsaMapT ? halAlsaMapT->ctlsCount : 0)) && (! subscriptionFound)) { if(! strcasecmp(currentSubscriptionString, halAlsaMapT->ctls[searchIdx].uid)) { if(halAlsaMapT->ctls[searchIdx].alsaControlEvent && subscribeUnsubscribeType == SUBSCRIPTION && afb_req_subscribe(request, halAlsaMapT->ctls[searchIdx].alsaControlEvent)) { AFB_ReqFailF(request, "request_control_event", "Error while trying to subscribe to %s halmap controls events", halAlsaMapT->ctls[searchIdx].uid); return; } else if(halAlsaMapT->ctls[searchIdx].alsaControlEvent && subscribeUnsubscribeType == UNSUBSCRIPTION && afb_req_unsubscribe(request, halAlsaMapT->ctls[searchIdx].alsaControlEvent)) { AFB_ReqFailF(request, "request_stream_event", "Error while trying to unsubscribe to %s halmap controls events", halAlsaMapT->ctls[searchIdx].uid); return; } subscriptionFound = 1; subscriptionDoneNb++; break; } searchIdx++; } } if(subscriptionDoneNb == 0) AFB_ReqFailF(request, "events_not_found", "%s failed, event(s) were not found", subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription"); if(subscriptionDoneNb == count) AFB_ReqSuccessF(request, json_object_new_int(subscriptionDoneNb), "%s succeed for all the %i events requested", subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription", subscriptionDoneNb); else if(subscriptionDoneNb < count) AFB_ReqSuccessF(request, json_object_new_int(subscriptionDoneNb), "%s succeed but only to %i events requested out of %i", subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription", subscriptionDoneNb, count); else AFB_ReqSuccessF(request, json_object_new_int(subscriptionDoneNb), "%s succeed but to more events than requested (%i out of %i)", subscribeUnsubscribeType == SUBSCRIPTION ? "Subscription" : "Unsubscription", subscriptionDoneNb, count); } void HalCtlsSubscribe(AFB_ReqT request) { HalCtlsSubscribeUnsubscribe(request, SUBSCRIPTION); } void HalCtlsUnsubscribe(AFB_ReqT request) { HalCtlsSubscribeUnsubscribe(request, UNSUBSCRIPTION); }