| /** ---------------------------------------------------------------------- |
| * |
| * Copyright (C) 2016 ST Microelectronics S.A. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * |
| ----------------------------------------------------------------------*/ |
| #define LOG_TAG "NfcHal" |
| |
| #include <hardware/nfc.h> |
| #include <pthread.h> |
| #include <semaphore.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "android_logmsg.h" |
| #include "halcore_private.h" |
| |
| extern int I2cWriteCmd(const uint8_t* x, size_t len); |
| extern void DispHal(const char* title, const void* data, size_t length); |
| |
| extern uint32_t ScrProtocolTraceFlag; // = SCR_PROTO_TRACE_ALL; |
| |
| // HAL WRAPPER |
| static void HalStopTimer(HalInstance* inst); |
| |
| typedef struct { |
| struct nfc_nci_device nci_device; // nci_device must be first struct member |
| // below declarations are private variables within HAL |
| nfc_stack_callback_t* p_cback; |
| nfc_stack_data_callback_t* p_data_cback; |
| HALHANDLE hHAL; |
| } st21nfc_dev_t; // beware, is a duplication of structure in nfc_nci_st21nfc.c |
| |
| /************************************************************************************************** |
| * |
| * Private API Declaration |
| * |
| **************************************************************************************************/ |
| |
| static void* HalWorkerThread(void* arg); |
| static inline int sem_wait_nointr(sem_t* sem); |
| |
| static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data, |
| size_t length); |
| static void HalTriggerNextDsPacket(HalInstance* inst); |
| static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg); |
| static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg); |
| static HalBuffer* HalAllocBuffer(HalInstance* inst); |
| static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b); |
| static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout); |
| |
| /************************************************************************************************** |
| * |
| * Public API Entry-Points |
| * |
| **************************************************************************************************/ |
| |
| /** |
| * Callback of HAL Core protocol layer. |
| * Invoked by HAL worker thread according to if message is received from NCI |
| * stack or posted by |
| * I2C worker thread. |
| * <p>@param context NFC callbacks for control/data |
| * @param event Next HAL state machine action (send msg to I2C layer or report |
| * data/control/error |
| * to NFC task) |
| * @param length Configure if debug and trace allowed, trace level |
| */ |
| void HalCoreCallback(void* context, uint32_t event, const void* d, |
| size_t length) { |
| const uint8_t* data = (const uint8_t*)d; |
| uint8_t cmd = 'W'; |
| |
| st21nfc_dev_t* dev = (st21nfc_dev_t*)context; |
| |
| switch (event) { |
| case HAL_EVENT_DSWRITE: |
| STLOG_HAL_V("!! got event HAL_EVENT_DSWRITE for %zu bytes\n", length); |
| DispHal("TX DATA", (data), length); |
| |
| // Send write command to IO thread |
| cmd = 'W'; |
| I2cWriteCmd(&cmd, sizeof(cmd)); |
| I2cWriteCmd((const uint8_t*)&length, sizeof(length)); |
| I2cWriteCmd(data, length); |
| break; |
| |
| case HAL_EVENT_DATAIND: |
| STLOG_HAL_V("!! got event HAL_EVENT_DATAIND for %zu bytes\n", length); |
| |
| if ((length >= 3) && (data[2] != (length - 3))) { |
| STLOG_HAL_W( |
| "length is illogical. Header length is %d, packet length %zu\n", |
| data[2], length); |
| } |
| |
| dev->p_data_cback(length, (uint8_t*)data); |
| break; |
| |
| case HAL_EVENT_ERROR: |
| STLOG_HAL_E("!! got event HAL_EVENT_ERROR\n"); |
| DispHal("Received unexpected HAL message !!!", data, length); |
| break; |
| |
| case HAL_EVENT_LINKLOST: |
| STLOG_HAL_E("!! got event HAL_EVENT_LINKLOST or HAL_EVENT_ERROR\n"); |
| |
| dev->p_cback(HAL_NFC_ERROR_EVT, HAL_NFC_STATUS_ERR_CMD_TIMEOUT); |
| |
| // Write terminate command |
| cmd = 'X'; |
| I2cWriteCmd(&cmd, sizeof(cmd)); |
| break; |
| |
| case HAL_EVENT_TIMER_TIMEOUT: |
| STLOG_HAL_D("!! got event HAL_EVENT_TIMER_TIMEOUT \n"); |
| dev->p_cback(HAL_WRAPPER_TIMEOUT_EVT, HAL_NFC_STATUS_OK); |
| break; |
| } |
| } |
| |
| /** |
| * Connection to the HAL Core layer. |
| * Set-up HAL context and create HAL worker thread. |
| * <p>@param context NFC NCI device context, NFC callbacks for control/data, HAL |
| * handle |
| * @param callback HAL callback function pointer |
| * @param flags Configure if debug and trace allowed, trace level |
| */ |
| HALHANDLE HalCreate(void* context, HAL_CALLBACK callback, uint32_t flags) { |
| /* bool halTraceMask = true; |
| |
| if (flags & HAL_FLAG_NO_DEBUG) { |
| halTraceMask = false; |
| } |
| */ |
| STLOG_HAL_V("HalCreate enter\n"); |
| |
| HalInstance* inst = (HalInstance*)calloc(1, sizeof(HalInstance)); |
| |
| if (!inst) { |
| STLOG_HAL_E("!out of memory\n"); |
| return NULL; |
| } |
| |
| // We need a semaphore to wakeup our protocol thread |
| if (0 != sem_init(&inst->semaphore, 0, 0)) { |
| STLOG_HAL_E("!sem_init failed\n"); |
| free(inst); |
| return NULL; |
| } |
| |
| // We need a semaphore to manage buffers |
| if (0 != sem_init(&inst->bufferResourceSem, 0, NUM_BUFFERS)) { |
| STLOG_HAL_E("!sem_init failed\n"); |
| sem_destroy(&inst->semaphore); |
| free(inst); |
| return NULL; |
| } |
| |
| // We need a semaphore to block upstream data indications |
| if (0 != sem_init(&inst->upstreamBlock, 0, 0)) { |
| STLOG_HAL_E("!sem_init failed\n"); |
| sem_destroy(&inst->semaphore); |
| sem_destroy(&inst->bufferResourceSem); |
| free(inst); |
| return NULL; |
| } |
| |
| // Initialize remaining data-members |
| inst->context = context; |
| inst->callback = callback; |
| inst->flags = flags; |
| inst->freeBufferList = 0; |
| inst->pendingNciList = 0; |
| inst->nciBuffer = 0; |
| inst->ringReadPos = 0; |
| inst->ringWritePos = 0; |
| inst->timeout = HAL_SLEEP_TIMER_DURATION; |
| |
| inst->bufferData = (HalBuffer*)calloc(NUM_BUFFERS, sizeof(HalBuffer)); |
| if (!inst->bufferData) { |
| STLOG_HAL_E("!failed to allocate memory\n"); |
| sem_destroy(&inst->semaphore); |
| sem_destroy(&inst->bufferResourceSem); |
| sem_destroy(&inst->upstreamBlock); |
| free(inst); |
| return NULL; |
| } |
| |
| // Concatenate the buffers into a linked list for easy access |
| size_t i; |
| for (i = 0; i < NUM_BUFFERS; i++) { |
| HalBuffer* b = &inst->bufferData[i]; |
| b->next = inst->freeBufferList; |
| inst->freeBufferList = b; |
| } |
| |
| if (0 != pthread_mutex_init(&inst->hMutex, 0)) { |
| STLOG_HAL_E("!failed to initialize Mutex \n"); |
| sem_destroy(&inst->semaphore); |
| sem_destroy(&inst->bufferResourceSem); |
| sem_destroy(&inst->upstreamBlock); |
| free(inst->bufferData); |
| free(inst); |
| return NULL; |
| } |
| |
| // Spawn the thread |
| if (0 != pthread_create(&inst->thread, NULL, HalWorkerThread, inst)) { |
| STLOG_HAL_E("!failed to spawn workerthread \n"); |
| sem_destroy(&inst->semaphore); |
| sem_destroy(&inst->bufferResourceSem); |
| sem_destroy(&inst->upstreamBlock); |
| pthread_mutex_destroy(&inst->hMutex); |
| free(inst->bufferData); |
| free(inst); |
| return NULL; |
| } |
| |
| STLOG_HAL_V("HalCreate exit\n"); |
| return (HALHANDLE)inst; |
| } |
| |
| /** |
| * Disconnection of the HAL protocol layer. |
| * Send message to stop the HAL worker thread and wait for it to finish. Free |
| * resources. |
| * @param hHAL HAL handle |
| */ |
| void HalDestroy(HALHANDLE hHAL) { |
| HalInstance* inst = (HalInstance*)hHAL; |
| // Tell the thread that we want to finish |
| ThreadMesssage msg; |
| msg.command = MSG_EXIT_REQUEST; |
| msg.payload = 0; |
| msg.length = 0; |
| |
| HalEnqueueThreadMessage(inst, &msg); |
| |
| // Wait for thread to finish |
| pthread_join(inst->thread, NULL); |
| |
| // Cleanup and exit |
| sem_destroy(&inst->semaphore); |
| sem_destroy(&inst->upstreamBlock); |
| sem_destroy(&inst->bufferResourceSem); |
| pthread_mutex_destroy(&inst->hMutex); |
| |
| // Free resources |
| free(inst->bufferData); |
| free(inst); |
| |
| STLOG_HAL_V("HalDestroy done\n"); |
| } |
| |
| /** |
| * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer). |
| * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will |
| * return immediately. |
| * @param hHAL HAL handle |
| * @param data Data message |
| * @param size Message size |
| */ bool HalSendDownstream(HALHANDLE hHAL, const uint8_t* data, size_t size) |
| { |
| // Send an NCI frame downstream. will |
| HalInstance* inst = (HalInstance*)hHAL; |
| |
| if ((size <= MAX_BUFFER_SIZE) && (size > 0)) { |
| ThreadMesssage msg; |
| HalBuffer* b = HalAllocBuffer(inst); |
| |
| if (!b) { |
| // Should never be reachable |
| return false; |
| } |
| |
| memcpy(b->data, data, size); |
| b->length = size; |
| |
| msg.command = MSG_TX_DATA; |
| msg.payload = 0; |
| msg.length = 0; |
| msg.buffer = b; |
| |
| return HalEnqueueThreadMessage(inst, &msg); |
| |
| } else { |
| STLOG_HAL_E("HalSendDownstream size to large %zu instead of %d\n", size, |
| MAX_BUFFER_SIZE); |
| return false; |
| } |
| } |
| |
| // HAL WRAPPER |
| /** |
| * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer). |
| * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will |
| * return immediately. |
| * @param hHAL HAL handle |
| * @param data Data message |
| * @param size Message size |
| */ |
| bool HalSendDownstreamTimer(HALHANDLE hHAL, const uint8_t* data, size_t size, |
| uint32_t duration) { |
| // Send an NCI frame downstream. will |
| HalInstance* inst = (HalInstance*)hHAL; |
| |
| if ((size <= MAX_BUFFER_SIZE) && (size > 0)) { |
| ThreadMesssage msg; |
| HalBuffer* b = HalAllocBuffer(inst); |
| |
| if (!b) { |
| // Should never be reachable |
| return false; |
| } |
| |
| memcpy(b->data, data, size); |
| b->length = size; |
| |
| msg.command = MSG_TX_DATA_TIMER_START; |
| msg.payload = 0; |
| msg.length = duration; |
| msg.buffer = b; |
| |
| return HalEnqueueThreadMessage(inst, &msg); |
| |
| } else { |
| STLOG_HAL_E("HalSendDownstreamTimer size to large %zu instead of %d\n", |
| size, MAX_BUFFER_SIZE); |
| return false; |
| } |
| } |
| |
| bool HalSendDownstreamTimer(HALHANDLE hHAL, uint32_t duration) { |
| HalInstance* inst = (HalInstance*)hHAL; |
| |
| ThreadMesssage msg; |
| |
| msg.command = MSG_TIMER_START; |
| msg.payload = 0; |
| msg.length = duration; |
| msg.buffer = NULL; |
| |
| return HalEnqueueThreadMessage(inst, &msg); |
| } |
| /** |
| * Send an NCI message downstream to HAL protocol layer (DH->NFCC transfer). |
| * Block if more than NUM_BUFFERS (10) transfers are outstanding, otherwise will |
| * return immediately. |
| * @param hHAL HAL handle |
| * @param data Data message |
| * @param size Message size |
| */ |
| bool HalSendDownstreamStopTimer(HALHANDLE hHAL) { |
| // Send an NCI frame downstream. will |
| HalInstance* inst = (HalInstance*)hHAL; |
| |
| HalStopTimer(inst); |
| return 1; |
| } |
| |
| /** |
| * Send an NCI message upstream to NFC NCI layer (NFCC->DH transfer). |
| * @param hHAL HAL handle |
| * @param data Data message |
| * @param size Message size |
| */ |
| bool HalSendUpstream(HALHANDLE hHAL, const uint8_t* data, size_t size) { |
| HalInstance* inst = (HalInstance*)hHAL; |
| if ((size <= MAX_BUFFER_SIZE) && (size > 0)) { |
| ThreadMesssage msg; |
| msg.command = MSG_RX_DATA; |
| msg.payload = data; |
| msg.length = size; |
| |
| if (HalEnqueueThreadMessage(inst, &msg)) { |
| // Block until the protocol has taken a copy of the data |
| sem_wait_nointr(&inst->upstreamBlock); |
| return true; |
| } |
| return false; |
| } else { |
| STLOG_HAL_E("HalSendUpstream size to large %zu instead of %d\n", size, |
| MAX_BUFFER_SIZE); |
| return false; |
| } |
| } |
| |
| /************************************************************************************************** |
| * |
| * Private API Definition |
| * |
| **************************************************************************************************/ |
| /* |
| * Get current time stamp |
| */ |
| struct timespec HalGetTimestamp(void) { |
| struct timespec tm; |
| clock_gettime(CLOCK_REALTIME, &tm); |
| return tm; |
| } |
| |
| int HalTimeDiffInMs(struct timespec start, struct timespec end) { |
| struct timespec temp; |
| if ((end.tv_nsec - start.tv_nsec) < 0) { |
| temp.tv_sec = end.tv_sec - start.tv_sec - 1; |
| temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; |
| } else { |
| temp.tv_sec = end.tv_sec - start.tv_sec; |
| temp.tv_nsec = end.tv_nsec - start.tv_nsec; |
| } |
| |
| return (temp.tv_nsec / 1000000) + (temp.tv_sec * 1000); |
| } |
| |
| /** |
| * Determine the next shortest sleep to fulfill the pending timer requirements. |
| * @param inst HAL instance |
| * @param now timespec structure for time definition |
| */ |
| static uint32_t HalCalcSemWaitingTime(HalInstance* inst, struct timespec* now) { |
| // Default to infinite wait time |
| uint32_t result = OS_SYNC_INFINITE; |
| |
| if (inst->timer.active) { |
| int delta = |
| inst->timer.duration - HalTimeDiffInMs(inst->timer.startTime, *now); |
| |
| if (delta < 0) { |
| // If we have a timer that has already expired, pick a zero wait time |
| result = 0; |
| |
| } else if ((uint32_t)delta < result) { |
| // Smaller time difference? If so take it |
| result = delta; |
| } |
| } |
| |
| if (result != OS_SYNC_INFINITE) { |
| // Add one millisecond on top of that, so the waiting semaphore will time |
| // out just a moment |
| // after the timer should expire |
| result += 1; |
| } |
| |
| return result; |
| } |
| |
| /************************************************************************************************** |
| * |
| * Timer Management |
| * |
| **************************************************************************************************/ |
| |
| static void HalStopTimer(HalInstance* inst) { |
| inst->timer.active = false; |
| STLOG_HAL_D("HalStopTimer \n"); |
| } |
| |
| static void HalStartTimer(HalInstance* inst, uint32_t duration) { |
| STLOG_HAL_D("HalStartTimer \n"); |
| inst->timer.startTime = HalGetTimestamp(); |
| inst->timer.active = true; |
| inst->timer.duration = duration; |
| } |
| |
| /************************************************************************************************** |
| * |
| * Thread Message Queue |
| * |
| **************************************************************************************************/ |
| |
| /** |
| * Write message pointer to small ring buffer for queuing HAL messages. |
| * @param inst HAL instance |
| * @param msg Message to send |
| * @return true if message properly copied in ring buffer |
| */ |
| static bool HalEnqueueThreadMessage(HalInstance* inst, ThreadMesssage* msg) { |
| // Put a message to the queue |
| int nextWriteSlot; |
| bool result = true; |
| |
| pthread_mutex_lock(&inst->hMutex); |
| |
| nextWriteSlot = inst->ringWritePos + 1; |
| |
| if (nextWriteSlot == HAL_QUEUE_MAX) { |
| nextWriteSlot = 0; |
| } |
| |
| // Check that we don't overflow the queue entries |
| if (nextWriteSlot == inst->ringReadPos) { |
| STLOG_HAL_E("HAL thread message ring: RNR (implement me!!)"); |
| result = false; |
| } |
| |
| if (result) { |
| // inst->ring[nextWriteSlot] = *msg; |
| memcpy(&(inst->ring[nextWriteSlot]), msg, sizeof(ThreadMesssage)); |
| inst->ringWritePos = nextWriteSlot; |
| } |
| |
| pthread_mutex_unlock(&inst->hMutex); |
| |
| if (result) { |
| sem_post(&inst->semaphore); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Remove message pointer from stored ring buffer. |
| * @param inst HAL instance |
| * @param msg Message received |
| * @return true if there is a new message to pull, false otherwise. |
| */ |
| static bool HalDequeueThreadMessage(HalInstance* inst, ThreadMesssage* msg) { |
| int nextCmdIndex; |
| bool result = true; |
| // New data available |
| pthread_mutex_lock(&inst->hMutex); |
| |
| // Get new timer read index |
| nextCmdIndex = inst->ringReadPos + 1; |
| |
| if (nextCmdIndex == HAL_QUEUE_MAX) { |
| nextCmdIndex = 0; |
| } |
| // check if ring buffer is empty |
| if (inst->ringReadPos == inst->ringWritePos) { |
| STLOG_HAL_E("HAL thread message ring: already read last valid data"); |
| result = false; |
| } |
| |
| // Get new element from ringbuffer |
| if (result) { |
| memcpy(msg, &(inst->ring[nextCmdIndex]), sizeof(ThreadMesssage)); |
| inst->ringReadPos = nextCmdIndex; |
| } |
| |
| pthread_mutex_unlock(&inst->hMutex); |
| |
| return result; |
| } |
| |
| /************************************************************************************************** |
| * |
| * Buffer/Memory Management |
| * |
| **************************************************************************************************/ |
| |
| /** |
| * Allocate buffer from pre-allocated pool. |
| * @param inst HAL instance |
| * @return Pointer to allocated HAL buffer |
| */ |
| static HalBuffer* HalAllocBuffer(HalInstance* inst) { |
| HalBuffer* b; |
| |
| // Wait until we have a buffer resource |
| sem_wait_nointr(&inst->bufferResourceSem); |
| |
| pthread_mutex_lock(&inst->hMutex); |
| |
| b = inst->freeBufferList; |
| if (b) { |
| inst->freeBufferList = b->next; |
| b->next = 0; |
| } |
| |
| pthread_mutex_unlock(&inst->hMutex); |
| |
| if (!b) { |
| STLOG_HAL_E( |
| "! unable to allocate buffer resource." |
| "check bufferResourceSem\n"); |
| } |
| |
| return b; |
| } |
| |
| /** |
| * Return buffer to pool. |
| * @param inst HAL instance |
| * @param b Pointer of HAL buffer to free |
| * @return Pointer of freed HAL buffer |
| */ |
| static HalBuffer* HalFreeBuffer(HalInstance* inst, HalBuffer* b) { |
| pthread_mutex_lock(&inst->hMutex); |
| |
| b->next = inst->freeBufferList; |
| inst->freeBufferList = b; |
| |
| pthread_mutex_unlock(&inst->hMutex); |
| |
| // Unblock treads waiting for a buffer |
| sem_post(&inst->bufferResourceSem); |
| |
| return b; |
| } |
| |
| /************************************************************************************************** |
| * |
| * State Machine |
| * |
| **************************************************************************************************/ |
| |
| /** |
| * Event handler for HAL message |
| * @param inst HAL instance |
| * @param e HAL event |
| */ |
| static void Hal_event_handler(HalInstance* inst, HalEvent e) { |
| switch (e) { |
| case EVT_RX_DATA: { |
| // New data packet arrived |
| const uint8_t* nciData; |
| size_t nciLength; |
| |
| // Extract raw NCI data from frame |
| nciData = inst->lastUsFrame; |
| nciLength = inst->lastUsFrameSize; |
| |
| // Pass received raw NCI data to stack |
| inst->callback(inst->context, HAL_EVENT_DATAIND, nciData, nciLength); |
| } break; |
| |
| case EVT_TX_DATA: |
| // NCI data arrived from stack |
| // Send data |
| inst->callback(inst->context, HAL_EVENT_DSWRITE, inst->nciBuffer->data, |
| inst->nciBuffer->length); |
| |
| // Free the buffer |
| HalFreeBuffer(inst, inst->nciBuffer); |
| inst->nciBuffer = 0; |
| break; |
| |
| // HAL WRAPPER |
| case EVT_TIMER: |
| inst->callback(inst->context, HAL_EVENT_TIMER_TIMEOUT, NULL, 0); |
| break; |
| } |
| } |
| |
| /************************************************************************************************** |
| * |
| * HAL Worker Thread |
| * |
| **************************************************************************************************/ |
| |
| /** |
| * HAL worker thread to serialize all actions into a single thread. |
| * RX/TX/TIMER are dispatched from here. |
| * @param arg HAL instance arguments |
| */ |
| static void* HalWorkerThread(void* arg) { |
| HalInstance* inst = (HalInstance*)arg; |
| inst->exitRequest = false; |
| |
| STLOG_HAL_V("thread running\n"); |
| |
| while (!inst->exitRequest) { |
| struct timespec now = HalGetTimestamp(); |
| uint32_t waitResult = |
| HalSemWait(&inst->semaphore, HalCalcSemWaitingTime(inst, &now)); |
| |
| switch (waitResult) { |
| case OS_SYNC_TIMEOUT: { |
| // One or more times have expired |
| STLOG_HAL_W("OS_SYNC_TIMEOUT\n"); |
| now = HalGetTimestamp(); |
| // Data frame |
| Hal_event_handler(inst, EVT_TIMER); |
| } break; |
| |
| case OS_SYNC_RELEASED: { |
| // A message arrived |
| ThreadMesssage msg; |
| |
| if (HalDequeueThreadMessage(inst, &msg)) { |
| switch (msg.command) { |
| case MSG_EXIT_REQUEST: |
| |
| STLOG_HAL_V("received exit request from upper layer\n"); |
| inst->exitRequest = true; |
| break; |
| |
| case MSG_TX_DATA: |
| STLOG_HAL_V("received new NCI data from stack\n"); |
| |
| // Attack to end of list |
| if (!inst->pendingNciList) { |
| inst->pendingNciList = msg.buffer; |
| inst->pendingNciList->next = 0; |
| } else { |
| // Find last element of the list. b->next is zero for this |
| // element |
| HalBuffer* b; |
| for (b = inst->pendingNciList; b->next; b = b->next) { |
| }; |
| |
| // Concatenate to list |
| b->next = msg.buffer; |
| msg.buffer->next = 0; |
| } |
| |
| // Start transmitting if we're in the correct state |
| HalTriggerNextDsPacket(inst); |
| break; |
| |
| // HAL WRAPPER |
| case MSG_TX_DATA_TIMER_START: |
| STLOG_HAL_V( |
| "received new NCI data from stack, need timer start\n"); |
| |
| // Attack to end of list |
| if (!inst->pendingNciList) { |
| inst->pendingNciList = msg.buffer; |
| inst->pendingNciList->next = 0; |
| } else { |
| // Find last element of the list. b->next is zero for this |
| // element |
| HalBuffer* b; |
| for (b = inst->pendingNciList; b->next; b = b->next) { |
| }; |
| |
| // Concatenate to list |
| b->next = msg.buffer; |
| msg.buffer->next = 0; |
| } |
| |
| // Start timer |
| HalStartTimer(inst, msg.length); |
| |
| // Start transmitting if we're in the correct state |
| HalTriggerNextDsPacket(inst); |
| break; |
| |
| case MSG_RX_DATA: |
| STLOG_HAL_V("received new data from CLF\n"); |
| HalOnNewUpstreamFrame(inst, (unsigned char*)msg.payload, |
| msg.length); |
| break; |
| |
| case MSG_TIMER_START: |
| // Start timer |
| HalStartTimer(inst, msg.length); |
| STLOG_HAL_D("MSG_TIMER_START \n"); |
| break; |
| default: |
| STLOG_HAL_E("!received unkown thread message?\n"); |
| break; |
| } |
| } else { |
| STLOG_HAL_E("!got wakeup in workerthread, but no message here? ?\n"); |
| } |
| } break; |
| |
| case OS_SYNC_FAILED: |
| |
| STLOG_HAL_E( |
| "!Something went horribly wrong.. The semaphore wait function " |
| "failed\n"); |
| inst->exitRequest = true; |
| break; |
| } |
| } |
| |
| STLOG_HAL_D("thread about to exit\n"); |
| return NULL; |
| } |
| |
| /************************************************************************************************** |
| * |
| * Misc. Functions |
| * |
| **************************************************************************************************/ |
| /** |
| * helper to make sem_t interrupt safe |
| * @param sem_t semaphore |
| * @return sem_wait return value. |
| */ |
| |
| static inline int sem_wait_nointr(sem_t* sem) { |
| while (sem_wait(sem)) |
| if (errno == EINTR) |
| errno = 0; |
| else |
| return -1; |
| return 0; |
| } |
| |
| /** |
| * Handle RX frames here first in HAL context. |
| * @param inst HAL instance |
| * @param data HAL data received from I2C worker thread |
| * @param length Size of HAL data |
| */ |
| static void HalOnNewUpstreamFrame(HalInstance* inst, const uint8_t* data, |
| size_t length) { |
| memcpy(inst->lastUsFrame, data, length); |
| inst->lastUsFrameSize = length; |
| |
| // Data frame |
| Hal_event_handler(inst, EVT_RX_DATA); |
| // Allow the I2C thread to get the next message (if done early, it may |
| // overwrite before handled) |
| sem_post(&inst->upstreamBlock); |
| } |
| |
| /** |
| * Send out the next queued up buffer for TX if any. |
| * @param inst HAL instance |
| */ |
| static void HalTriggerNextDsPacket(HalInstance* inst) { |
| // Check if we have something to transmit downstream |
| HalBuffer* b = inst->pendingNciList; |
| |
| if (b) { |
| // Get the buffer from the pending list |
| inst->pendingNciList = b->next; |
| inst->nciBuffer = b; |
| |
| STLOG_HAL_V("trigger transport of next NCI data downstream\n"); |
| // Process the new nci frame |
| Hal_event_handler(inst, EVT_TX_DATA); |
| |
| } else { |
| STLOG_HAL_V("no new NCI data to transmit, enter wait..\n"); |
| } |
| } |
| |
| /* |
| * Wait for given semaphore signaling a specific time or ever |
| * param sem_t * pSemaphore |
| * param uint32_t timeout |
| * return uint32_t |
| */ |
| static uint32_t HalSemWait(sem_t* pSemaphore, uint32_t timeout) { |
| uint32_t result = OS_SYNC_RELEASED; |
| bool gotResult = false; |
| |
| if (timeout == OS_SYNC_INFINITE) { |
| while (!gotResult) { |
| if (sem_wait(pSemaphore) == -1) { |
| int e = errno; |
| char msg[200]; |
| |
| if (e == EINTR) { |
| STLOG_HAL_W( |
| "! semaphore (infin) wait interrupted by system signal. re-enter " |
| "wait"); |
| continue; |
| } |
| |
| strerror_r(e, msg, sizeof(msg) - 1); |
| STLOG_HAL_E("! semaphore (infin) wait failed. sem=0x%p, %s", pSemaphore, |
| msg); |
| gotResult = true; |
| result = OS_SYNC_FAILED; |
| } else { |
| gotResult = true; |
| } |
| }; |
| } else { |
| struct timespec tm; |
| long oneSecInNs = (int)1e9; |
| |
| clock_gettime(CLOCK_REALTIME, &tm); |
| |
| /* add timeout (can't overflow): */ |
| tm.tv_sec += (timeout / 1000); |
| tm.tv_nsec += ((timeout % 1000) * 1000000); |
| |
| /* make sure nanoseconds are below a million */ |
| if (tm.tv_nsec >= oneSecInNs) { |
| tm.tv_sec++; |
| tm.tv_nsec -= oneSecInNs; |
| } |
| |
| while (!gotResult) { |
| if (sem_timedwait(pSemaphore, &tm) == -1) { |
| int e = errno; |
| |
| if (e == EINTR) { |
| /* interrupted by signal? repeat sem_wait again */ |
| continue; |
| } |
| |
| if (e == ETIMEDOUT) { |
| result = OS_SYNC_TIMEOUT; |
| gotResult = true; |
| } else { |
| result = OS_SYNC_FAILED; |
| gotResult = true; |
| } |
| } else { |
| gotResult = true; |
| } |
| } |
| } |
| return result; |
| } |