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