summaryrefslogtreecommitdiffstats
path: root/4a-hal/4a-hal-controllers/4a-hal-controllers-api-loader.c
blob: f6acaef484a2bfcab51a98ba6f2ac7bf049ddf87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * Copyright (C) 2018 "IoT.bzh"
 * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
 *
 * 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 <stdio.h>
#include <string.h>

#include <filescan-utils.h>
#include <wrap-json.h>

#include <afb-definitions.h>

#include <ctl-config.h>

#include "../4a-hal-utilities/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 CtlHalDynApiStaticVerbs[] =
{
	/* 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		       *
 *		TBD JAI : Use API-V3 instead of API-PRE-V3		       *
 ******************************************************************************/

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_dynapi_get_userdata(apiHandle);
	if(! ctrlConfig)
		return -2;

	currentCtlHalData = (struct SpecificHalData *) ctrlConfig->external;
	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->ctlHalSpecificData->ctlHalStreamsData.count = 0;

	currentCtlHalData->sndCardId = HalCtlsGetCardIdByCardPath(apiHandle, currentCtlHalData->sndCardPath);

	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_dynapi_set_userdata(apiHandle, ctrlConfig);

	// Add static controls verbs
	if(HalUtlLoadVerbs(apiHandle, CtlHalDynApiStaticVerbs)) {
		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_dynapi_on_event(apiHandle, HalCtlsDispatchApiEvent);

	// Init Api function (does not receive user closure ???)
	afb_dynapi_on_init(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
	ctrlConfig->external = (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_dynapi_new_api(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;
}