2 * Copyright (C) 2016 "IoT.bzh"
3 * Author Fulup Ar Foll <fulup@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 * https://gist.github.com/ghedo/963382
19 * http://alsa-utils.sourcearchive.com/documentation/1.0.15/aplay_8c-source.html
24 #define BUFFER_FRAME_COUNT 10 /* max frames in buffer */
25 #define WAIT_TIMER_US 1000000 /* default waiting timer 1s */
26 #define I2C_MAX_DATA_SZ 32 /* max. number of bytes to be written to i2c */
27 #define CTRL_MAX_DATA_SZ 45 /* max. number of bytes to be written to control
30 #include <systemd/sd-event.h>
31 #include <sys/types.h>
42 #include "ucs_binding.h"
43 #include "ucs_interface.h"
44 #include <wrap-json.h>
46 #define MAX_FILENAME_LEN (100)
47 #define RX_BUFFER (64)
48 #define XML_CONFIG_FOLDER "/var/"
49 #define XML_CONFIG_FILE "config_multichannel_audio_kit.xml"
51 /** Internal structure, enabling multiple instances of this component.
52 * \note Do not access any of this variables.
57 char fileName[MAX_FILENAME_LEN];
58 uint8_t rxBuffer[RX_BUFFER];
67 UcsXmlVal_t* ucsConfig;
71 afb_event_t node_event;
78 static ucsContextT *ucsContextS = NULL;
79 static EventData_t *eventData = NULL;
80 static EventDataRx_t *eventDataRx = NULL;
82 PUBLIC void UcsXml_CB_OnError(const char format[], uint16_t vargsCnt, ...) {
83 /*AFB_API_DEBUG (afbBindingRoot, afbIface, format, args); */
85 va_start (args, vargsCnt);
86 vfprintf (stderr, format, args);
91 va_start(argptr, vargsCnt);
92 vsprintf(outbuf, format, argptr);
94 AFB_API_WARNING (afbBindingRoot, "%s", outbuf);
97 PUBLIC uint16_t UCSI_CB_OnGetTime(void *pTag) {
98 struct timespec currentTime;
102 if (clock_gettime(CLOCK_MONOTONIC_RAW, ¤tTime)) {
107 timer = (uint16_t) ((currentTime.tv_sec * 1000 ) + ( currentTime.tv_nsec / 1000000 ));
111 STATIC int onTimerCB (sd_event_source* source,uint64_t timer, void* pTag) {
112 ucsContextT *ucsContext = (ucsContextT*) pTag;
114 sd_event_source_unref(source);
115 UCSI_Timeout(&ucsContext->ucsiData);
120 void UCSI_CB_OnNetworkState(void *pTag, bool isAvailable, uint16_t packetBandwidth, uint8_t amountOfNodes)
122 AFB_API_NOTICE (afbBindingRoot, "Network is available=%d, bw=%d, nodeCnt=%d", isAvailable, packetBandwidth, amountOfNodes);
125 /* UCS2 Interface Timer Callback */
126 PUBLIC void UCSI_CB_OnSetServiceTimer(void *pTag, uint16_t timeout) {
128 /* set a timer with 250ms accuracy */
129 sd_event_now(afb_api_get_event_loop(afbBindingRoot), CLOCK_BOOTTIME, &usec);
130 sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, usec + (timeout*1000), 250, onTimerCB, pTag);
135 * \brief Callback when ever an Unicens forms a human readable message.
136 * This can be error events or when enabled also debug messages.
137 * \note This function must be implemented by the integrator
138 * \param pTag - Pointer given by the integrator by UCSI_Init
139 * \param format - Zero terminated format string (following printf rules)
140 * \param vargsCnt - Amount of parameters stored in "..."
142 void UCSI_CB_OnUserMessage(void *pTag, bool isError, const char format[],
143 uint16_t vargsCnt, ...) {
147 va_start(argptr, vargsCnt);
148 vsprintf(outbuf, format, argptr);
150 AFB_API_NOTICE (afbBindingRoot, "%s",outbuf);
153 /** UCSI_Service cannot be called directly within UNICENS context, need to service stack through mainloop */
154 STATIC int OnServiceRequiredCB (sd_event_source *source, uint64_t usec, void *pTag) {
155 ucsContextT *ucsContext = (ucsContextT*) pTag;
157 sd_event_source_unref(source);
158 UCSI_Service(&ucsContext->ucsiData);
162 /* UCS Callback fire when ever UNICENS needs to be serviced */
163 PUBLIC void UCSI_CB_OnServiceRequired(void *pTag) {
165 /* push an asynchronous request for loopback to call UCSI_Service */
166 sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, 0, 0, OnServiceRequiredCB, pTag);
169 /* Callback when ever this UNICENS wants to send a message to INIC. */
170 PUBLIC void UCSI_CB_OnTxRequest(void *pTag, const uint8_t *pData, uint32_t len) {
171 ucsContextT *ucsContext = (ucsContextT*) pTag;
172 CdevData_t *cdevTx = &ucsContext->tx;
175 if (NULL == pData || 0 == len) return;
177 if (O_RDONLY == cdevTx->fileFlags) return;
178 if (-1 == cdevTx->fileHandle)
179 cdevTx->fileHandle = open(cdevTx->fileName, cdevTx->fileFlags);
180 if (-1 == cdevTx->fileHandle)
184 ssize_t written = write(cdevTx->fileHandle, &pData[total], (len - total));
187 /* Silently ignore write error (only occur in non-blocking mode) */
190 total += (uint32_t) written;
194 /** UcsXml_FreeVal be called directly within UNICENS context, need to service stack through mainloop */
195 STATIC int OnStopCB (sd_event_source *source, uint64_t usec, void *pTag) {
196 if (NULL != ucsContextS && NULL != ucsContextS->ucsConfig) {
197 UcsXml_FreeVal(ucsContextS->ucsConfig);
198 ucsContextS->ucsConfig = NULL;
204 * \brief Callback when UNICENS instance has been stopped.
205 * \note This event can be used to free memory holding the resources
206 * passed with UCSI_NewConfig
207 * \note This function must be implemented by the integrator
208 * \param pTag - Pointer given by the integrator by UCSI_Init
210 void UCSI_CB_OnStop(void *pTag) {
211 AFB_API_NOTICE (afbBindingRoot, "UNICENS stopped");
212 /* push an asynchronous request for loopback to call UcsXml_FreeVal */
213 sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, 0, 0, OnStopCB, pTag);
216 /* helper function: wraps Rx message in json and triggers notification */
217 STATIC void NotifyEventRxMsg(uint16_t src_addr, uint16_t msg_id, uint8_t *data_ptr, uint32_t data_sz) {
222 if (data_sz > CTRL_MAX_DATA_SZ) {
223 AFB_API_NOTICE(afbBindingRoot, "RX-MSG: discarded, payload exceeds %d bytes", CTRL_MAX_DATA_SZ);
227 json_object *j_query = NULL;
228 int node = (int)src_addr;
229 int msgid = (int)msg_id;
230 size_t data_size = (size_t)data_sz;
232 /* skip data attribute if possible, wrap_json_unpack may fail to deal with
233 * an empty Base64 string */
235 wrap_json_pack(&j_query, "{s:i, s:i, s:Y*}", "node", node, "msgid", msgid, "data", data_ptr, data_size);
237 wrap_json_pack(&j_query, "{s:i, s:i}", "node", node, "msgid", msgid);
239 afb_event_push(eventDataRx->rx_event, j_query);
242 /** Asynchronous processing of Rx messages in mainloop is recommended */
243 STATIC int OnAmsMessageReceivedCB (sd_event_source *source, void *pTag) {
244 ucsContextT *ucsContext = (ucsContextT*) pTag;
245 uint32_t data_sz = 0U;
246 uint8_t *data_ptr = NULL;
247 uint16_t msg_id = 0U;
248 uint16_t src_addr = 0U;
250 while (UCSI_GetAmsMessage(&ucsContext->ucsiData, &msg_id, &src_addr, &data_ptr, &data_sz)) {
251 NotifyEventRxMsg(src_addr, msg_id, data_ptr, data_sz);
252 AFB_API_DEBUG(afbBindingRoot, "RX-MSG: src=0x%04X, msg_id=0x%04X, size=%d", src_addr, msg_id, data_sz);
253 UCSI_ReleaseAmsMessage(&ucsContext->ucsiData);
259 /** This callback will be raised, when ever an applicative message on the control channel arrived */
260 void UCSI_CB_OnAmsMessageReceived(void *pTag)
262 static sd_event_source *src_ptr = NULL;
266 /* first time usage: create and trigger event source */
267 sd_event_add_defer(afb_api_get_event_loop(afbBindingRoot), &src_ptr, &OnAmsMessageReceivedCB, pTag);
271 sd_event_source_set_enabled(src_ptr, SD_EVENT_ONESHOT);
275 void UCSI_CB_OnRouteResult(void *pTag, uint16_t routeId, bool isActive, uint16_t connectionLabel)
277 AFB_API_NOTICE (afbBindingRoot, "Route 0x%X is active=%d, connection label=0x%X", routeId, isActive, connectionLabel);
280 void UCSI_CB_OnGpioStateChange(void *pTag, uint16_t nodeAddress, uint8_t gpioPinId, bool isHighState)
282 AFB_API_NOTICE (afbBindingRoot, "GPIO state of node 0x%X changed, pin=%d isHigh=%d", nodeAddress, gpioPinId, isHighState);
285 PUBLIC void UCSI_CB_OnMgrReport(void *pTag, Ucs_MgrReport_t code, uint16_t nodeAddress, Ucs_Rm_Node_t *pNode){
289 if (code == UCS_MGR_REP_AVAILABLE) {
292 else if (code == UCS_MGR_REP_NOT_AVAILABLE) {
296 /*untracked event - just exit*/
302 json_object *j_event_info = json_object_new_object();
303 json_object_object_add(j_event_info, "node", json_object_new_int(nodeAddress));
304 json_object_object_add(j_event_info, "available", json_object_new_boolean(available));
306 afb_event_push(eventData->node_event, j_event_info);
310 bool Cdev_Init(CdevData_t *d, const char *fileName, bool read, bool write)
312 if (NULL == d || NULL == fileName) goto OnErrorExit;
314 memset(d, 0, sizeof(CdevData_t));
315 strncpy(d->fileName, fileName, MAX_FILENAME_LEN);
319 d->fileFlags = O_RDWR | O_NONBLOCK;
321 d->fileFlags = O_RDONLY | O_NONBLOCK;
323 d->fileFlags = O_WRONLY | O_NONBLOCK;
325 /* open file to enable event loop */
326 d->fileHandle = open(d->fileName, d->fileFlags);
327 if (d->fileHandle <= 0) goto OnErrorExit;
335 static bool InitializeCdevs(ucsContextT *ucsContext)
337 if(!Cdev_Init(&ucsContext->tx, CONTROL_CDEV_TX, false, true))
339 if(!Cdev_Init(&ucsContext->rx, CONTROL_CDEV_RX, true, false))
344 /* Callback fire when something is avaliable on MOST cdev */
345 int onReadCB (sd_event_source* src, int fileFd, uint32_t revents, void* pTag) {
346 ucsContextT *ucsContext =( ucsContextT*) pTag;
348 uint8_t pBuffer[RX_BUFFER];
351 len = read (ucsContext->rx.fileHandle, &pBuffer, sizeof(pBuffer));
354 ok= UCSI_ProcessRxData(&ucsContext->ucsiData, pBuffer, (uint16_t)len);
356 AFB_API_DEBUG (afbBindingRoot, "Buffer overrun (not handle)");
357 /* Buffer overrun could replay pBuffer */
363 STATIC char* GetDefaultConfig(void) {
365 char const *data_path = getenv("AFM_APP_INSTALL_DIR");
368 AFB_API_ERROR(afbBindingRoot, "AFM_APP_INSTALL_DIR is not defined");
374 AFB_API_NOTICE(afbBindingRoot, "AFM_APP_INSTALL_DIR is: %s", data_path);
375 size = strlen(data_path) + strlen(XML_CONFIG_FOLDER) + strlen(XML_CONFIG_FILE) + 2;
376 config_path = malloc(size);
377 if (config_path != NULL) {
378 snprintf(config_path, size, "%s%s%s", data_path, XML_CONFIG_FOLDER, XML_CONFIG_FILE);
379 if(access(config_path, R_OK ) == 0) {
380 AFB_API_NOTICE(afbBindingRoot, "Default configuration: %s", config_path);
389 STATIC UcsXmlVal_t* ParseFile(const char *filename) {
394 UcsXmlVal_t *ucsConfig = NULL;
396 fdHandle = open(filename, O_RDONLY);
398 AFB_API_ERROR(afbBindingRoot, "File not accessible: '%s' err=%s", filename, strerror(fdHandle));
402 /* read file into buffer as a \0 terminated string */
403 fstat(fdHandle, &fdStat);
404 xmlBuffer = (char*)alloca(fdStat.st_size + 1);
405 readSize = read(fdHandle, xmlBuffer, fdStat.st_size);
407 xmlBuffer[readSize] = '\0'; /* In any case, terminate it. */
409 if (readSize != fdStat.st_size) {
410 AFB_API_ERROR(afbBindingRoot, "File to read fullfile '%s' size(%d!=%d)", filename, (int)readSize, (int)fdStat.st_size);
414 ucsConfig = UcsXml_Parse(xmlBuffer);
416 AFB_API_ERROR(afbBindingRoot, "File XML invalid: '%s'", filename);
419 AFB_API_NOTICE (afbBindingRoot, "Parsing result: %d Nodes, %d Scripts, Ethernet Bandwith %d bytes = %.2f MBit/s", ucsConfig->nodSize, ucsConfig->routesSize, ucsConfig->packetBw, (48 * 8 * ucsConfig->packetBw / 1000.0));
427 PUBLIC int StartConfiguration(const char *filename) {
428 static ucsContextT ucsContext = { 0 };
430 sd_event_source *evtSource;
433 /* Read and parse XML file */
434 ucsContext.ucsConfig = ParseFile(filename);
435 if (NULL == ucsContext.ucsConfig) {
436 AFB_API_ERROR (afbBindingRoot, "Cannot access or load file: '%s'", filename);
440 /* When ucsContextS is set, do not initalize UNICENS, CDEVs or system hooks, just load new XML */
443 if (!ucsContextS && !InitializeCdevs(&ucsContext)) {
444 AFB_API_ERROR (afbBindingRoot, "Fail to initialise device [rx=%s tx=%s]", CONTROL_CDEV_RX, CONTROL_CDEV_TX);
448 /* Initialise UNICENS Config Data Structure */
449 UCSI_Init(&ucsContext.ucsiData, &ucsContext);
451 /* register aplayHandle file fd into binder mainloop */
452 err = sd_event_add_io(afb_api_get_event_loop(afbBindingRoot), &evtSource, ucsContext.rx.fileHandle, EPOLLIN, onReadCB, &ucsContext);
454 AFB_API_ERROR (afbBindingRoot, "Cannot hook events to mainloop");
458 /* save this in a statical variable until ucs2vol move to C */
459 ucsContextS = &ucsContext;
461 /* Initialise UNICENS with parsed config */
462 if (!UCSI_NewConfig(&ucsContext.ucsiData, ucsContext.ucsConfig)) {
463 AFB_API_ERROR (afbBindingRoot, "Fail to initialize UNICENS");
473 PUBLIC void ucs2_initialise (afb_req_t request) {
474 const char *filename = afb_req_value(request, "filename");
477 afb_req_reply (request, NULL, "filename-missing", "No filename given");
481 if (StartConfiguration(filename) != 0) {
482 afb_req_reply (request, NULL, "load-failed", "Cannot parse file and start UNICENS");
486 afb_req_reply(request,NULL, NULL,"UNICENS-active");
493 // List Avaliable Configuration Files
494 PUBLIC void ucs2_listconfig (afb_req_t request) {
495 struct json_object *queryJ, *tmpJ, *responseJ;
497 char *dirPath, *dirList;
500 queryJ = afb_req_json(request);
501 if (queryJ && json_object_object_get_ex (queryJ, "cfgpath" , &tmpJ)) {
502 dirList = strdup (json_object_get_string(tmpJ));
504 dirList = strdup (UCS2_CFG_PATH);
505 AFB_API_NOTICE (afbBindingRoot, "fgpath:missing uses UCS2_CFG_PATH=%s", UCS2_CFG_PATH);
508 responseJ = json_object_new_array();
509 for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) {
510 struct dirent *dirEnt;
512 dirHandle = opendir (dirPath);
514 AFB_API_NOTICE (afbBindingRoot, "ucs2_listconfig dir=%s not readable", dirPath);
519 AFB_API_NOTICE (afbBindingRoot, "ucs2_listconfig scanning: %s", dirPath);
520 while ((dirEnt = readdir(dirHandle)) != NULL) {
521 // Unknown type is accepted to support dump filesystems
522 if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) {
523 struct json_object *pathJ = json_object_new_object();
524 json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath));
525 json_object_object_add(pathJ, "basename", json_object_new_string(dirEnt->d_name));
526 json_object_array_add(responseJ, pathJ);
533 if (!error) afb_req_reply(request,responseJ, NULL,NULL);
536 snprintf (info, sizeof(info), "[%d] where not scanned", error);
537 afb_req_reply(request,responseJ, NULL, info);
543 PUBLIC void ucs2_subscribe (afb_req_t request) {
547 eventData = malloc(sizeof(EventData_t));
549 eventData->node_event = afb_api_make_event (afbBindingRoot, "node-availibility");
552 if (!eventData || !afb_event_is_valid(eventData->node_event)) {
553 afb_req_reply (request, NULL, "create-event", "Cannot create or register event");
558 if (afb_req_subscribe(request, eventData->node_event) != 0) {
560 afb_req_reply (request, NULL, "subscribe-event", "Cannot subscribe to event");
564 afb_req_reply(request,NULL, NULL,"event subscription successful");
570 PUBLIC void ucs2_subscriberx (afb_req_t request) {
574 eventDataRx = malloc(sizeof(EventDataRx_t));
576 eventDataRx->rx_event = afb_api_make_event(afbBindingRoot, "rx-message");
579 if (!eventDataRx || !afb_event_is_valid(eventDataRx->rx_event)) {
580 afb_req_reply(request, NULL, "create-event", "Cannot create or register event");
585 if (afb_req_subscribe(request, eventDataRx->rx_event) != 0) {
587 afb_req_reply (request, NULL, "subscribe-event", "Cannot subscribe to event");
591 afb_req_reply(request,NULL, NULL,"event subscription successful");
597 static json_object * ucs2_validate_command (afb_req_t request,
598 const char* func_name) {
600 struct json_object *j_obj = NULL;
602 if (!ucsContextS) { /* check UNICENS is initialized */
603 afb_req_reply_f(request, NULL, "unicens-init",
604 "Load a configuration before calling %s.",
609 j_obj = afb_req_json(request);
611 afb_req_reply_f(request, NULL,
612 "query-notjson","query=%s not a valid json entry",
613 afb_req_value(request,""));
617 AFB_API_DEBUG(afbBindingRoot, "request: %s", json_object_to_json_string(j_obj));
619 if (json_object_get_type(j_obj)==json_type_array) {
620 int len = json_object_array_length(j_obj);
622 if (len == 1) { /* only support 1 command in array */
623 j_obj = json_object_array_get_idx(j_obj, 0);
626 afb_req_reply_f(request, NULL,
628 "query of multiple %s commands is not supported",
639 STATIC void ucs2_writei2c_CB (void *result_ptr, void *request_ptr) {
642 afb_req_t req = request_ptr;
643 Ucs_I2c_ResultCode_t *res = (Ucs_I2c_ResultCode_t *)result_ptr;
646 afb_req_reply(req, NULL, "processing","busy or lost initialization");
648 else if (*res != UCS_I2C_RES_SUCCESS){
649 afb_req_reply_f(req, NULL, "error-result", "result code: %d", *res);
652 afb_req_reply(req, NULL, NULL, "success");
658 AFB_API_NOTICE(afbBindingRoot, "write_i2c: ambiguous response data");
662 /* write a single i2c command */
663 STATIC void ucs2_writei2c_cmd(afb_req_t request, json_object *j_obj) {
665 static uint8_t i2c_data[I2C_MAX_DATA_SZ];
666 uint8_t i2c_data_sz = 0;
667 uint16_t node_addr = 0;
671 if (json_object_object_get_ex(j_obj, "node", &j_tmp)) {
672 node_addr = (uint16_t)json_object_get_int(j_tmp);
673 AFB_API_NOTICE(afbBindingRoot, "node_address: 0x%02X", node_addr);
674 if (node_addr == 0) {
675 afb_req_reply(request, NULL, "query-params","param node invalid type");
680 afb_req_reply(request, NULL, "query-params","param node missing");
684 key_found = json_object_object_get_ex(j_obj, "data", &j_tmp);
685 if (key_found && (json_object_get_type(j_tmp)==json_type_array)) {
686 int size = json_object_array_length(j_tmp);
687 if ((size > 0) && (size <= I2C_MAX_DATA_SZ)) {
691 struct json_object *j_elem;
693 for (i = 0; i < size; i++) {
695 j_elem = json_object_array_get_idx(j_tmp, i);
696 val = json_object_get_int(j_elem);
697 if ((val < 0) && (val > 0xFF)){
701 i2c_data[i] = (uint8_t)json_object_get_int(j_elem);
704 i2c_data_sz = (uint8_t)i;
708 if (i2c_data_sz == 0) {
709 AFB_API_NOTICE(afbBindingRoot, "data: invalid or not found");
710 afb_req_reply(request, NULL, "query-params","params wrong or missing");
714 if (UCSI_I2CWrite( &ucsContextS->ucsiData, /* UCSI_Data_t *pPriv*/
715 node_addr, /* uint16_t targetAddress*/
716 false, /* bool isBurst*/
717 0u, /* block count */
718 0x2Au, /* i2c slave address */
719 0x03E8u, /* timeout 1000 milliseconds */
720 i2c_data_sz, /* uint8_t dataLen */
721 &i2c_data[0], /* uint8_t *pData */
722 &ucs2_writei2c_CB, /* callback*/
723 (void*)request /* callback argument */
725 /* asynchronous command is running */
726 afb_req_addref(request);
729 AFB_API_NOTICE(afbBindingRoot, "i2c write: scheduling command failed");
730 afb_req_reply(request, NULL, "query-command-queue","command queue overload");
738 /* parse array or single command */
739 PUBLIC void ucs2_writei2c (afb_req_t request) {
741 struct json_object *j_obj;
743 j_obj = ucs2_validate_command(request, "writei2c");
746 ucs2_writei2c_cmd(request, j_obj);
750 PUBLIC void ucs2_sendmessage(afb_req_t req) {
751 uint8_t *data_ptr = NULL;
753 int ret, node_addr, msg_id = 0;
754 struct json_object *j_obj;
756 j_obj = ucs2_validate_command(req, "sendmessageb64");
759 AFB_API_NOTICE(afbBindingRoot, "validation of command failed");
763 ret = wrap_json_unpack(j_obj, "{s:i, s:i, s?Y}", "node", &node_addr, "msgid", &msg_id, "data", &data_ptr, &data_sz);
766 UCSI_SendAmsMessage(&ucsContextS->ucsiData, msg_id, node_addr, &data_ptr[0], data_sz)
768 afb_req_reply(req, NULL, NULL, "sendmessageb64 started successful");
771 AFB_API_ERROR(afbBindingRoot, "sendmessageb64: scheduling command failed. ret: %d", ret);
772 afb_req_reply(req, NULL, "query-command-queue","ambiguous command or queue overload");
783 PUBLIC int ucs2_initbinding(afb_api_t api) {
784 #ifndef DISABLE_AUTOSTART
785 char *filename = GetDefaultConfig();
786 if (filename != NULL) {
788 AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD configuration: %s", filename);
789 if (StartConfiguration(filename) == 0) {
790 AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD successful");
792 AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD failed");