adds processing of data to i2c_write
[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
27 #include <systemd/sd-event.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <assert.h>
36 #include <errno.h>
37 #include <dirent.h> 
38
39 #include "ucs_binding.h"
40 #include "ucs_interface.h"
41
42
43
44 #define MAX_FILENAME_LEN (100)
45 #define RX_BUFFER (64)
46
47 /** Internal structure, enabling multiple instances of this component.
48  * \note Do not access any of this variables.
49  *  */
50 typedef struct {
51     int fileHandle;
52     int fileFlags;
53     char fileName[MAX_FILENAME_LEN];
54     uint8_t rxBuffer[RX_BUFFER];
55     uint32_t rxLen;
56 } CdevData_t;
57
58
59 typedef struct {
60   CdevData_t rx;
61   CdevData_t tx;
62   UCSI_Data_t ucsiData;
63   UCSI_channelsT *channels;
64 } ucsContextT;
65
66 static ucsContextT *ucsContextS;
67
68 PUBLIC void UcsXml_CB_OnError(const char format[], uint16_t vargsCnt, ...) {
69     /*AFB_DEBUG (afbIface, format, args); */
70     va_list args;
71     va_start (args, vargsCnt);
72     vfprintf (stderr, format, args);
73     va_end(args);
74     
75     va_list argptr;
76     char outbuf[300];
77     va_start(argptr, vargsCnt);
78     vsprintf(outbuf, format, argptr);
79     va_end(argptr);
80     AFB_WARNING (outbuf);
81 }
82
83 PUBLIC uint16_t UCSI_CB_OnGetTime(void *pTag) {
84     struct timespec currentTime;
85     uint16_t timer;
86     pTag = pTag;
87
88     if (clock_gettime(CLOCK_MONOTONIC_RAW, &currentTime))   {
89         assert(false);
90         return 0;
91     }
92
93     timer = (uint16_t) ((currentTime.tv_sec * 1000 ) + ( currentTime.tv_nsec / 1000000 ));
94     return(timer);
95 }
96
97 STATIC int onTimerCB (sd_event_source* source,uint64_t timer, void* pTag) {
98     ucsContextT *ucsContext = (ucsContextT*) pTag;
99
100     sd_event_source_unref(source);
101     UCSI_Timeout(&ucsContext->ucsiData);
102
103     return 0;
104 }
105
106 /* UCS2 Interface Timer Callback */
107 PUBLIC void UCSI_CB_OnSetServiceTimer(void *pTag, uint16_t timeout) {
108   uint64_t usec;
109   /* set a timer with  250ms accuracy */
110   sd_event_now(afb_daemon_get_event_loop(), CLOCK_BOOTTIME, &usec);
111   sd_event_add_time(afb_daemon_get_event_loop(), NULL, CLOCK_MONOTONIC, usec + (timeout*1000), 250, onTimerCB, pTag);
112
113 }
114
115 /**
116  * \brief Callback when ever an Unicens forms a human readable message.
117  *        This can be error events or when enabled also debug messages.
118  * \note This function must be implemented by the integrator
119  * \param pTag - Pointer given by the integrator by UCSI_Init
120  * \param format - Zero terminated format string (following printf rules)
121  * \param vargsCnt - Amount of parameters stored in "..."
122  */
123 void UCSI_CB_OnUserMessage(void *pTag, bool isError, const char format[],
124     uint16_t vargsCnt, ...) {
125     va_list argptr;
126     char outbuf[300];
127     pTag = pTag;
128     va_start(argptr, vargsCnt);
129     vsprintf(outbuf, format, argptr);
130     va_end(argptr);
131     if (isError)
132         AFB_NOTICE (outbuf);
133 }
134
135 /** UCSI_Service cannot be called directly within UNICENS context, need to service stack through mainloop */
136 STATIC int OnServiceRequiredCB (sd_event_source *source, uint64_t usec, void *pTag) {
137     ucsContextT *ucsContext = (ucsContextT*) pTag;
138
139     sd_event_source_unref(source);
140     UCSI_Service(&ucsContext->ucsiData);
141     return (0);
142 }
143
144 /* UCS Callback fire when ever UNICENS needs to be serviced */
145 PUBLIC void UCSI_CB_OnServiceRequired(void *pTag) {
146
147    /* push an asynchronous request for loopback to call UCSI_Service */
148    sd_event_add_time(afb_daemon_get_event_loop(), NULL, CLOCK_MONOTONIC, 0, 0, OnServiceRequiredCB, pTag);
149 }
150
151 /* Callback when ever this UNICENS wants to send a message to INIC. */
152 PUBLIC void UCSI_CB_OnTxRequest(void *pTag, const uint8_t *pData, uint32_t len) {
153     ucsContextT *ucsContext = (ucsContextT*) pTag;
154     CdevData_t *cdevTx = &ucsContext->tx;
155     uint32_t total = 0;
156
157     if (NULL == pData || 0 == len) return;
158
159     if (O_RDONLY == cdevTx->fileFlags) return;
160     if (-1 == cdevTx->fileHandle)
161         cdevTx->fileHandle = open(cdevTx->fileName, cdevTx->fileFlags);
162     if (-1 == cdevTx->fileHandle)
163         return;
164
165     while(total < len) {
166         ssize_t written = write(cdevTx->fileHandle, &pData[total], (len - total));
167         if (0 >= written)
168         {
169             /* Silently ignore write error (only occur in non-blocking mode) */
170             break;
171         }
172         total += (uint32_t) written;
173     }
174 }
175
176 /**
177  * \brief Callback when UNICENS instance has been stopped.
178  * \note This event can be used to free memory holding the resources
179  *       passed with UCSI_NewConfig
180  * \note This function must be implemented by the integrator
181  * \param pTag - Pointer given by the integrator by UCSI_Init
182  */
183 void UCSI_CB_OnStop(void *pTag) {
184     AFB_NOTICE ("UNICENS stopped");
185
186 }
187
188 /** This callback will be raised, when ever an applicative message on the control channel arrived */
189 void UCSI_CB_OnAmsMessageReceived(void *pTag)
190 {
191         /* If not interested, just ignore this event.
192            Otherwise UCSI_GetAmsMessage may now be called asynchronous (mainloop) to get the content. 
193            Don't forget to call UCSI_ReleaseAmsMessage after that */
194 }
195
196 void UCSI_CB_OnRouteResult(void *pTag, uint16_t routeId, bool isActive)
197 {
198 }
199
200 void UCSI_CB_OnGpioStateChange(void *pTag, uint16_t nodeAddress, uint8_t gpioPinId, bool isHighState)
201 {
202 }
203
204 bool Cdev_Init(CdevData_t *d, const char *fileName, bool read, bool write)
205 {
206     if (NULL == d || NULL == fileName)  goto OnErrorExit;
207
208     memset(d, 0, sizeof(CdevData_t));
209     strncpy(d->fileName, fileName, MAX_FILENAME_LEN);
210     d->fileHandle = -1;
211
212     if (read && write)
213         d->fileFlags = O_RDWR | O_NONBLOCK;
214     else if (read)
215         d->fileFlags = O_RDONLY | O_NONBLOCK;
216     else if (write)
217         d->fileFlags = O_WRONLY | O_NONBLOCK;
218
219     /* open file to enable event loop */
220     d->fileHandle = open(d->fileName, d->fileFlags);
221     if (d->fileHandle  <= 0) goto OnErrorExit;
222
223     return true;
224
225  OnErrorExit:
226     return false;
227 }
228
229 static bool InitializeCdevs(ucsContextT *ucsContext)
230 {
231     if(!Cdev_Init(&ucsContext->tx, CONTROL_CDEV_TX, false, true))
232         return false;
233     if(!Cdev_Init(&ucsContext->rx, CONTROL_CDEV_RX, true, false))
234         return false;
235     return true;
236 }
237
238 /* Callback fire when something is avaliable on MOST cdev */
239 int onReadCB (sd_event_source* src, int fileFd, uint32_t revents, void* pTag) {
240     ucsContextT *ucsContext =( ucsContextT*) pTag;
241     ssize_t len;
242     uint8_t pBuffer[RX_BUFFER];
243     int ok;
244
245     len = read (ucsContext->rx.fileHandle, &pBuffer, sizeof(pBuffer));
246     if (0 == len)
247         return 0;
248     ok= UCSI_ProcessRxData(&ucsContext->ucsiData, pBuffer, (uint16_t)len);
249     if (!ok) {
250         AFB_DEBUG ("Buffer overrun (not handle)");
251         /* Buffer overrun could replay pBuffer */
252     }
253     return 0;
254 }
255
256 STATIC UcsXmlVal_t* ParseFile(struct afb_req request) {
257     char *xmlBuffer;
258     ssize_t readSize;
259     int fdHandle ;
260     struct stat fdStat;
261     UcsXmlVal_t* ucsConfig;
262
263     const char *filename = afb_req_value(request, "filename");
264     if (!filename) {
265         afb_req_fail_f (request, "filename-missing", "No filename given");
266         goto OnErrorExit;
267     }
268
269     fdHandle = open(filename, O_RDONLY);
270     if (fdHandle <= 0) {
271         afb_req_fail_f (request, "fileread-error", "File not accessible: '%s' err=%s", filename, strerror(fdHandle));
272         goto OnErrorExit;
273     }
274
275     /* read file into buffer as a \0 terminated string */
276     fstat(fdHandle, &fdStat);
277     xmlBuffer = (char*)alloca(fdStat.st_size + 1);
278     readSize = read(fdHandle, xmlBuffer, fdStat.st_size);
279     close(fdHandle);
280     xmlBuffer[readSize] = '\0'; /* In any case, terminate it. */
281
282     if (readSize != fdStat.st_size)  {
283         afb_req_fail_f (request, "fileread-fail", "File to read fullfile '%s' size(%d!=%d)", filename, (int)readSize, (int)fdStat.st_size);
284         goto OnErrorExit;
285     }
286
287     ucsConfig = UcsXml_Parse(xmlBuffer);
288     if (!ucsConfig)  {
289         afb_req_fail_f (request, "filexml-error", "File XML invalid: '%s'", filename);
290         goto OnErrorExit;
291     }
292
293     return (ucsConfig);
294
295  OnErrorExit:
296     return NULL;
297 }
298
299 STATIC int volOnSvcCB (sd_event_source* source,uint64_t timer, void* pTag) {
300     ucsContextT *ucsContext = (ucsContextT*) pTag;
301
302     sd_event_source_unref(source);
303     UCSI_Vol_Service(&ucsContext->ucsiData);
304
305     return 0;
306 }
307
308 /* This callback is fire each time an volume event wait in the queue */
309 void volumeCB (uint16_t timeout) {
310     uint64_t usec;
311     sd_event_now(afb_daemon_get_event_loop(), CLOCK_BOOTTIME, &usec);
312     sd_event_add_time(afb_daemon_get_event_loop(), NULL, CLOCK_MONOTONIC, usec + (timeout*1000), 250, volOnSvcCB, ucsContextS);
313 }
314
315 STATIC int volSndCmd (struct afb_req request, struct json_object *commandJ, ucsContextT *ucsContext) {
316     int numid, vol, err;
317     struct json_object *nameJ, *channelJ, *volJ;
318
319     enum json_type jtype= json_object_get_type(commandJ);
320     switch (jtype) {
321         case json_type_array:
322             if (!sscanf (json_object_get_string (json_object_array_get_idx(commandJ, 0)), "%d", &numid)) {
323                 afb_req_fail_f (request, "channel-invalid","command=%s channel is not an integer", json_object_get_string (channelJ));
324                 goto OnErrorExit;
325             }
326             if (!sscanf (json_object_get_string (json_object_array_get_idx(commandJ, 1)), "%d", &vol)) {
327                 afb_req_fail_f (request, "vol-invalid","command=%s vol is not an integer", json_object_get_string (channelJ));
328                 goto OnErrorExit;
329             }
330             break;
331
332         case json_type_object:
333             if (json_object_object_get_ex (commandJ, "numid", &channelJ)) {
334                 if (!sscanf (json_object_get_string (channelJ), "%d", &numid)) {
335                     afb_req_fail_f (request, "channel-invalid","command=%s numid is not an integer", json_object_get_string (channelJ));
336                     goto OnErrorExit;
337                 }
338             } else {
339                 if (json_object_object_get_ex (commandJ, "channel", &nameJ)) {
340                     int idx;
341                     const char *name = json_object_get_string(nameJ);
342
343                     for (idx =0; ucsContext->channels[idx].name != NULL; idx++) {
344                         if (!strcasecmp(ucsContext->channels[idx].name, name)) {
345                             numid = ucsContext->channels[idx].numid;
346                             break;
347                         }
348                     }
349                     if (ucsContext->channels[idx].name == NULL) {
350                         afb_req_fail_f (request, "channel-invalid","command=%s channel name does not exist", name);
351                         goto OnErrorExit;
352                     }
353                 } else {
354                     afb_req_fail_f (request, "channel-invalid","command=%s no valid channel name or channel", json_object_get_string(commandJ));
355                     goto OnErrorExit;
356                 };
357             }
358
359             if (!json_object_object_get_ex (commandJ, "volume", &volJ)) {
360                 afb_req_fail_f (request, "vol-missing","command=%s vol not present", json_object_get_string (commandJ));
361                 goto OnErrorExit;
362             }
363
364             if (!sscanf (json_object_get_string (volJ), "%d", &vol)) {
365                 afb_req_fail_f (request, "vol-invalid","command=%s vol:%s is not an integer", json_object_get_string (commandJ), json_object_get_string (volJ));
366                 goto OnErrorExit;
367             }
368
369             break;
370
371         default:
372             afb_req_fail_f (request, "setvol-invalid","command=%s not valid JSON Volume Command", json_object_get_string(commandJ));
373             goto OnErrorExit;
374     }
375
376
377     /* Fulup what's append when channel or vol are invalid ??? */
378     err = UCSI_Vol_Set  (&ucsContext->ucsiData, numid, (uint8_t) vol);
379     if (err) {
380         /* Fulup this might only be a warning (not sure about it) */
381         afb_req_fail_f (request, "vol-refused","command=%s vol was refused by UNICENS", json_object_get_string (volJ));
382         goto OnErrorExit;
383     }
384
385     return 0;
386
387   OnErrorExit:
388     return 1;
389 }
390
391 PUBLIC void ucs2_volume (struct afb_req request) {
392     struct json_object *queryJ;
393     int err;
394
395     /* check UNICENS is initialised */
396     if (!ucsContextS) {
397         afb_req_fail_f (request, "UNICENS-init","Should Load Config before using setvol");
398         goto OnErrorExit;
399     }
400
401     queryJ = afb_req_json(request);
402     if (!queryJ) {
403         afb_req_fail_f (request, "query-notjson","query=%s not a valid json entry", afb_req_value(request,""));
404         goto OnErrorExit;
405     };
406
407     enum json_type jtype= json_object_get_type(queryJ);
408     switch (jtype) {
409         case json_type_array:
410             for (int idx=0; idx < json_object_array_length (queryJ); idx ++) {
411                err= volSndCmd (request, json_object_array_get_idx (queryJ, idx), ucsContextS);
412                if (err) goto OnErrorExit;
413             }
414             break;
415
416         case json_type_object:
417             err = volSndCmd (request, queryJ, ucsContextS);
418             if (err) goto OnErrorExit;
419             break;
420
421         default:
422             afb_req_fail_f (request, "query-notarray","query=%s not valid JSON Volume Command Array", afb_req_value(request,""));
423             goto OnErrorExit;
424     }
425
426
427     afb_req_success(request,NULL,NULL);
428
429  OnErrorExit:
430     return;
431 }
432
433
434 PUBLIC void ucs2_initialise (struct afb_req request) {
435     static UcsXmlVal_t *ucsConfig;
436     static ucsContextT ucsContext;
437
438     sd_event_source *evtSource;
439     int err;
440
441     /* Read and parse XML file */
442     ucsConfig = ParseFile (request);
443     if (NULL == ucsConfig) goto OnErrorExit;
444
445     /* When ucsContextS is set, do not initalize UNICENS, CDEVs or system hooks, just load new XML */
446     if (!ucsContextS)
447     {
448         if (!ucsContextS && !InitializeCdevs(&ucsContext))  {
449             afb_req_fail_f (request, "devnit-error", "Fail to initialise device [rx=%s tx=%s]", CONTROL_CDEV_RX, CONTROL_CDEV_TX);
450             goto OnErrorExit;
451         }
452
453         /* Initialise UNICENS Config Data Structure */
454         UCSI_Init(&ucsContext.ucsiData, &ucsContext);
455
456         /* register aplayHandle file fd into binder mainloop */
457         err = sd_event_add_io(afb_daemon_get_event_loop(), &evtSource, ucsContext.rx.fileHandle, EPOLLIN, onReadCB, &ucsContext);
458         if (err < 0) {
459             afb_req_fail_f (request, "register-mainloop", "Cannot hook events to mainloop");
460             goto OnErrorExit;
461         }
462
463         /* init UNICENS Volume Library */
464         ucsContext.channels = UCSI_Vol_Init (&ucsContext.ucsiData, volumeCB);
465         if (!ucsContext.channels) {
466             afb_req_fail_f (request, "register-volume", "Could not enqueue new Unicens config");
467             goto OnErrorExit;
468         }
469         /* save this in a statical variable until ucs2vol move to C */
470         ucsContextS = &ucsContext;
471     }
472     /* Initialise UNICENS with parsed config */
473     if (!UCSI_NewConfig(&ucsContext.ucsiData, ucsConfig))   {
474         afb_req_fail_f (request, "UNICENS-init", "Fail to initialize UNICENS");
475         goto OnErrorExit;
476     }
477
478     afb_req_success(request,NULL,"UNICENS-active");
479
480  OnErrorExit:
481     return;
482 }
483
484
485 // List Avaliable Configuration Files
486 PUBLIC void ucs2_listconfig (struct afb_req request) {
487     struct json_object *queryJ, *tmpJ, *responseJ;
488     DIR  *dirHandle;
489     char *dirPath, *dirList;
490     int error=0;
491
492     queryJ = afb_req_json(request);
493     if (queryJ && json_object_object_get_ex (queryJ, "cfgpath" , &tmpJ)) {
494         strdup (json_object_get_string(tmpJ));
495     } else {    
496         dirList = strdup (UCS2_CFG_PATH); 
497         AFB_NOTICE ("fgpath:missing uses UCS2_CFG_PATH=%s", UCS2_CFG_PATH);
498     }
499
500     responseJ = json_object_new_array();
501     for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) {
502          struct dirent *dirEnt;
503          
504         dirHandle = opendir (dirPath);
505         if (!dirHandle) {
506             AFB_NOTICE ("ucs2_listconfig dir=%s not readable", dirPath);
507             error++;
508             continue;
509         } 
510         
511         AFB_NOTICE ("ucs2_listconfig scanning: %s", dirPath);
512         while ((dirEnt = readdir(dirHandle)) != NULL) {
513             // Unknown type is accepted to support dump filesystems
514             if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) {
515                 struct json_object *pathJ = json_object_new_object();
516                 json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath));
517                 json_object_object_add(pathJ, "basename", json_object_new_string(dirEnt->d_name));
518                 json_object_array_add(responseJ, pathJ);
519             }
520         }
521     }
522     
523     free (dirList);
524    
525     if (!error)  afb_req_success(request,responseJ,NULL);
526     else {
527         char info[40];
528         snprintf (info, sizeof(info), "[%d] where not scanned", error); 
529          afb_req_success(request,responseJ, info);
530     } 
531     
532     return;
533 }
534
535 PUBLIC void ucs2_monitor (struct afb_req request) {
536     
537    afb_req_success(request,NULL,"UNICENS-to_be_done"); 
538 }
539
540 #define MUTE_VALUE      0x03FFU
541 #define MUTE_VALUE_HB   0x03U
542 #define MUTE_VALUE_LB   0xFFU
543
544 #define CONTROL_MASTER  0x07U
545 #define CONTROL_CH_1    0x08U
546 #define CONTROL_CH_2    0x09U
547
548 #define UCSB_I2C_MAX_PAYLOAD     32
549
550 PUBLIC void ucs2_write_i2c (struct afb_req request) {
551     
552     struct json_object *j_obj;
553     static uint8_t tx_payload[UCSB_I2C_MAX_PAYLOAD];
554     uint8_t tx_payload_sz = 0;
555     uint16_t node_addr = 0;
556     
557     /* check UNICENS is initialised */
558     if (!ucsContextS) {
559         afb_req_fail_f(request, "unicens-init","Should Load Config before using setvol");
560         goto OnErrorExit;
561     }
562
563     j_obj = afb_req_json(request);
564     if (!j_obj) {
565         afb_req_fail_f(request, "query-notjson","query=%s not a valid json entry", afb_req_value(request,""));
566         goto OnErrorExit;
567     };
568     
569     node_addr = (uint16_t)json_object_get_int(json_object_object_get(j_obj, "node_address"));
570     AFB_NOTICE("node_address: 0x%02X", node_addr);
571     
572     if (node_addr == 0) {
573         afb_req_fail_f(request, "query-params","params wrong or missing");
574         goto OnErrorExit;
575     }
576        
577     if (json_object_get_type(json_object_object_get(j_obj, "i2c_data"))==json_type_array) {
578         int size = json_object_array_length(json_object_object_get(j_obj, "i2c_data"));
579         if ((size > 0) && (size <= UCSB_I2C_MAX_PAYLOAD)) {
580             
581             int32_t i;
582             int32_t val;
583             struct json_object *j_elem;
584             struct json_object *j_arr = json_object_object_get(j_obj, "i2c_data");
585
586             for (i = 0; i < size; i++) {
587                 
588                 
589                 j_elem = json_object_array_get_idx(j_arr, i);
590                 val = json_object_get_int(j_elem);
591                 if ((val < 0) && (val > 0xFF)){
592                     i = 0;
593                     break;
594                 }
595                 tx_payload[i] = (uint8_t)json_object_get_int(j_elem);
596             }
597             
598             tx_payload_sz = (uint8_t)i;
599         }
600     }
601     
602     if (tx_payload_sz == 0) {
603         AFB_NOTICE("i2c_data: invalid or not found");
604         afb_req_fail_f(request, "query-params","params wrong or missing");
605         goto OnErrorExit;
606     }
607         
608     UCSI_I2CWrite(&ucsContextS->ucsiData,/*UCSI_Data_t *pPriv*/
609                   node_addr,            /*uint16_t targetAddress*/
610                   false,                /*bool isBurst*/
611                   0u,                   /* block count */
612                   0x2Au,                /* i2c slave address */
613                   0x03E8u,              /* timeout 1000 milliseconds */
614                   tx_payload_sz,        /* uint8_t dataLen */
615                   &tx_payload[0]        /* uint8_t *pData */
616                 );  
617     
618     afb_req_success(request,NULL,"done!!!");
619     
620  OnErrorExit:
621     return;
622 }