/* * 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-internals-hal-api-loader.h" #include "4a-internals-hal-alsacore-link.h" #include "4a-internals-hal-cb.h" #include "4a-internals-hal-mixer-link.h" /******************************************************************************* * Json parsing functions using controller * ******************************************************************************/ // Config Section definition static CtlSectionT ctrlSectionsDefault[] = { { .key = "resources", .loadCB = PluginConfig }, { .key = "halmixer", .loadCB = InternalHalHalMixerConfig }, { .key = "onload", .loadCB = OnloadConfig }, { .key = "controls", .loadCB = ControlConfig }, { .key = "events", .loadCB = EventConfig }, { .key = "halmap", .loadCB = InternalHalHalMapConfig }, { .key = NULL } }; /******************************************************************************* * Dynamic HAL verbs' functions * ******************************************************************************/ // Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT static afb_verb_t InternalHalApiStaticVerbs[] = { /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ { .verb = "info", .callback = InternalHalInfo, .info = "List available streams/controls for this api" }, { .verb = "subscribe", .callback = InternalHalSubscribe, .info = "Subscribe to event(s) for values changes (streams/controls) for this api" }, { .verb = "unsubscribe", .callback = InternalHalUnsubscribe, .info = "Unsubscribe to event(s) for values changes (streams/controls) for this api" }, { .verb = NULL } // Marker for end of the array }; /******************************************************************************* * Dynamic API functions for internals hal * ******************************************************************************/ static int InternalHalInitOneApi(afb_api_t apiHandle) { CtlConfigT *ctrlConfig; struct HalData *currentHalData; if(! apiHandle) return -1; // Retrieve section config from api handle ctrlConfig = (CtlConfigT *) afb_api_get_userdata(apiHandle); if(! ctrlConfig) return -2; currentHalData = (struct HalData *) getExternalData(ctrlConfig); if(! currentHalData) return -3; // Fill HalDatadata structure currentHalData->internal = 1; currentHalData->sndCardPath = (char *) ctrlConfig->uid; currentHalData->uid = (char *) ctrlConfig->uid; currentHalData->info = (char *) ctrlConfig->info; currentHalData->author = (char *) ctrlConfig->author; currentHalData->version = (char *) ctrlConfig->version; currentHalData->date = (char *) ctrlConfig->date; currentHalData->internalHalData->apiHandle = apiHandle; currentHalData->internalHalData->ctrlConfig = ctrlConfig; currentHalData->sndCardId = InternalHalGetCardIdByCardPath(apiHandle, currentHalData->sndCardPath); currentHalData->internalHalData->streamUpdates = afb_api_make_event(apiHandle, HAL_STREAM_UPDATES_EVENT_NAME); if(! currentHalData->internalHalData->streamUpdates) return -4; if(currentHalData->sndCardId < 0) currentHalData->status = HAL_STATUS_UNAVAILABLE; else currentHalData->status = HAL_STATUS_AVAILABLE; // TBD JAI: handle refresh of hal status for dynamic card (/dev/by-id) return CtlConfigExec(apiHandle, ctrlConfig); } static int InternalHalLoadOneApi(void *cbdata, afb_api_t apiHandle) { int err; CtlConfigT *ctrlConfig; CtlSectionT *ctrlCurrentSections; if(! cbdata || ! apiHandle) return -1; ctrlConfig = (CtlConfigT*) cbdata; // Save closure as api's data context afb_api_set_userdata(apiHandle, ctrlConfig); // Add static internal hal verbs if(afb_api_set_verbs_v3(apiHandle, InternalHalApiStaticVerbs)) { AFB_API_ERROR(apiHandle, "Load Section : fail to register static V2 verbs"); return -2; } ctrlCurrentSections = malloc(sizeof(ctrlSectionsDefault)); if(! ctrlCurrentSections) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate current internal hal section data structure for controller"); return -3; } memcpy(ctrlCurrentSections, ctrlSectionsDefault, sizeof(ctrlSectionsDefault)); // Load section for corresponding Api err = CtlLoadSections(apiHandle, ctrlConfig, ctrlCurrentSections); if(err) { AFB_API_ERROR(apiHandle, "Didn't succeed to load current internal hal controller section"); return -4; } // Declare an event manager for this Api afb_api_on_event(apiHandle, InternalHalDispatchApiEvent); // Init Api function (does not receive user closure ???) afb_api_on_init(apiHandle, InternalHalInitOneApi); return 0; } int InternalHalCreateApi(afb_api_t apiHandle, char *path, struct HalMgrData *halMgrData) { CtlConfigT *ctrlConfig; struct HalData *currentHalData; if(! apiHandle || ! path || ! halMgrData) return -1; // Create one Api per file ctrlConfig = CtlLoadMetaData(apiHandle, path); if(! ctrlConfig) { AFB_API_ERROR(apiHandle, "No valid internal hal config file in : '%s'", path); return -2; } if(! ctrlConfig->api) { AFB_API_ERROR(apiHandle, "API Missing from metadata in : '%s'", path); return -3; } // Allocation of current internal hal data currentHalData = HalUtlAddHalToHalList(&halMgrData->halDataList); if(! currentHalData) { AFB_API_ERROR(apiHandle, "Didn't succeed to add hal to hal list"); return -4; } currentHalData->apiName = (char *) ctrlConfig->api; // Stores hal data in controller config setExternalData(ctrlConfig, (void *) currentHalData); // Allocation of the structure that will be used to store internal hal data currentHalData->internalHalData = calloc(1, sizeof(struct InternalHalData)); if(! currentHalData->internalHalData) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate internal hal data structure"); return -5; } // Create one API if(! afb_api_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, InternalHalLoadOneApi, ctrlConfig)) { AFB_API_ERROR(apiHandle, "An error occurred at '%s' internal hal api creation", ctrlConfig->api); return -6; } return 0; } int InternalHalCreateAllApi(afb_api_t apiHandle, struct HalMgrData *halMgrData) { int index; char *dirList, *fileName, *fullPath; char filePath[CONTROL_MAXPATH_LEN]; filePath[CONTROL_MAXPATH_LEN - 1] = '\0'; json_object *configJ, *entryJ; if(! apiHandle || ! halMgrData) return -1; AFB_API_NOTICE(apiHandle, "Begining to create all APIs"); dirList = getenv("CONTROL_CONFIG_PATH"); if(! dirList) dirList = CONTROL_CONFIG_PATH; configJ = CtlConfigScan(dirList, "hal"); if(! configJ) { AFB_API_WARNING(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_API_ERROR(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(InternalHalCreateApi(apiHandle, filePath, halMgrData) < 0) return -3; } return 0; }