/* * 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-verbs-loader.h" #include "4a-hal-controllers-api-loader.h" #include "4a-hal-controllers-alsacore-link.h" #include "4a-hal-controllers-cb.h" #include "4a-hal-controllers-mixer-link.h" // Default api to print log when apihandle not available AFB_ApiT AFB_default; /******************************************************************************* * Json parsing functions using app controller * ******************************************************************************/ // Config Section definition static CtlSectionT ctrlSectionsDefault[] = { { .key = "resources", .loadCB = PluginConfig }, { .key = "halmixer", .loadCB = HalCtlsHalMixerConfig }, { .key = "onload", .loadCB = OnloadConfig }, { .key = "controls", .loadCB = ControlConfig }, { .key = "events", .loadCB = EventConfig }, { .key = "halmap", .loadCB = HalCtlsHalMapConfig }, { .key = NULL } }; /******************************************************************************* * Dynamic HAL verbs' functions * ******************************************************************************/ // Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT static AFB_ApiVerbs CtlHalApiStaticVerbs[] = { /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ { .verb = "info", .callback = HalCtlsInfo, .info = "List available streams/playbacks/captures/controls for this api" }, { .verb = "subscribe", .callback = HalCtlsSubscribe, .info = "Subscribe to event(s) for values changes (streams/playbacks/captures/controls) for this api" }, { .verb = "unsubscribe", .callback = HalCtlsUnsubscribe, .info = "Unsubscribe to event(s) for values changes (streams/playbacks/captures/controls) for this api" }, { .verb = NULL } // Marker for end of the array }; /******************************************************************************* * Dynamic API functions for app controller * ******************************************************************************/ static int HalCtlsInitOneApi(AFB_ApiT apiHandle) { CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData; if(! apiHandle) return -1; // Hugely hack to make all V2 AFB_DEBUG to work in fileutils AFB_default = apiHandle; // Retrieve section config from api handle ctrlConfig = (CtlConfigT *) AFB_ApiGetUserData(apiHandle); if(! ctrlConfig) return -2; currentCtlHalData = (struct SpecificHalData *) getExternalData(ctrlConfig); if(! currentCtlHalData) return -3; // Fill SpecificHalDatadata structure currentCtlHalData->internal = 1; currentCtlHalData->sndCardPath = (char *) ctrlConfig->uid; currentCtlHalData->info = (char *) ctrlConfig->info; currentCtlHalData->author = (char *) ctrlConfig->author; currentCtlHalData->version = (char *) ctrlConfig->version; currentCtlHalData->date = (char *) ctrlConfig->date; currentCtlHalData->ctlHalSpecificData->apiHandle = apiHandle; currentCtlHalData->ctlHalSpecificData->ctrlConfig = ctrlConfig; currentCtlHalData->sndCardId = HalCtlsGetCardIdByCardPath(apiHandle, currentCtlHalData->sndCardPath); currentCtlHalData->ctlHalSpecificData->streamUpdates = AFB_EventMake(apiHandle, HAL_STREAM_UPDATES_EVENT_NAME); if(! AFB_EventIsValid(currentCtlHalData->ctlHalSpecificData->streamUpdates)) return -4; if(currentCtlHalData->sndCardId < 0) currentCtlHalData->status = HAL_STATUS_UNAVAILABLE; else currentCtlHalData->status = HAL_STATUS_AVAILABLE; // TBD JAI: handle refresh of hal status for dynamic card (/dev/by-id) return CtlConfigExec(apiHandle, ctrlConfig); } static int HalCtlsLoadOneApi(void *cbdata, AFB_ApiT apiHandle) { int err; CtlConfigT *ctrlConfig; CtlSectionT *ctrlCurrentSections; if(! cbdata || ! apiHandle) return -1; ctrlConfig = (CtlConfigT*) cbdata; // Save closure as api's data context AFB_ApiSetUserData(apiHandle, ctrlConfig); // Add static controls verbs if(HalUtlLoadVerbs(apiHandle, CtlHalApiStaticVerbs)) { AFB_ApiError(apiHandle, "Load Section : fail to register static V2 verbs"); return 1; } ctrlCurrentSections = malloc(sizeof(ctrlSectionsDefault)); memcpy(ctrlCurrentSections, ctrlSectionsDefault, sizeof(ctrlSectionsDefault)); // Load section for corresponding Api if((err = CtlLoadSections(apiHandle, ctrlConfig, ctrlCurrentSections))) return err; // Declare an event manager for this Api AFB_ApiOnEvent(apiHandle, HalCtlsDispatchApiEvent); // Init Api function (does not receive user closure ???) AFB_ApiOnInit(apiHandle, HalCtlsInitOneApi); return 0; } int HalCtlsCreateApi(AFB_ApiT apiHandle, char *path, struct HalMgrData *HalMgrGlobalData) { CtlConfigT *ctrlConfig; struct SpecificHalData *currentCtlHalData; if(! apiHandle || ! path || ! HalMgrGlobalData) return -1; // Create one Api per file ctrlConfig = CtlLoadMetaData(apiHandle, path); if(! ctrlConfig) { AFB_ApiError(apiHandle, "No valid control config file in:\n-- %s", path); return -2; } if(! ctrlConfig->api) { AFB_ApiError(apiHandle, "API Missing from metadata in:\n-- %s", path); return -3; } // Allocation of current hal controller data currentCtlHalData = HalUtlAddHalApiToHalList(&HalMgrGlobalData->first); if(! currentCtlHalData) return -4; currentCtlHalData->apiName = (char *) ctrlConfig->api; // Stores current hal controller data in controller config setExternalData(ctrlConfig, (void *) currentCtlHalData); // Allocation of the structure that will be used to store specific hal controller data currentCtlHalData->ctlHalSpecificData = calloc(1, sizeof(struct CtlHalSpecificData)); // Create one API (Pre-V3 return code ToBeChanged) if(! AFB_NewApi(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, HalCtlsLoadOneApi, ctrlConfig)) return -5; return 0; } int HalCtlsCreateAllApi(AFB_ApiT apiHandle, struct HalMgrData *HalMgrGlobalData) { int index, status = 0; char *dirList, *fileName, *fullPath; char filePath[CONTROL_MAXPATH_LEN]; filePath[CONTROL_MAXPATH_LEN - 1] = '\0'; json_object *configJ, *entryJ; if(! apiHandle || ! HalMgrGlobalData) return -1; // Hugely hack to make all V2 AFB_DEBUG to work in fileutils AFB_default = apiHandle; AFB_ApiNotice(apiHandle, "Begining to create all APIs"); dirList = getenv("CONTROL_CONFIG_PATH"); if(! dirList) dirList = CONTROL_CONFIG_PATH; configJ = CtlConfigScan(dirList, "hal"); if(! configJ) { AFB_ApiWarning(apiHandle, "No hal-(binder-middle-name)*.json config file(s) found in %s, 4a-hal-manager will only works with external hal", dirList); return 0; } // We load 1st file others are just warnings for(index = 0; index < (int) json_object_array_length(configJ); index++) { entryJ = json_object_array_get_idx(configJ, index); if(wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullPath, "filename", &fileName)) { AFB_ApiError(apiHandle, "HOOPs invalid JSON entry = %s", json_object_get_string(entryJ)); return -2; } strncpy(filePath, fullPath, sizeof(filePath) - 1); strncat(filePath, "/", sizeof(filePath) - 1); strncat(filePath, fileName, sizeof(filePath) - 1); if(HalCtlsCreateApi(apiHandle, filePath, HalMgrGlobalData) < 0) status--; } return status; }