1 /** ----------------------------------------------------------------------
2  *
3  * Copyright (C) 2016 ST Microelectronics S.A.
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  *
18  ----------------------------------------------------------------------*/
19 #define LOG_TAG "NfcHal"
20 #define TX_DELAY 10
21 
22 #include <hardware/nfc.h>
23 #include <pthread.h>
24 #include <semaphore.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include "android_logmsg.h"
29 #include "halcore_private.h"
30 
31 extern int I2cWriteCmd(const uint8_t* x, size_t len);
32 extern void DispHal(const char* title, const void* data, size_t length);
33 
34 extern uint32_t ScrProtocolTraceFlag;  // = SCR_PROTO_TRACE_ALL;
35 
36 // HAL WRAPPER
37 static void HalStopTimer(HalInstance* inst);
38 static bool rf_deactivate_delay;
39 struct timespec start_tx_data;
40 
41 typedef struct {
42   struct nfc_nci_device nci_device;  // nci_device must be first struct member
43   // below declarations are private variables within HAL
44   nfc_stack_callback_t* p_cback;
45   nfc_stack_data_callback_t* p_data_cback;
46   HALHANDLE hHAL;
47 } st21nfc_dev_t;  // beware, is a duplication of structure in nfc_nci_st21nfc.c
48 
49 /**************************************************************************************************
50  *
51  *                                      Private API Declaration
52  *
53  **************************************************************************************************/
54 
55 static void* HalWorkerThread(void* arg);
56 static inline int sem_wait_nointr(sem_t* sem);
57 
58 static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data,
59                                   size_t length);
60 static void HalTriggerNextDsPacket(HalInstance* inst);
61 static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg);
62 static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg);
63 static HalBuffer* HalAllocBuffer(HalInstance* inst);
64 static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b);
65 static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout);
66 struct timespec HalGetTimestamp(void);
67 int HalTimeDiffInMs(struct timespec start, struct timespec end);
68 
69 /**************************************************************************************************
70  *
71  *                                      Public API Entry-Points
72  *
73  **************************************************************************************************/
74 
75 /**
76  * Callback of HAL Core protocol layer.
77  * Invoked by HAL worker thread according to if message is received from NCI
78  * stack or posted by
79  * I2C worker thread.
80  * <p>@param context NFC callbacks for control/data
81  * @param event Next HAL state machine action (send msg to I2C layer or report
82  * data/control/error
83  * to NFC task)
84  * @param length Configure if debug and trace allowed, trace level
85  */
HalCoreCallback(void * context,uint32_t event,const void * d,size_t length)86 void HalCoreCallback(void* context, uint32_t event, const void* d,
87                      size_t length) {
88   const uint8_t* data = (const uint8_t*)d;
89   uint8_t cmd = 'W';
90   int delta_time_ms;
91 
92   st21nfc_dev_t* dev = (st21nfc_dev_t*)context;
93 
94   switch (event) {
95     case HAL_EVENT_DSWRITE:
96       if (rf_deactivate_delay && length == 4 && data[0] == 0x21
97           && data[1] == 0x06 && data[2] == 0x01) {
98         delta_time_ms = HalTimeDiffInMs(start_tx_data, HalGetTimestamp());
99         if (delta_time_ms >= 0 && delta_time_ms < TX_DELAY) {
100             STLOG_HAL_D("Delay %d ms\n", TX_DELAY - delta_time_ms);
101             usleep(1000 * (TX_DELAY - delta_time_ms));
102         }
103         rf_deactivate_delay = false;
104       } else if (data[0] == 0x00 && data[1] == 0x00) {
105         start_tx_data = HalGetTimestamp();
106         rf_deactivate_delay = true;
107       } else {
108         rf_deactivate_delay = false;
109       }
110       STLOG_HAL_V("!! got event HAL_EVENT_DSWRITE for %zu bytes\n", length);
111       DispHal("TX DATA", (data), length);
112 
113       // Send write command to IO thread
114       cmd = 'W';
115       I2cWriteCmd(&cmd, sizeof(cmd));
116       I2cWriteCmd((const uint8_t*)&length, sizeof(length));
117       I2cWriteCmd(data, length);
118       break;
119 
120     case HAL_EVENT_DATAIND:
121       STLOG_HAL_V("!! got event HAL_EVENT_DATAIND for %zu bytes\n", length);
122 
123       if ((length >= 3) && (data[2] != (length - 3))) {
124         STLOG_HAL_W(
125             "length is illogical. Header length is %d, packet length %zu\n",
126             data[2], length);
127       } else if (rf_deactivate_delay && data[0] == 0x00 && data[1] == 0x00) {
128         rf_deactivate_delay = false;
129       }
130 
131       dev->p_data_cback(length, (uint8_t*)data);
132       break;
133 
134     case HAL_EVENT_ERROR:
135       STLOG_HAL_E("!! got event HAL_EVENT_ERROR\n");
136       DispHal("Received unexpected HAL message !!!", data, length);
137       break;
138 
139     case HAL_EVENT_LINKLOST:
140       STLOG_HAL_E("!! got event HAL_EVENT_LINKLOST or HAL_EVENT_ERROR\n");
141 
142       dev->p_cback(HAL_NFC_ERROR_EVT, HAL_NFC_STATUS_ERR_CMD_TIMEOUT);
143 
144       // Write terminate command
145       cmd = 'X';
146       I2cWriteCmd(&cmd, sizeof(cmd));
147       break;
148 
149     case HAL_EVENT_TIMER_TIMEOUT:
150       STLOG_HAL_D("!! got event HAL_EVENT_TIMER_TIMEOUT \n");
151       dev->p_cback(HAL_WRAPPER_TIMEOUT_EVT, HAL_NFC_STATUS_OK);
152       break;
153   }
154 }
155 
156 /**
157  * Connection to the HAL Core layer.
158  * Set-up HAL context and create HAL worker thread.
159  * <p>@param context NFC NCI device context, NFC callbacks for control/data, HAL
160  * handle
161  * @param callback HAL callback function pointer
162  * @param flags Configure if debug and trace allowed, trace level
163  */
HalCreate(void * context,HAL_CALLBACK callback,uint32_t flags)164 HALHANDLE HalCreate(void* context, HAL_CALLBACK callback, uint32_t flags) {
165   /*   bool halTraceMask = true;
166 
167      if (flags & HAL_FLAG_NO_DEBUG) {
168          halTraceMask = false;
169      }
170  */
171   STLOG_HAL_V("HalCreate enter\n");
172 
173   HalInstance* inst = (HalInstance*)calloc(1, sizeof(HalInstance));
174 
175   if (!inst) {
176     STLOG_HAL_E("!out of memory\n");
177     return NULL;
178   }
179 
180   // We need a semaphore to wakeup our protocol thread
181   if (0 != sem_init(&inst->semaphore, 0, 0)) {
182     STLOG_HAL_E("!sem_init failed\n");
183     free(inst);
184     return NULL;
185   }
186 
187   // We need a semaphore to manage buffers
188   if (0 != sem_init(&inst->bufferResourceSem, 0, NUM_BUFFERS)) {
189     STLOG_HAL_E("!sem_init failed\n");
190     sem_destroy(&inst->semaphore);
191     free(inst);
192     return NULL;
193   }
194 
195   // We need a semaphore to block upstream data indications
196   if (0 != sem_init(&inst->upstreamBlock, 0, 0)) {
197     STLOG_HAL_E("!sem_init failed\n");
198     sem_destroy(&inst->semaphore);
199     sem_destroy(&inst->bufferResourceSem);
200     free(inst);
201     return NULL;
202   }
203 
204   // Initialize remaining data-members
205   inst->context = context;
206   inst->callback = callback;
207   inst->flags = flags;
208   inst->freeBufferList = 0;
209   inst->pendingNciList = 0;
210   inst->nciBuffer = 0;
211   inst->ringReadPos = 0;
212   inst->ringWritePos = 0;
213   inst->timeout = HAL_SLEEP_TIMER_DURATION;
214 
215   inst->bufferData = (HalBuffer*)calloc(NUM_BUFFERS, sizeof(HalBuffer));
216   if (!inst->bufferData) {
217     STLOG_HAL_E("!failed to allocate memory\n");
218     sem_destroy(&inst->semaphore);
219     sem_destroy(&inst->bufferResourceSem);
220     sem_destroy(&inst->upstreamBlock);
221     free(inst);
222     return NULL;
223   }
224 
225   // Concatenate the buffers into a linked list for easy access
226   size_t i;
227   for (i = 0; i < NUM_BUFFERS; i++) {
228     HalBuffer* b = &inst->bufferData[i];
229     b->next = inst->freeBufferList;
230     inst->freeBufferList = b;
231   }
232 
233   if (0 != pthread_mutex_init(&inst->hMutex, 0)) {
234     STLOG_HAL_E("!failed to initialize Mutex \n");
235     sem_destroy(&inst->semaphore);
236     sem_destroy(&inst->bufferResourceSem);
237     sem_destroy(&inst->upstreamBlock);
238     free(inst->bufferData);
239     free(inst);
240     return NULL;
241   }
242 
243   // Spawn the thread
244   if (0 != pthread_create(&inst->thread, NULL, HalWorkerThread, inst)) {
245     STLOG_HAL_E("!failed to spawn workerthread \n");
246     sem_destroy(&inst->semaphore);
247     sem_destroy(&inst->bufferResourceSem);
248     sem_destroy(&inst->upstreamBlock);
249     pthread_mutex_destroy(&inst->hMutex);
250     free(inst->bufferData);
251     free(inst);
252     return NULL;
253   }
254 
255   STLOG_HAL_V("HalCreate exit\n");
256   return (HALHANDLE)inst;
257 }
258 
259 /**
260  * Disconnection of the HAL protocol layer.
261  * Send message to stop the HAL worker thread and wait for it to finish. Free
262  * resources.
263  * @param hHAL HAL handle
264  */
HalDestroy(HALHANDLE hHAL)265 void HalDestroy(HALHANDLE hHAL) {
266   HalInstance* inst = (HalInstance*)hHAL;
267   // Tell the thread that we want to finish
268   ThreadMesssage msg;
269   msg.command = MSG_EXIT_REQUEST;
270   msg.payload = 0;
271   msg.length = 0;
272 
273   HalEnqueueThreadMessage(inst, &msg);
274 
275   // Wait for thread to finish
276   pthread_join(inst->thread, NULL);
277 
278   // Cleanup and exit
279   sem_destroy(&inst->semaphore);
280   sem_destroy(&inst->upstreamBlock);
281   sem_destroy(&inst->bufferResourceSem);
282   pthread_mutex_destroy(&inst->hMutex);
283 
284   // Free resources
285   free(inst->bufferData);
286   free(inst);
287 
288   STLOG_HAL_V("HalDestroy done\n");
289 }
290 
291 /**
292  * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
293  * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will
294  * return immediately.
295  * @param hHAL HAL handle
296  * @param data Data message
297  * @param size Message size
HalSendDownstream(HALHANDLE hHAL,const uint8_t * data,size_t size)298  */ bool HalSendDownstream(HALHANDLE hHAL, const uint8_t* data, size_t size)
299 {
300   // Send an NCI frame downstream. will
301   HalInstance* inst = (HalInstance*)hHAL;
302 
303   if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
304     ThreadMesssage msg;
305     HalBuffer* b = HalAllocBuffer(inst);
306 
307     if (!b) {
308       // Should never be reachable
309       return false;
310     }
311 
312     memcpy(b->data, data, size);
313     b->length = size;
314 
315     msg.command = MSG_TX_DATA;
316     msg.payload = 0;
317     msg.length = 0;
318     msg.buffer = b;
319 
320     return HalEnqueueThreadMessage(inst, &msg);
321 
322   } else {
323     STLOG_HAL_E("HalSendDownstream size to large %zu instead of %d\n", size,
324                 MAX_BUFFER_SIZE);
325     return false;
326   }
327 }
328 
329 // HAL WRAPPER
330 /**
331  * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
332  * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will
333  * return immediately.
334  * @param hHAL HAL handle
335  * @param data Data message
336  * @param size Message size
337  */
HalSendDownstreamTimer(HALHANDLE hHAL,const uint8_t * data,size_t size,uint32_t duration)338 bool HalSendDownstreamTimer(HALHANDLE hHAL, const uint8_t* data, size_t size,
339                             uint32_t duration) {
340   // Send an NCI frame downstream. will
341   HalInstance* inst = (HalInstance*)hHAL;
342 
343   if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
344     ThreadMesssage msg;
345     HalBuffer* b = HalAllocBuffer(inst);
346 
347     if (!b) {
348       // Should never be reachable
349       return false;
350     }
351 
352     memcpy(b->data, data, size);
353     b->length = size;
354 
355     msg.command = MSG_TX_DATA_TIMER_START;
356     msg.payload = 0;
357     msg.length = duration;
358     msg.buffer = b;
359 
360     return HalEnqueueThreadMessage(inst, &msg);
361 
362   } else {
363     STLOG_HAL_E("HalSendDownstreamTimer size to large %zu instead of %d\n",
364                 size, MAX_BUFFER_SIZE);
365     return false;
366   }
367 }
368 
HalSendDownstreamTimer(HALHANDLE hHAL,uint32_t duration)369 bool HalSendDownstreamTimer(HALHANDLE hHAL, uint32_t duration) {
370   HalInstance* inst = (HalInstance*)hHAL;
371 
372   ThreadMesssage msg;
373 
374   msg.command = MSG_TIMER_START;
375   msg.payload = 0;
376   msg.length = duration;
377   msg.buffer = NULL;
378 
379   return HalEnqueueThreadMessage(inst, &msg);
380 }
381 /**
382  * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer).
383  * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will
384  * return immediately.
385  * @param hHAL HAL handle
386  * @param data Data message
387  * @param size Message size
388  */
HalSendDownstreamStopTimer(HALHANDLE hHAL)389 bool HalSendDownstreamStopTimer(HALHANDLE hHAL) {
390   // Send an NCI frame downstream. will
391   HalInstance* inst = (HalInstance*)hHAL;
392 
393   HalStopTimer(inst);
394   return 1;
395 }
396 
397 /**
398  * Send an NCI message upstream to NFC NCI layer (NFCC->DH transfer).
399  * @param hHAL HAL handle
400  * @param data Data message
401  * @param size Message size
402  */
HalSendUpstream(HALHANDLE hHAL,const uint8_t * data,size_t size)403 bool HalSendUpstream(HALHANDLE hHAL, const uint8_t* data, size_t size) {
404   HalInstance* inst = (HalInstance*)hHAL;
405   if ((size <= MAX_BUFFER_SIZE) && (size > 0)) {
406     ThreadMesssage msg;
407     msg.command = MSG_RX_DATA;
408     msg.payload = data;
409     msg.length = size;
410 
411     if (HalEnqueueThreadMessage(inst, &msg)) {
412       // Block until the protocol has taken a copy of the data
413       sem_wait_nointr(&inst->upstreamBlock);
414       return true;
415     }
416     return false;
417   } else {
418     STLOG_HAL_E("HalSendUpstream size to large %zu instead of %d\n", size,
419                 MAX_BUFFER_SIZE);
420     return false;
421   }
422 }
423 
424 /**************************************************************************************************
425  *
426  *                                      Private API Definition
427  *
428  **************************************************************************************************/
429 /*
430  * Get current time stamp
431  */
HalGetTimestamp(void)432 struct timespec HalGetTimestamp(void) {
433   struct timespec tm;
434   clock_gettime(CLOCK_REALTIME, &tm);
435   return tm;
436 }
437 
HalTimeDiffInMs(struct timespec start,struct timespec end)438 int HalTimeDiffInMs(struct timespec start, struct timespec end) {
439   struct timespec temp;
440   if ((end.tv_nsec - start.tv_nsec) < 0) {
441     temp.tv_sec = end.tv_sec - start.tv_sec - 1;
442     temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
443   } else {
444     temp.tv_sec = end.tv_sec - start.tv_sec;
445     temp.tv_nsec = end.tv_nsec - start.tv_nsec;
446   }
447 
448   return (temp.tv_nsec / 1000000) + (temp.tv_sec * 1000);
449 }
450 
451 /**
452  * Determine the next shortest sleep to fulfill the pending timer requirements.
453  * @param inst HAL instance
454  * @param now timespec structure for time definition
455  */
HalCalcSemWaitingTime(HalInstance * inst,struct timespec * now)456 static uint32_t HalCalcSemWaitingTime(HalInstance* inst, struct timespec* now) {
457   // Default to infinite wait time
458   uint32_t result = OS_SYNC_INFINITE;
459 
460   if (inst->timer.active) {
461     int delta =
462         inst->timer.duration - HalTimeDiffInMs(inst->timer.startTime, *now);
463 
464     if (delta < 0) {
465       // If we have a timer that has already expired, pick a zero wait time
466       result = 0;
467 
468     } else if ((uint32_t)delta < result) {
469       // Smaller time difference? If so take it
470       result = delta;
471     }
472   }
473 
474   if (result != OS_SYNC_INFINITE) {
475     // Add one millisecond on top of that, so the waiting semaphore will time
476     // out just a moment
477     // after the timer should expire
478     result += 1;
479   }
480 
481   return result;
482 }
483 
484 /**************************************************************************************************
485  *
486  *                                     Timer Management
487  *
488  **************************************************************************************************/
489 
HalStopTimer(HalInstance * inst)490 static void HalStopTimer(HalInstance* inst) {
491   inst->timer.active = false;
492   STLOG_HAL_D("HalStopTimer \n");
493 }
494 
HalStartTimer(HalInstance * inst,uint32_t duration)495 static void HalStartTimer(HalInstance* inst, uint32_t duration) {
496   STLOG_HAL_D("HalStartTimer \n");
497   inst->timer.startTime = HalGetTimestamp();
498   inst->timer.active = true;
499   inst->timer.duration = duration;
500 }
501 
502 /**************************************************************************************************
503  *
504  *                                     Thread Message Queue
505  *
506  **************************************************************************************************/
507 
508 /**
509  * Write message pointer to small ring buffer for queuing HAL messages.
510  * @param inst HAL instance
511  * @param msg Message to send
512  * @return true if message properly copied in ring buffer
513  */
HalEnqueueThreadMessage(HalInstance * inst,ThreadMesssage * msg)514 static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg) {
515   // Put a message to the queue
516   int nextWriteSlot;
517   bool result = true;
518 
519   pthread_mutex_lock(&inst->hMutex);
520 
521   nextWriteSlot = inst->ringWritePos + 1;
522 
523   if (nextWriteSlot == HAL_QUEUE_MAX) {
524     nextWriteSlot = 0;
525   }
526 
527   // Check that we don't overflow the queue entries
528   if (nextWriteSlot == inst->ringReadPos) {
529     STLOG_HAL_E("HAL thread message ring: RNR (implement me!!)");
530     result = false;
531   }
532 
533   if (result) {
534     // inst->ring[nextWriteSlot] = *msg;
535     memcpy(&(inst->ring[nextWriteSlot]), msg, sizeof(ThreadMesssage));
536     inst->ringWritePos = nextWriteSlot;
537   }
538 
539   pthread_mutex_unlock(&inst->hMutex);
540 
541   if (result) {
542     sem_post(&inst->semaphore);
543   }
544 
545   return result;
546 }
547 
548 /**
549  * Remove message pointer from stored ring buffer.
550  * @param inst HAL instance
551  * @param msg Message received
552  * @return true if there is a new message to pull, false otherwise.
553  */
HalDequeueThreadMessage(HalInstance * inst,ThreadMesssage * msg)554 static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg) {
555   int nextCmdIndex;
556   bool result = true;
557   // New data available
558   pthread_mutex_lock(&inst->hMutex);
559 
560   // Get new timer read index
561   nextCmdIndex = inst->ringReadPos + 1;
562 
563   if (nextCmdIndex == HAL_QUEUE_MAX) {
564     nextCmdIndex = 0;
565   }
566   // check if ring buffer is empty
567   if (inst->ringReadPos == inst->ringWritePos) {
568     STLOG_HAL_E("HAL thread message ring: already read last valid data");
569     result = false;
570   }
571 
572   // Get new element from ringbuffer
573   if (result) {
574     memcpy(msg, &(inst->ring[nextCmdIndex]), sizeof(ThreadMesssage));
575     inst->ringReadPos = nextCmdIndex;
576   }
577 
578   pthread_mutex_unlock(&inst->hMutex);
579 
580   return result;
581 }
582 
583 /**************************************************************************************************
584  *
585  *                                     Buffer/Memory Management
586  *
587  **************************************************************************************************/
588 
589 /**
590  * Allocate buffer from pre-allocated pool.
591  * @param inst HAL instance
592  * @return Pointer to allocated HAL buffer
593  */
HalAllocBuffer(HalInstance * inst)594 static HalBuffer* HalAllocBuffer(HalInstance* inst) {
595   HalBuffer* b;
596 
597   // Wait until we have a buffer resource
598   sem_wait_nointr(&inst->bufferResourceSem);
599 
600   pthread_mutex_lock(&inst->hMutex);
601 
602   b = inst->freeBufferList;
603   if (b) {
604     inst->freeBufferList = b->next;
605     b->next = 0;
606   }
607 
608   pthread_mutex_unlock(&inst->hMutex);
609 
610   if (!b) {
611     STLOG_HAL_E(
612         "! unable to allocate buffer resource."
613         "check bufferResourceSem\n");
614   }
615 
616   return b;
617 }
618 
619 /**
620  * Return buffer to pool.
621  * @param inst HAL instance
622  * @param b Pointer of HAL buffer to free
623  * @return Pointer of freed HAL buffer
624  */
HalFreeBuffer(HalInstance * inst,HalBuffer * b)625 static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b) {
626   pthread_mutex_lock(&inst->hMutex);
627 
628   b->next = inst->freeBufferList;
629   inst->freeBufferList = b;
630 
631   pthread_mutex_unlock(&inst->hMutex);
632 
633   // Unblock treads waiting for a buffer
634   sem_post(&inst->bufferResourceSem);
635 
636   return b;
637 }
638 
639 /**************************************************************************************************
640  *
641  *                                     State Machine
642  *
643  **************************************************************************************************/
644 
645 /**
646  * Event handler for HAL message
647  * @param inst HAL instance
648  * @param e HAL event
649  */
Hal_event_handler(HalInstance * inst,HalEvent e)650 static void Hal_event_handler(HalInstance* inst, HalEvent e) {
651   switch (e) {
652     case EVT_RX_DATA: {
653       // New data packet arrived
654       const uint8_t* nciData;
655       size_t nciLength;
656 
657       // Extract raw NCI data from frame
658       nciData = inst->lastUsFrame;
659       nciLength = inst->lastUsFrameSize;
660 
661       // Pass received raw NCI data to stack
662       inst->callback(inst->context, HAL_EVENT_DATAIND, nciData, nciLength);
663     } break;
664 
665     case EVT_TX_DATA:
666       // NCI data arrived from stack
667       // Send data
668       inst->callback(inst->context, HAL_EVENT_DSWRITE, inst->nciBuffer->data,
669                      inst->nciBuffer->length);
670 
671       // Free the buffer
672       HalFreeBuffer(inst, inst->nciBuffer);
673       inst->nciBuffer = 0;
674       break;
675 
676     // HAL WRAPPER
677     case EVT_TIMER:
678       inst->callback(inst->context, HAL_EVENT_TIMER_TIMEOUT, NULL, 0);
679       break;
680   }
681 }
682 
683 /**************************************************************************************************
684  *
685  *                                     HAL Worker Thread
686  *
687  **************************************************************************************************/
688 
689 /**
690  * HAL worker thread to serialize all actions into a single thread.
691  * RX/TX/TIMER are dispatched from here.
692  * @param arg HAL instance arguments
693  */
HalWorkerThread(void * arg)694 static void* HalWorkerThread(void* arg) {
695   HalInstance* inst = (HalInstance*)arg;
696   inst->exitRequest = false;
697 
698   STLOG_HAL_V("thread running\n");
699 
700   while (!inst->exitRequest) {
701     struct timespec now = HalGetTimestamp();
702     uint32_t waitResult =
703         HalSemWait(&inst->semaphore, HalCalcSemWaitingTime(inst, &now));
704 
705     switch (waitResult) {
706       case OS_SYNC_TIMEOUT: {
707         // One or more times have expired
708         STLOG_HAL_W("OS_SYNC_TIMEOUT\n");
709         now = HalGetTimestamp();
710         // Data frame
711         Hal_event_handler(inst, EVT_TIMER);
712       } break;
713 
714       case OS_SYNC_RELEASED: {
715         // A message arrived
716         ThreadMesssage msg;
717 
718         if (HalDequeueThreadMessage(inst, &msg)) {
719           switch (msg.command) {
720             case MSG_EXIT_REQUEST:
721 
722               STLOG_HAL_V("received exit request from upper layer\n");
723               inst->exitRequest = true;
724               break;
725 
726             case MSG_TX_DATA:
727               STLOG_HAL_V("received new NCI data from stack\n");
728 
729               // Attack to end of list
730               if (!inst->pendingNciList) {
731                 inst->pendingNciList = msg.buffer;
732                 inst->pendingNciList->next = 0;
733               } else {
734                 // Find last element of the list. b->next is zero for this
735                 // element
736                 HalBuffer* b;
737                 for (b = inst->pendingNciList; b->next; b = b->next) {
738                 };
739 
740                 // Concatenate to list
741                 b->next = msg.buffer;
742                 msg.buffer->next = 0;
743               }
744 
745               // Start transmitting if we're in the correct state
746               HalTriggerNextDsPacket(inst);
747               break;
748 
749             // HAL WRAPPER
750             case MSG_TX_DATA_TIMER_START:
751               STLOG_HAL_V(
752                   "received new NCI data from stack, need timer start\n");
753 
754               // Attack to end of list
755               if (!inst->pendingNciList) {
756                 inst->pendingNciList = msg.buffer;
757                 inst->pendingNciList->next = 0;
758               } else {
759                 // Find last element of the list. b->next is zero for this
760                 // element
761                 HalBuffer* b;
762                 for (b = inst->pendingNciList; b->next; b = b->next) {
763                 };
764 
765                 // Concatenate to list
766                 b->next = msg.buffer;
767                 msg.buffer->next = 0;
768               }
769 
770               // Start timer
771               HalStartTimer(inst, msg.length);
772 
773               // Start transmitting if we're in the correct state
774               HalTriggerNextDsPacket(inst);
775               break;
776 
777             case MSG_RX_DATA:
778               STLOG_HAL_V("received new data from CLF\n");
779               HalOnNewUpstreamFrame(inst, (unsigned char*)msg.payload,
780                                     msg.length);
781               break;
782 
783             case MSG_TIMER_START:
784               // Start timer
785               HalStartTimer(inst, msg.length);
786               STLOG_HAL_D("MSG_TIMER_START \n");
787               break;
788             default:
789               STLOG_HAL_E("!received unkown thread message?\n");
790               break;
791           }
792         } else {
793           STLOG_HAL_E("!got wakeup in workerthread, but no message here? ?\n");
794         }
795       } break;
796 
797       case OS_SYNC_FAILED:
798 
799         STLOG_HAL_E(
800             "!Something went horribly wrong.. The semaphore wait function "
801             "failed\n");
802         inst->exitRequest = true;
803         break;
804     }
805   }
806 
807   STLOG_HAL_D("thread about to exit\n");
808   return NULL;
809 }
810 
811 /**************************************************************************************************
812  *
813  *                                     Misc. Functions
814  *
815  **************************************************************************************************/
816 /**
817  *  helper to make sem_t interrupt safe
818  * @param sem_t  semaphore
819  * @return sem_wait return value.
820  */
821 
sem_wait_nointr(sem_t * sem)822 static inline int sem_wait_nointr(sem_t* sem) {
823   while (sem_wait(sem))
824     if (errno == EINTR)
825       errno = 0;
826     else
827       return -1;
828   return 0;
829 }
830 
831 /**
832  * Handle RX frames here first in HAL context.
833  * @param inst HAL instance
834  * @param data HAL data received from I2C worker thread
835  * @param length Size of HAL data
836  */
HalOnNewUpstreamFrame(HalInstance * inst,const uint8_t * data,size_t length)837 static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data,
838                                   size_t length) {
839   memcpy(inst->lastUsFrame, data, length);
840   inst->lastUsFrameSize = length;
841 
842   // Data frame
843   Hal_event_handler(inst, EVT_RX_DATA);
844   // Allow the I2C thread to get the next message (if done early, it may
845   // overwrite before handled)
846   sem_post(&inst->upstreamBlock);
847 }
848 
849 /**
850  * Send out the next queued up buffer for TX if any.
851  * @param inst HAL instance
852  */
HalTriggerNextDsPacket(HalInstance * inst)853 static void HalTriggerNextDsPacket(HalInstance* inst) {
854   // Check if we have something to transmit downstream
855   HalBuffer* b = inst->pendingNciList;
856 
857   if (b) {
858     // Get the buffer from the pending list
859     inst->pendingNciList = b->next;
860     inst->nciBuffer = b;
861 
862     STLOG_HAL_V("trigger transport of next NCI data downstream\n");
863     // Process the new nci frame
864     Hal_event_handler(inst, EVT_TX_DATA);
865 
866   } else {
867     STLOG_HAL_V("no new NCI data to transmit, enter wait..\n");
868   }
869 }
870 
871 /*
872  * Wait for given semaphore signaling a specific time or ever
873  * param sem_t * pSemaphore
874  * param uint32_t timeout
875  * return uint32_t
876  */
HalSemWait(sem_t * pSemaphore,uint32_t timeout)877 static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout) {
878   uint32_t result = OS_SYNC_RELEASED;
879   bool gotResult = false;
880 
881   if (timeout == OS_SYNC_INFINITE) {
882     while (!gotResult) {
883       if (sem_wait(pSemaphore) == -1) {
884         int e = errno;
885         char msg[200];
886 
887         if (e == EINTR) {
888           STLOG_HAL_W(
889               "! semaphore (infin) wait interrupted by system signal. re-enter "
890               "wait");
891           continue;
892         }
893 
894         strerror_r(e, msg, sizeof(msg) - 1);
895         STLOG_HAL_E("! semaphore (infin) wait failed. sem=0x%p, %s", pSemaphore,
896                     msg);
897         gotResult = true;
898         result = OS_SYNC_FAILED;
899       } else {
900         gotResult = true;
901       }
902     };
903   } else {
904     struct timespec tm;
905     long oneSecInNs = (int)1e9;
906 
907     clock_gettime(CLOCK_REALTIME, &tm);
908 
909     /* add timeout (can't overflow): */
910     tm.tv_sec += (timeout / 1000);
911     tm.tv_nsec += ((timeout % 1000) * 1000000);
912 
913     /* make sure nanoseconds are below a million */
914     if (tm.tv_nsec >= oneSecInNs) {
915       tm.tv_sec++;
916       tm.tv_nsec -= oneSecInNs;
917     }
918 
919     while (!gotResult) {
920       if (sem_timedwait(pSemaphore, &tm) == -1) {
921         int e = errno;
922 
923         if (e == EINTR) {
924           /* interrupted by signal? repeat sem_wait again */
925           continue;
926         }
927 
928         if (e == ETIMEDOUT) {
929           result = OS_SYNC_TIMEOUT;
930           gotResult = true;
931         } else {
932           result = OS_SYNC_FAILED;
933           gotResult = true;
934         }
935       } else {
936         gotResult = true;
937       }
938     }
939   }
940   return result;
941 }
942