unicens: support reproducible build
[apps/agl-service-unicens.git] / ucs2-afb / ucs_binding.c
1 /*
2  * Copyright (C) 2016 "IoT.bzh"
3  * Author Fulup Ar Foll <fulup@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  * references:
18  *   https://gist.github.com/ghedo/963382
19  *   http://alsa-utils.sourcearchive.com/documentation/1.0.15/aplay_8c-source.html
20  */
21
22 #define _GNU_SOURCE
23
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
28                                * channel */
29
30 #include <systemd/sd-event.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <time.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <dirent.h>
41
42 #include "ucs_binding.h"
43 #include "ucs_interface.h"
44 #include <wrap-json.h>
45
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"
50
51 /** Internal structure, enabling multiple instances of this component.
52  * \note Do not access any of this variables.
53  *  */
54 typedef struct {
55     int fileHandle;
56     int fileFlags;
57     char fileName[MAX_FILENAME_LEN];
58     uint8_t rxBuffer[RX_BUFFER];
59     uint32_t rxLen;
60 } CdevData_t;
61
62
63 typedef struct {
64   CdevData_t rx;
65   CdevData_t tx;
66   UCSI_Data_t ucsiData;
67   UcsXmlVal_t* ucsConfig;
68 } ucsContextT;
69
70 typedef struct {
71     afb_event_t node_event;
72 } EventData_t;
73
74 typedef struct {
75     afb_event_t rx_event;
76 } EventDataRx_t;
77
78 static ucsContextT *ucsContextS = NULL;
79 static EventData_t *eventData = NULL;
80 static EventDataRx_t *eventDataRx = NULL;
81
82 PUBLIC void UcsXml_CB_OnError(const char format[], uint16_t vargsCnt, ...) {
83     /*AFB_API_DEBUG (afbBindingRoot, afbIface, format, args); */
84     /*you may activite logging to stdout here
85     va_list args;
86     va_start (args, vargsCnt);
87     vfprintf (stderr, format, args);
88     va_end(args);*/
89
90     va_list argptr;
91     char outbuf[300];
92     va_start(argptr, vargsCnt);
93     vsprintf(outbuf, format, argptr);
94     va_end(argptr);
95     AFB_API_WARNING (afbBindingRoot, "%s", outbuf);
96 }
97
98 PUBLIC uint16_t UCSI_CB_OnGetTime(void *pTag) {
99     struct timespec currentTime;
100     uint16_t timer;
101     pTag = pTag;
102
103     if (clock_gettime(CLOCK_MONOTONIC_RAW, &currentTime))   {
104         assert(false);
105         return 0;
106     }
107
108     timer = (uint16_t) ((currentTime.tv_sec * 1000 ) + ( currentTime.tv_nsec / 1000000 ));
109     return(timer);
110 }
111
112 STATIC int onTimerCB (sd_event_source* source,uint64_t timer, void* pTag) {
113     ucsContextT *ucsContext = (ucsContextT*) pTag;
114
115     sd_event_source_unref(source);
116     UCSI_Timeout(&ucsContext->ucsiData);
117
118     return 0;
119 }
120
121 void UCSI_CB_OnNetworkState(void *pTag, bool isAvailable, uint16_t packetBandwidth, uint8_t amountOfNodes)
122 {
123     AFB_API_NOTICE (afbBindingRoot, "Network is available=%d, bw=%d, nodeCnt=%d", isAvailable, packetBandwidth, amountOfNodes);
124 }
125
126 /* UCS2 Interface Timer Callback */
127 PUBLIC void UCSI_CB_OnSetServiceTimer(void *pTag, uint16_t timeout) {
128   uint64_t usec;
129   /* set a timer with  250ms accuracy */
130   sd_event_now(afb_api_get_event_loop(afbBindingRoot), CLOCK_BOOTTIME, &usec);
131   sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, usec + (timeout*1000), 250, onTimerCB, pTag);
132
133 }
134
135 /**
136  * \brief Callback when ever an Unicens forms a human readable message.
137  *        This can be error events or when enabled also debug messages.
138  * \note This function must be implemented by the integrator
139  * \param pTag - Pointer given by the integrator by UCSI_Init
140  * \param format - Zero terminated format string (following printf rules)
141  * \param vargsCnt - Amount of parameters stored in "..."
142  */
143 void UCSI_CB_OnUserMessage(void *pTag, bool isError, const char format[],
144     uint16_t vargsCnt, ...) {
145     va_list argptr;
146     char outbuf[300];
147     pTag = pTag;
148     va_start(argptr, vargsCnt);
149     vsprintf(outbuf, format, argptr);
150     va_end(argptr);
151     AFB_API_NOTICE (afbBindingRoot, "%s",outbuf);
152 }
153
154 /** UCSI_Service cannot be called directly within UNICENS context, need to service stack through mainloop */
155 STATIC int OnServiceRequiredCB (sd_event_source *source, uint64_t usec, void *pTag) {
156     ucsContextT *ucsContext = (ucsContextT*) pTag;
157
158     sd_event_source_unref(source);
159     UCSI_Service(&ucsContext->ucsiData);
160     return (0);
161 }
162
163 /* UCS Callback fire when ever UNICENS needs to be serviced */
164 PUBLIC void UCSI_CB_OnServiceRequired(void *pTag) {
165
166    /* push an asynchronous request for loopback to call UCSI_Service */
167    sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, 0, 0, OnServiceRequiredCB, pTag);
168 }
169
170 /* Callback when ever this UNICENS wants to send a message to INIC. */
171 PUBLIC void UCSI_CB_OnTxRequest(void *pTag, const uint8_t *pData, uint32_t len) {
172     ucsContextT *ucsContext = (ucsContextT*) pTag;
173     CdevData_t *cdevTx = &ucsContext->tx;
174     uint32_t total = 0;
175
176     if (NULL == pData || 0 == len) return;
177
178     if (O_RDONLY == cdevTx->fileFlags) return;
179     if (-1 == cdevTx->fileHandle)
180         cdevTx->fileHandle = open(cdevTx->fileName, cdevTx->fileFlags);
181     if (-1 == cdevTx->fileHandle)
182         return;
183
184     while(total < len) {
185         ssize_t written = write(cdevTx->fileHandle, &pData[total], (len - total));
186         if (0 >= written)
187         {
188             /* Silently ignore write error (only occur in non-blocking mode) */
189             break;
190         }
191         total += (uint32_t) written;
192     }
193 }
194
195 /** UcsXml_FreeVal be called directly within UNICENS context, need to service stack through mainloop */
196 STATIC int OnStopCB (sd_event_source *source, uint64_t usec, void *pTag) {
197     if (NULL != ucsContextS && NULL != ucsContextS->ucsConfig) {
198         UcsXml_FreeVal(ucsContextS->ucsConfig);
199         ucsContextS->ucsConfig = NULL;
200     }
201     return 0;
202 }
203
204 /**
205  * \brief Callback when UNICENS instance has been stopped.
206  * \note This event can be used to free memory holding the resources
207  *       passed with UCSI_NewConfig
208  * \note This function must be implemented by the integrator
209  * \param pTag - Pointer given by the integrator by UCSI_Init
210  */
211 void UCSI_CB_OnStop(void *pTag) {
212    AFB_API_NOTICE (afbBindingRoot, "UNICENS stopped");
213    /* push an asynchronous request for loopback to call UcsXml_FreeVal */
214    sd_event_add_time(afb_api_get_event_loop(afbBindingRoot), NULL, CLOCK_MONOTONIC, 0, 0, OnStopCB, pTag);
215 }
216
217 /* helper function: wraps Rx message in json and triggers notification */
218 STATIC void NotifyEventRxMsg(uint16_t src_addr, uint16_t msg_id, uint8_t *data_ptr, uint32_t data_sz) {
219
220     if (!eventDataRx)
221         return;
222
223     if (data_sz > CTRL_MAX_DATA_SZ) {
224         AFB_API_NOTICE(afbBindingRoot, "RX-MSG: discarded, payload exceeds %d bytes", CTRL_MAX_DATA_SZ);
225         return;
226     }
227
228     json_object *j_query = NULL;
229     int node = (int)src_addr;
230     int msgid = (int)msg_id;
231     size_t data_size = (size_t)data_sz;
232
233     /* skip data attribute if possible, wrap_json_unpack may fail to deal with
234      * an empty Base64 string */
235     if (data_size > 0)
236         wrap_json_pack(&j_query, "{s:i, s:i, s:Y*}", "node", node, "msgid", msgid, "data", data_ptr, data_size);
237     else
238         wrap_json_pack(&j_query, "{s:i, s:i}", "node", node, "msgid", msgid);
239
240     afb_event_push(eventDataRx->rx_event, j_query);
241 }
242
243 /** Asynchronous processing of Rx messages in mainloop is recommended */
244 STATIC int OnAmsMessageReceivedCB (sd_event_source *source, void *pTag) {
245     ucsContextT *ucsContext = (ucsContextT*) pTag;
246     uint32_t data_sz = 0U;
247     uint8_t *data_ptr = NULL;
248     uint16_t msg_id = 0U;
249     uint16_t src_addr = 0U;
250
251     while (UCSI_GetAmsMessage(&ucsContext->ucsiData, &msg_id, &src_addr, &data_ptr, &data_sz)) {
252         NotifyEventRxMsg(src_addr, msg_id, data_ptr, data_sz);
253         AFB_API_DEBUG(afbBindingRoot, "RX-MSG: src=0x%04X, msg_id=0x%04X, size=%d", src_addr, msg_id, data_sz);
254         UCSI_ReleaseAmsMessage(&ucsContext->ucsiData);
255     }
256
257     return 0;
258 }
259
260 /** This callback will be raised, when ever an applicative message on the control channel arrived */
261 void UCSI_CB_OnAmsMessageReceived(void *pTag)
262 {
263     static sd_event_source *src_ptr = NULL;
264
265     if (!src_ptr)
266     {
267         /* first time usage: create and trigger event source */
268         sd_event_add_defer(afb_api_get_event_loop(afbBindingRoot), &src_ptr, &OnAmsMessageReceivedCB, pTag);
269     }
270     else
271     {
272         sd_event_source_set_enabled(src_ptr, SD_EVENT_ONESHOT);
273     }
274 }
275
276 void UCSI_CB_OnRouteResult(void *pTag, uint16_t routeId, bool isActive, uint16_t connectionLabel)
277 {
278     AFB_API_NOTICE (afbBindingRoot, "Route 0x%X is active=%d, connection label=0x%X", routeId, isActive, connectionLabel);
279 }
280
281 void UCSI_CB_OnGpioStateChange(void *pTag, uint16_t nodeAddress, uint8_t gpioPinId, bool isHighState)
282 {
283     AFB_API_NOTICE (afbBindingRoot, "GPIO state of node 0x%X changed, pin=%d isHigh=%d", nodeAddress, gpioPinId, isHighState);
284 }
285
286 PUBLIC void UCSI_CB_OnMgrReport(void *pTag, Ucs_MgrReport_t code, uint16_t nodeAddress, Ucs_Rm_Node_t *pNode){
287
288     bool available;
289
290     if (code == UCS_MGR_REP_AVAILABLE) {
291         available = true;
292     }
293     else if (code == UCS_MGR_REP_NOT_AVAILABLE) {
294         available = false;
295     }
296     else {
297         /*untracked event - just exit*/
298         return;
299     }
300
301     if (eventData) {
302
303         json_object *j_event_info = json_object_new_object();
304         json_object_object_add(j_event_info, "node", json_object_new_int(nodeAddress));
305         json_object_object_add(j_event_info, "available", json_object_new_boolean(available));
306
307         afb_event_push(eventData->node_event, j_event_info);
308     }
309 }
310
311 bool Cdev_Init(CdevData_t *d, const char *fileName, bool read, bool write)
312 {
313     if (NULL == d || NULL == fileName)  goto OnErrorExit;
314
315     memset(d, 0, sizeof(CdevData_t));
316     strncpy(d->fileName, fileName, MAX_FILENAME_LEN);
317     d->fileHandle = -1;
318
319     if (read && write)
320         d->fileFlags = O_RDWR | O_NONBLOCK;
321     else if (read)
322         d->fileFlags = O_RDONLY | O_NONBLOCK;
323     else if (write)
324         d->fileFlags = O_WRONLY | O_NONBLOCK;
325
326     /* open file to enable event loop */
327     d->fileHandle = open(d->fileName, d->fileFlags);
328     if (d->fileHandle  <= 0) goto OnErrorExit;
329
330     return true;
331
332  OnErrorExit:
333     return false;
334 }
335
336 static bool InitializeCdevs(ucsContextT *ucsContext)
337 {
338     if(!Cdev_Init(&ucsContext->tx, CONTROL_CDEV_TX, false, true))
339         return false;
340     if(!Cdev_Init(&ucsContext->rx, CONTROL_CDEV_RX, true, false))
341         return false;
342     return true;
343 }
344
345 /* Callback fire when something is avaliable on MOST cdev */
346 int onReadCB (sd_event_source* src, int fileFd, uint32_t revents, void* pTag) {
347     ucsContextT *ucsContext =( ucsContextT*) pTag;
348     ssize_t len;
349     uint8_t pBuffer[RX_BUFFER];
350     int ok;
351
352     len = read (ucsContext->rx.fileHandle, &pBuffer, sizeof(pBuffer));
353     if (0 == len)
354         return 0;
355     ok= UCSI_ProcessRxData(&ucsContext->ucsiData, pBuffer, (uint16_t)len);
356     if (!ok) {
357         AFB_API_DEBUG (afbBindingRoot, "Buffer overrun (not handle)");
358         /* Buffer overrun could replay pBuffer */
359     }
360     return 0;
361 }
362
363
364 STATIC char* GetDefaultConfig(void) {
365
366     char const *data_path = getenv("AFM_APP_INSTALL_DIR");
367
368     if (!data_path) {
369         AFB_API_ERROR(afbBindingRoot, "AFM_APP_INSTALL_DIR is not defined");
370     }
371     else {
372         size_t size;
373         char * config_path;
374
375         AFB_API_NOTICE(afbBindingRoot, "AFM_APP_INSTALL_DIR is: %s", data_path);
376         size = strlen(data_path) + strlen(XML_CONFIG_FOLDER) + strlen(XML_CONFIG_FILE) + 2;
377         config_path = malloc(size);
378         if (config_path != NULL) {
379             snprintf(config_path, size, "%s%s%s", data_path, XML_CONFIG_FOLDER, XML_CONFIG_FILE);
380             if(access(config_path, R_OK ) == 0) {
381                 AFB_API_NOTICE(afbBindingRoot, "Default configuration: %s", config_path);
382                 return config_path;
383             }
384         }
385     }
386
387     return NULL;
388 }
389
390 STATIC UcsXmlVal_t* ParseFile(const char *filename) {
391     char *xmlBuffer;
392     ssize_t readSize;
393     int fdHandle ;
394     struct stat fdStat;
395     UcsXmlVal_t *ucsConfig = NULL;
396
397     fdHandle = open(filename, O_RDONLY);
398     if (fdHandle <= 0) {
399         AFB_API_ERROR(afbBindingRoot, "File not accessible: '%s' err=%s", filename, strerror(fdHandle));
400         goto OnErrorExit;
401     }
402
403     /* read file into buffer as a \0 terminated string */
404     fstat(fdHandle, &fdStat);
405     xmlBuffer = (char*)alloca(fdStat.st_size + 1);
406     readSize = read(fdHandle, xmlBuffer, fdStat.st_size);
407     close(fdHandle);
408     xmlBuffer[readSize] = '\0'; /* In any case, terminate it. */
409
410     if (readSize != fdStat.st_size)  {
411         AFB_API_ERROR(afbBindingRoot, "File to read fullfile '%s' size(%d!=%d)", filename, (int)readSize, (int)fdStat.st_size);
412         goto OnErrorExit;
413     }
414
415     ucsConfig = UcsXml_Parse(xmlBuffer);
416     if (!ucsConfig)  {
417         AFB_API_ERROR(afbBindingRoot, "File XML invalid: '%s'", filename);
418         goto OnErrorExit;
419     }
420     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));
421
422     return (ucsConfig);
423
424  OnErrorExit:
425     return NULL;
426 }
427
428 PUBLIC int StartConfiguration(const char *filename) {
429     static ucsContextT ucsContext = { 0 };
430
431     sd_event_source *evtSource;
432     int err;
433
434     /* Read and parse XML file */
435     ucsContext.ucsConfig = ParseFile(filename);
436     if (NULL == ucsContext.ucsConfig) {
437         AFB_API_ERROR (afbBindingRoot, "Cannot access or load file: '%s'", filename);
438         goto OnErrorExit;
439     }
440
441     /* When ucsContextS is set, do not initalize UNICENS, CDEVs or system hooks, just load new XML */
442     if (!ucsContextS)
443     {
444         if (!ucsContextS && !InitializeCdevs(&ucsContext))  {
445             AFB_API_ERROR (afbBindingRoot, "Fail to initialise device [rx=%s tx=%s]", CONTROL_CDEV_RX, CONTROL_CDEV_TX);
446             goto OnErrorExit;
447         }
448
449         /* Initialise UNICENS Config Data Structure */
450         UCSI_Init(&ucsContext.ucsiData, &ucsContext);
451
452         /* register aplayHandle file fd into binder mainloop */
453         err = sd_event_add_io(afb_api_get_event_loop(afbBindingRoot), &evtSource, ucsContext.rx.fileHandle, EPOLLIN, onReadCB, &ucsContext);
454         if (err < 0) {
455             AFB_API_ERROR (afbBindingRoot, "Cannot hook events to mainloop");
456             goto OnErrorExit;
457         }
458
459         /* save this in a statical variable until ucs2vol move to C */
460         ucsContextS = &ucsContext;
461     }
462     /* Initialise UNICENS with parsed config */
463     if (!UCSI_NewConfig(&ucsContext.ucsiData, ucsContext.ucsConfig))   {
464         AFB_API_ERROR (afbBindingRoot, "Fail to initialize UNICENS");
465         goto OnErrorExit;
466     }
467
468     return 0;
469
470  OnErrorExit:
471     return -1;
472 }
473
474 PUBLIC void ucs2_initialise (afb_req_t request) {
475     const char *filename = afb_req_value(request, "filename");
476
477     if (!filename) {
478         afb_req_reply (request, NULL, "filename-missing", "No filename given");
479         goto OnErrorExit;
480     }
481
482     if (StartConfiguration(filename) != 0) {
483         afb_req_reply (request, NULL, "load-failed", "Cannot parse file and start UNICENS");
484         goto OnErrorExit;
485     }
486
487     afb_req_reply(request,NULL, NULL,"UNICENS-active");
488
489  OnErrorExit:
490     return;
491 }
492
493
494 // List Avaliable Configuration Files
495 PUBLIC void ucs2_listconfig (afb_req_t request) {
496     struct json_object *queryJ, *tmpJ, *responseJ;
497     DIR  *dirHandle;
498     char *dirPath, *dirList;
499     int error=0;
500
501     queryJ = afb_req_json(request);
502     if (queryJ && json_object_object_get_ex (queryJ, "cfgpath" , &tmpJ)) {
503         dirList = strdup (json_object_get_string(tmpJ));
504     } else {
505         dirList = strdup (UCS2_CFG_PATH);
506         AFB_API_NOTICE (afbBindingRoot, "fgpath:missing uses UCS2_CFG_PATH=%s", UCS2_CFG_PATH);
507     }
508
509     responseJ = json_object_new_array();
510     for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) {
511          struct dirent *dirEnt;
512
513         dirHandle = opendir (dirPath);
514         if (!dirHandle) {
515             AFB_API_NOTICE (afbBindingRoot, "ucs2_listconfig dir=%s not readable", dirPath);
516             error++;
517             continue;
518         }
519
520         AFB_API_NOTICE (afbBindingRoot, "ucs2_listconfig scanning: %s", dirPath);
521         while ((dirEnt = readdir(dirHandle)) != NULL) {
522             // Unknown type is accepted to support dump filesystems
523             if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) {
524                 struct json_object *pathJ = json_object_new_object();
525                 json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath));
526                 json_object_object_add(pathJ, "basename", json_object_new_string(dirEnt->d_name));
527                 json_object_array_add(responseJ, pathJ);
528             }
529         }
530     }
531
532     free (dirList);
533
534     if (!error)  afb_req_reply(request,responseJ, NULL,NULL);
535     else {
536         char info[40];
537         snprintf (info, sizeof(info), "[%d] where not scanned", error);
538          afb_req_reply(request,responseJ, NULL, info);
539     }
540
541     return;
542 }
543
544 PUBLIC void ucs2_subscribe (afb_req_t request) {
545
546     if (!eventData) {
547
548         eventData = malloc(sizeof(EventData_t));
549         if (eventData) {
550             eventData->node_event = afb_api_make_event (afbBindingRoot, "node-availibility");
551         }
552
553         if (!eventData || !afb_event_is_valid(eventData->node_event)) {
554             afb_req_reply (request, NULL, "create-event", "Cannot create or register event");
555             goto OnExitError;
556         }
557     }
558
559     if (afb_req_subscribe(request, eventData->node_event) != 0) {
560
561         afb_req_reply (request, NULL, "subscribe-event", "Cannot subscribe to event");
562         goto OnExitError;
563     }
564
565     afb_req_reply(request,NULL, NULL,"event subscription successful");
566
567 OnExitError:
568     return;
569 }
570
571 PUBLIC void ucs2_subscriberx (afb_req_t request) {
572
573     if (!eventDataRx) {
574
575         eventDataRx = malloc(sizeof(EventDataRx_t));
576         if (eventDataRx) {
577             eventDataRx->rx_event = afb_api_make_event(afbBindingRoot, "rx-message");
578         }
579
580         if (!eventDataRx || !afb_event_is_valid(eventDataRx->rx_event)) {
581             afb_req_reply(request, NULL, "create-event", "Cannot create or register event");
582             goto OnExitError;
583         }
584     }
585
586     if (afb_req_subscribe(request, eventDataRx->rx_event) != 0) {
587
588         afb_req_reply (request, NULL, "subscribe-event", "Cannot subscribe to event");
589         goto OnExitError;
590     }
591
592     afb_req_reply(request,NULL, NULL,"event subscription successful");
593
594 OnExitError:
595     return;
596 }
597
598 static json_object * ucs2_validate_command (afb_req_t request,
599         const char* func_name) {
600
601     struct json_object *j_obj = NULL;
602
603     if (!ucsContextS) {                     /* check UNICENS is initialized */
604         afb_req_reply_f(request, NULL, "unicens-init",
605                 "Load a configuration before calling %s.",
606                 func_name);
607         goto OnErrorExit;
608     }
609
610     j_obj = afb_req_json(request);
611     if (!j_obj) {
612         afb_req_reply_f(request, NULL,
613                 "query-notjson","query=%s not a valid json entry",
614                 afb_req_value(request,""));
615         goto OnErrorExit;
616     }
617
618     AFB_API_DEBUG(afbBindingRoot, "request: %s", json_object_to_json_string(j_obj));
619
620     if (json_object_get_type(j_obj)==json_type_array) {
621         int len = json_object_array_length(j_obj);
622
623         if (len == 1) {             /* only support 1 command in array */
624             j_obj = json_object_array_get_idx(j_obj, 0);
625         }
626         else {
627             afb_req_reply_f(request, NULL,
628                     "query-array",
629                     "query of multiple %s commands is not supported",
630                     func_name);
631             j_obj = NULL;
632             goto OnErrorExit;
633         }
634     }
635
636  OnErrorExit:
637     return j_obj;
638 }
639
640 STATIC void ucs2_writei2c_CB (void *result_ptr, void *request_ptr) {
641
642     if (request_ptr){
643         afb_req_t req = request_ptr;
644         Ucs_I2c_ResultCode_t *res = (Ucs_I2c_ResultCode_t *)result_ptr;
645
646         if (!res) {
647             afb_req_reply(req, NULL, "processing","busy or lost initialization");
648         }
649         else if (*res != UCS_I2C_RES_SUCCESS){
650             afb_req_reply_f(req, NULL, "error-result", "result code: %d", *res);
651         }
652         else {
653             afb_req_reply(req, NULL, NULL, "success");
654         }
655
656         afb_req_unref(req);
657     }
658     else {
659         AFB_API_NOTICE(afbBindingRoot, "write_i2c: ambiguous response data");
660     }
661 }
662
663 /* write a single i2c command */
664 STATIC void ucs2_writei2c_cmd(afb_req_t request, json_object *j_obj) {
665
666     static uint8_t i2c_data[I2C_MAX_DATA_SZ];
667     uint8_t i2c_data_sz = 0;
668     uint16_t node_addr = 0;
669     json_object *j_tmp;
670     json_bool key_found;
671
672     if (json_object_object_get_ex(j_obj, "node", &j_tmp)) {
673         node_addr = (uint16_t)json_object_get_int(j_tmp);
674         AFB_API_NOTICE(afbBindingRoot, "node_address: 0x%02X", node_addr);
675         if (node_addr == 0) {
676             afb_req_reply(request, NULL, "query-params","param node invalid type");
677             goto OnErrorExit;
678         }
679     }
680     else {
681         afb_req_reply(request, NULL, "query-params","param node missing");
682         goto OnErrorExit;
683     }
684
685     key_found = json_object_object_get_ex(j_obj, "data", &j_tmp);
686     if (key_found && (json_object_get_type(j_tmp)==json_type_array)) {
687         int size = json_object_array_length(j_tmp);
688         if ((size > 0) && (size <= I2C_MAX_DATA_SZ)) {
689
690             int32_t i;
691             int32_t val;
692             struct json_object *j_elem;
693
694             for (i = 0; i < size; i++) {
695
696                 j_elem = json_object_array_get_idx(j_tmp, i);
697                 val = json_object_get_int(j_elem);
698                 if ((val < 0) && (val > 0xFF)){
699                     i = 0;
700                     break;
701                 }
702                 i2c_data[i] = (uint8_t)json_object_get_int(j_elem);
703             }
704
705             i2c_data_sz = (uint8_t)i;
706         }
707     }
708
709     if (i2c_data_sz == 0) {
710         AFB_API_NOTICE(afbBindingRoot, "data: invalid or not found");
711         afb_req_reply(request, NULL, "query-params","params wrong or missing");
712         goto OnErrorExit;
713     }
714
715     if (UCSI_I2CWrite(  &ucsContextS->ucsiData,   /* UCSI_Data_t *pPriv*/
716                         node_addr,                /* uint16_t targetAddress*/
717                         false,                    /* bool isBurst*/
718                         0u,                       /* block count */
719                         0x2Au,                    /* i2c slave address */
720                         0x03E8u,                  /* timeout 1000 milliseconds */
721                         i2c_data_sz,              /* uint8_t dataLen */
722                         &i2c_data[0],             /* uint8_t *pData */
723                         &ucs2_writei2c_CB,        /* callback*/
724                         (void*)request            /* callback argument */
725                   )) {
726         /* asynchronous command is running */
727         afb_req_addref(request);
728     }
729     else {
730         AFB_API_NOTICE(afbBindingRoot, "i2c write: scheduling command failed");
731         afb_req_reply(request, NULL, "query-command-queue","command queue overload");
732         goto OnErrorExit;
733     }
734
735 OnErrorExit:
736     return;
737 }
738
739 /* parse array or single command */
740 PUBLIC void ucs2_writei2c (afb_req_t request) {
741
742     struct json_object *j_obj;
743
744     j_obj = ucs2_validate_command(request, "writei2c");
745
746     if (j_obj) {
747         ucs2_writei2c_cmd(request, j_obj);
748     }
749 }
750
751 PUBLIC void ucs2_sendmessage(afb_req_t req) {
752     uint8_t *data_ptr = NULL;
753     size_t data_sz = 0;
754     int ret, node_addr, msg_id  = 0;
755     struct json_object *j_obj;
756
757     j_obj = ucs2_validate_command(req, "sendmessageb64");
758
759     if (!j_obj) {
760         AFB_API_NOTICE(afbBindingRoot, "validation of command failed");
761         goto OnErrorExit;
762     }
763
764     ret = wrap_json_unpack(j_obj, "{s:i, s:i, s?Y}", "node", &node_addr, "msgid", &msg_id, "data", &data_ptr, &data_sz);
765
766     if ((ret==0) &&
767         UCSI_SendAmsMessage(&ucsContextS->ucsiData, msg_id, node_addr, &data_ptr[0], data_sz)
768             ) {
769         afb_req_reply(req, NULL, NULL, "sendmessageb64 started successful");
770     }
771     else {
772         AFB_API_ERROR(afbBindingRoot, "sendmessageb64: scheduling command failed. ret: %d", ret);
773         afb_req_reply(req, NULL, "query-command-queue","ambiguous command or queue overload");
774         goto OnErrorExit;
775     }
776
777 OnErrorExit:
778     if (data_ptr) {
779         free(data_ptr);
780     }
781     return;
782 }
783
784 PUBLIC int ucs2_initbinding(afb_api_t api) {
785 #ifndef DISABLE_AUTOSTART
786     char *filename = GetDefaultConfig();
787     if (filename != NULL) {
788
789         AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD configuration: %s", filename);
790         if (StartConfiguration(filename) == 0) {
791             AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD successful");
792         } else {
793             AFB_API_NOTICE(afbBindingRoot, "AUTO-LOAD failed");
794         }
795         free(filename);
796     }
797 #endif
798     return 0;
799 }