| /* |
| * Copyright 2012-2020, 2023 NXP |
| * |
| * 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. |
| */ |
| #include <errno.h> |
| #include <pthread.h> |
| #include <log/log.h> |
| |
| #include <phNxpLog.h> |
| #include <phNxpUciHal.h> |
| #include <phNxpUciHal_utils.h> |
| |
| using namespace std; |
| map<uint16_t, vector<uint16_t>> input_map; |
| map<uint16_t, vector<uint16_t>> conf_map; |
| |
| /****************** Semaphore and mutex helper functions **********************/ |
| /* Semaphore and mutex monitor */ |
| struct phNxpUciHal_Monitor { |
| public: |
| static std::unique_ptr<phNxpUciHal_Monitor> Create() { |
| //auto monitor = std::unique_ptr<phNxpUciHal_Monitor>(new phNxpUciHal_Monitor()); |
| auto monitor = std::make_unique<phNxpUciHal_Monitor>(); |
| if (pthread_mutex_init(&monitor->reentrance_mutex_, NULL) == -1) { |
| return nullptr; |
| } |
| if (pthread_mutex_init(&monitor->concurrency_mutex_, NULL) == -1) { |
| pthread_mutex_destroy(&monitor->reentrance_mutex_); |
| return nullptr; |
| } |
| return monitor; |
| } |
| |
| virtual ~phNxpUciHal_Monitor() { |
| pthread_mutex_destroy(&concurrency_mutex_); |
| ReentranceUnlock(); |
| pthread_mutex_destroy(&reentrance_mutex_); |
| for (auto p : sems_) { |
| NXPLOG_UCIHAL_E("Unreleased semaphore %p", p); |
| p->status = UWBSTATUS_FAILED; |
| sem_post(&p->sem); |
| } |
| sems_.clear(); |
| } |
| |
| void AddSem(phNxpUciHal_Sem_t* pCallbackData) { |
| std::lock_guard<std::mutex> lock(lock_); |
| auto it = sems_.find(pCallbackData); |
| if (it == sems_.end()) { |
| sems_.insert(pCallbackData); |
| } else { |
| NXPLOG_UCIHAL_E("phNxpUciHal_init_cb_data: duplicated semaphore %p", |
| pCallbackData); |
| } |
| } |
| |
| void RemoveSem(phNxpUciHal_Sem_t* pCallbackData) { |
| std::lock_guard<std::mutex> lock(lock_); |
| auto it = sems_.find(pCallbackData); |
| if (it == sems_.end()) { |
| NXPLOG_UCIHAL_E("phNxpUciHal_cleanup_cb_data: orphan semaphore %p", |
| pCallbackData); |
| } else { |
| sems_.erase(it); |
| } |
| } |
| |
| void Reentrancelock() { |
| pthread_mutex_lock(&reentrance_mutex_); |
| } |
| |
| void ReentranceUnlock() { |
| pthread_mutex_unlock(&reentrance_mutex_); |
| } |
| |
| void Concurrencylock() { |
| pthread_mutex_lock(&concurrency_mutex_); |
| } |
| |
| void ConcurrencyUnlock() { |
| pthread_mutex_unlock(&concurrency_mutex_); |
| } |
| |
| private: |
| std::unordered_set<phNxpUciHal_Sem_t*> sems_; |
| std::mutex lock_; |
| // Mutex protecting native library against reentrance |
| pthread_mutex_t reentrance_mutex_; |
| // Mutex protecting native library against concurrency |
| pthread_mutex_t concurrency_mutex_; |
| }; |
| |
| static std::unique_ptr<phNxpUciHal_Monitor> nxpucihal_monitor; |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_init_monitor |
| ** |
| ** Description Initialize the semaphore monitor |
| ** |
| ** Returns Pointer to monitor, otherwise NULL if failed |
| ** |
| *******************************************************************************/ |
| bool phNxpUciHal_init_monitor(void) { |
| NXPLOG_UCIHAL_D("Entering phNxpUciHal_init_monitor"); |
| |
| nxpucihal_monitor = phNxpUciHal_Monitor::Create(); |
| |
| if (nxpucihal_monitor == nullptr) { |
| NXPLOG_UCIHAL_E("nxphal_monitor creation failed"); |
| return false; |
| } |
| return true; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_cleanup_monitor |
| ** |
| ** Description Clean up semaphore monitor |
| ** |
| ** Returns None |
| ** |
| *******************************************************************************/ |
| void phNxpUciHal_cleanup_monitor(void) { |
| nxpucihal_monitor = nullptr; |
| } |
| |
| /* Initialize the callback data */ |
| tHAL_UWB_STATUS phNxpUciHal_init_cb_data(phNxpUciHal_Sem_t* pCallbackData, |
| void* pContext) { |
| /* Create semaphore */ |
| if (sem_init(&pCallbackData->sem, 0, 0) == -1) { |
| NXPLOG_UCIHAL_E("Semaphore creation failed"); |
| return UWBSTATUS_FAILED; |
| } |
| |
| /* Set default status value */ |
| pCallbackData->status = UWBSTATUS_FAILED; |
| |
| /* Copy the context */ |
| pCallbackData->pContext = pContext; |
| |
| /* Add to active semaphore list */ |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->AddSem(pCallbackData); |
| } |
| |
| return UWBSTATUS_SUCCESS; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_cleanup_cb_data |
| ** |
| ** Description Clean up callback data |
| ** |
| ** Returns None |
| ** |
| *******************************************************************************/ |
| void phNxpUciHal_cleanup_cb_data(phNxpUciHal_Sem_t* pCallbackData) { |
| /* Destroy semaphore */ |
| if (sem_destroy(&pCallbackData->sem)) { |
| NXPLOG_UCIHAL_E( |
| "phNxpUciHal_cleanup_cb_data: Failed to destroy semaphore"); |
| } |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->RemoveSem(pCallbackData); |
| } |
| } |
| |
| void REENTRANCE_LOCK() { |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->Reentrancelock(); |
| } |
| } |
| void REENTRANCE_UNLOCK() { |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->ReentranceUnlock(); |
| } |
| } |
| void CONCURRENCY_LOCK() { |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->Concurrencylock(); |
| } |
| } |
| void CONCURRENCY_UNLOCK() { |
| if (nxpucihal_monitor != nullptr) { |
| nxpucihal_monitor->ConcurrencyUnlock(); |
| } |
| } |
| |
| int phNxpUciHal_sem_timed_wait_msec(phNxpUciHal_Sem_t* pCallbackData, long msec) |
| { |
| int ret; |
| struct timespec absTimeout; |
| if (clock_gettime(CLOCK_MONOTONIC, &absTimeout) == -1) { |
| NXPLOG_UCIHAL_E("clock_gettime failed"); |
| return -1; |
| } |
| |
| if (msec > 1000L) { |
| absTimeout.tv_sec += msec / 1000L; |
| msec = msec % 1000L; |
| } |
| absTimeout.tv_nsec += msec * 1000000L; |
| if (absTimeout.tv_nsec > 1000000000L) { |
| absTimeout.tv_nsec -= 1000000000L; |
| absTimeout.tv_sec += 1; |
| } |
| |
| while ((ret = sem_timedwait_monotonic_np(&pCallbackData->sem, &absTimeout)) == -1 && errno == EINTR) { |
| continue; |
| } |
| if (ret == -1 && errno == ETIMEDOUT) { |
| pCallbackData->status = UWBSTATUS_RESPONSE_TIMEOUT; |
| NXPLOG_UCIHAL_E("wait semaphore timed out"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* END Semaphore and mutex helper functions */ |
| |
| /**************************** Other functions *********************************/ |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_print_packet |
| ** |
| ** Description Print packet |
| ** |
| ** Returns None |
| ** |
| *******************************************************************************/ |
| void phNxpUciHal_print_packet(enum phNxpUciHal_Pkt_Type what, const uint8_t* p_data, |
| uint16_t len) { |
| uint32_t i; |
| char print_buffer[len * 3 + 1]; |
| |
| if ((gLog_level.ucix_log_level >= NXPLOG_LOG_DEBUG_LOGLEVEL)) { |
| /* OK to print */ |
| } |
| else |
| { |
| /* Nothing to print... |
| * Why prepare buffer without printing? |
| */ |
| return; |
| } |
| |
| memset(print_buffer, 0, sizeof(print_buffer)); |
| for (i = 0; i < len; i++) { |
| snprintf(&print_buffer[i * 2], 3, "%02X", p_data[i]); |
| } |
| switch(what) { |
| case NXP_TML_UCI_CMD_AP_2_UWBS: |
| { |
| NXPLOG_UCIX_D("len = %3d > %s", len, print_buffer); |
| } |
| break; |
| case NXP_TML_UCI_RSP_NTF_UWBS_2_AP: |
| { |
| NXPLOG_UCIR_D("len = %3d < %s", len, print_buffer); |
| } |
| break; |
| case NXP_TML_FW_DNLD_CMD_AP_2_UWBS: |
| { |
| // TODO: Should be NXPLOG_FWDNLD_D |
| NXPLOG_UCIX_D("len = %3d > (FW)%s", len, print_buffer); |
| } |
| break; |
| case NXP_TML_FW_DNLD_RSP_UWBS_2_AP: |
| { |
| // TODO: Should be NXPLOG_FWDNLD_D |
| NXPLOG_UCIR_D("len = %3d < (FW)%s", len, print_buffer); |
| } |
| break; |
| } |
| |
| return; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_emergency_recovery |
| ** |
| ** Description Emergency recovery in case of no other way out |
| ** |
| ** Returns None |
| ** |
| *******************************************************************************/ |
| |
| void phNxpUciHal_emergency_recovery(void) { |
| NXPLOG_UCIHAL_E("%s: abort()", __func__); |
| abort(); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function phNxpUciHal_byteArrayToDouble |
| ** |
| ** Description convert byte array to double |
| ** |
| ** Returns double |
| ** |
| *******************************************************************************/ |
| double phNxpUciHal_byteArrayToDouble(const uint8_t* p_data) { |
| double d; |
| int size_d = sizeof(d); |
| uint8_t ptr[size_d],ptr_1[size_d]; |
| memcpy(&ptr, p_data, size_d); |
| for(int i=0;i<size_d;i++) { |
| ptr_1[i] = ptr[size_d - 1 - i]; |
| } |
| memcpy(&d, &ptr_1, sizeof(d)); |
| return d; \ |
| } |
| |
| std::map<uint16_t, std::vector<uint8_t>> |
| decodeTlvBytes(const std::vector<uint8_t> &ext_ids, const uint8_t *tlv_bytes, size_t tlv_len) |
| { |
| std::map<uint16_t, std::vector<uint8_t>> ret; |
| |
| size_t i = 0; |
| while ((i + 1) < tlv_len) { |
| uint16_t tag; |
| uint8_t len; |
| |
| uint8_t byte0 = tlv_bytes[i++]; |
| uint8_t byte1 = tlv_bytes[i++]; |
| if (std::find(ext_ids.begin(), ext_ids.end(), byte0) != ext_ids.end()) { |
| if (i >= tlv_len) { |
| NXPLOG_UCIHAL_E("Failed to decode TLV bytes (offset=%zu).", i); |
| break; |
| } |
| tag = (byte0 << 8) | byte1; // 2 bytes tag as big endiann |
| len = tlv_bytes[i++]; |
| } else { |
| tag = byte0; |
| len = byte1; |
| } |
| if ((i + len) > tlv_len) { |
| NXPLOG_UCIHAL_E("Failed to decode TLV bytes (offset=%zu).", i); |
| break; |
| } |
| ret[tag] = std::vector(&tlv_bytes[i], &tlv_bytes[i + len]); |
| i += len; |
| } |
| |
| return ret; |
| } |
| |
| std::vector<uint8_t> encodeTlvBytes(const std::map<uint16_t, std::vector<uint8_t>> &tlvs) |
| { |
| std::vector<uint8_t> bytes; |
| |
| for (auto const & [tag, val] : tlvs) { |
| // Tag |
| if (tag > 0xff) { |
| bytes.push_back(tag >> 8); |
| } |
| bytes.push_back(tag & 0xff); |
| |
| // Length |
| bytes.push_back(val.size()); |
| |
| // Value |
| bytes.insert(bytes.end(), val.begin(), val.end()); |
| } |
| |
| return bytes; |
| } |