#include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "esp_timer.h" #include "esp_log.h" #include "esp_err.h" #include "transaction.h" #define TAG "TRANS" static transaction_t transactions[MAX_TRANSACTION_COUNT]; static SemaphoreHandle_t trans_mutex; static uint32_t utils_get_system_ts(void) { return esp_log_timestamp(); } static void transaction_reset(transaction_t *trans) { EventBits_t bits; ESP_LOGV(TAG, "transaction reset: %x", (uint32_t) trans); // set to inactive state and clear all bits of the transaction xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); trans->type = 0; trans->sub_type = 0; trans->current_bits = 0; bits = xEventGroupGetBits(trans->event_group); xEventGroupClearBits(trans->event_group, bits); trans->state = TRANSACTION_INACTIVE; trans->ret = ESP_OK; xSemaphoreGiveRecursive(trans_mutex); } void transaction_deinit(transaction_t *trans) { if (trans != NULL) { transaction_reset(trans); } } esp_err_t transaction_set_events(transaction_t *trans, EventBits_t events) { esp_err_t ret = ESP_OK; xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); if (trans) { if (trans->state == TRANSACTION_INACTIVE) { ret = TRANS_RET_STATE_ERR; } else { // if the task (task A) setting current bits is with higher priority than the task (task B) run transaction, // current_bits might not be updated until task A yield (not it's only update in run_transaction function). // If task A set events and immediately use current_bits, current_bits is not correct. // update current_bits here to make sure it's updated trans->current_bits |= events; xEventGroupSetBits(trans->event_group, events); } ESP_LOGD(TAG, "transactions set events: %x, %x, %x, %x; ret: %x", (uint32_t) trans, trans->type, trans->sub_type, events, ret); } else { ret = TRANS_RET_INVALID_TRNSACTION; } xSemaphoreGiveRecursive(trans_mutex); return ret; } esp_err_t transaction_test_events(transaction_t *trans, EventBits_t events) { esp_err_t ret = TRANS_RET_TEST_EVENT_FAILED; xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); if (trans) { if (trans->state == TRANSACTION_INACTIVE) { ret = TRANS_RET_STATE_ERR; } else { if ((trans->current_bits & events) == events) { ret = ESP_OK; } } ESP_LOGV(TAG, "transactions test events: %x, %x; ret: %x", (uint32_t) trans, events, ret); } else { ret = TRANS_RET_INVALID_TRNSACTION; } xSemaphoreGiveRecursive(trans_mutex); return ret; } esp_err_t transaction_clear_events(transaction_t *trans, EventBits_t events) { esp_err_t ret = ESP_OK; xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); if (trans) { if (trans->state == TRANSACTION_INACTIVE) { ret = TRANS_RET_STATE_ERR; } else { trans->current_bits &= ~events; xEventGroupClearBits(trans->event_group, events); } ESP_LOGD(TAG, "transactions clear events: %x, %x, %x, %x; ret: %x", (uint32_t) trans, trans->type, trans->sub_type, events, ret); } else { ret = TRANS_RET_INVALID_TRNSACTION; } xSemaphoreGiveRecursive(trans_mutex); return ret; } esp_err_t transaction_abort(transaction_t *trans, esp_err_t reason) { esp_err_t ret = ESP_OK; xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); if (trans) { if (trans->state == TRANSACTION_INACTIVE) { ret = TRANS_RET_STATE_ERR; } else { trans->ret = reason; xEventGroupSetBits(trans->event_group, TRANSACTION_ABORT_EVENT); } ESP_LOGD(TAG, "transactions abort: %x, %x, %x, %x; ret: %x", (uint32_t) trans, trans->type, trans->sub_type, reason, ret); } else { ret = TRANS_RET_INVALID_TRNSACTION; } xSemaphoreGiveRecursive(trans_mutex); return ret; } esp_err_t transaction_init(transaction_t **trans, uint8_t type, uint32_t sub_type, EventBits_t wait_events, uint32_t timeout, void *input, void *output) { esp_err_t ret = ESP_OK; uint8_t i; if ((wait_events & TRANSACTION_EVENT_MASK) && wait_events != TRANSACTION_TIMEOUT_EVENT) { ret = TRANS_RET_EVENTS_CONFLICT; return ret; } xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); for (i = 0; i < MAX_TRANSACTION_COUNT; i++) { if ( transactions[i].state == TRANSACTION_INACTIVE ) { transactions[i].state = TRANSACTION_ACTIVE; *trans = &transactions[i]; break; } } if ( i == MAX_TRANSACTION_COUNT ) { ret = TRANS_RET_FAILED_TO_ALLOCATE; } else { // init transaction transactions[i].type = type; transactions[i].wait_events = wait_events; transactions[i].sub_type = sub_type; transactions[i].timeout = timeout; transactions[i].ret = ESP_OK; transactions[i].input = input; transactions[i].output = output; } xSemaphoreGiveRecursive(trans_mutex); if (ret == ESP_OK) { ESP_LOGD(TAG, "transaction created: %x, %x, %x; ret: %x", type, sub_type, (uint32_t) *trans, ret); } return ret; } esp_err_t transaction_run(transaction_t *trans) { esp_err_t ret = ESP_OK; uint32_t start_time; int32_t wait_time; EventBits_t current_bits; if (trans) { start_time = utils_get_system_ts(); // wait for wait events while (1) { //TODO: we didn't handle ts overflow wait_time = start_time + trans->timeout - utils_get_system_ts(); if ( wait_time < 0 ) { ESP_LOGI(TAG, "transaction timeout: %x, %x, %x, %x, %x", (uint32_t) trans, trans->type, trans->sub_type, trans->wait_events, trans->current_bits); ret = TRANS_RET_TIMEOUT; break; } // trans->event_group and trans->wait_events will not be changed once trans is created, so we don't need protect them current_bits = xEventGroupWaitBits(trans->event_group, trans->wait_events | TRANSACTION_ABORT_EVENT, 1, 0, wait_time/portTICK_RATE_MS); xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); trans->current_bits |= current_bits; if (trans->current_bits == trans->wait_events) { // wait succeeded, we copy the trans->ret as ret of run transaction. This value could be changed by ret = trans->ret; xSemaphoreGiveRecursive(trans_mutex); break; } else if ( trans->current_bits & TRANSACTION_ABORT_EVENT ) { if ( trans->ret ) { // copy user defined ret value if it's set ret = trans->ret; } else { ret = TRANS_RET_ABORTED; } xSemaphoreGiveRecursive(trans_mutex); break; } xSemaphoreGiveRecursive(trans_mutex); } ESP_LOGD(TAG, "transaction run: %x, %x, %x; ret: %x", (uint32_t) trans, trans->type, trans->sub_type, ret); // reset after it's finished transaction_reset(trans); } else { ESP_LOGD(TAG, "transaction run: %x; ret: %x", (uint32_t) trans, ret); ret = TRANS_RET_INVALID_TRNSACTION; } return ret; } transaction_t *transaction_get(uint8_t type, uint32_t sub_type, transaction_t *start) { uint8_t i, start_index; transaction_t *trans = NULL; if ( start == NULL ) { start_index = 0; } else { start_index = (start - transactions) + 1; } xSemaphoreTakeRecursive(trans_mutex, portMAX_DELAY); for (i = start_index; i < MAX_TRANSACTION_COUNT; i++) { if ( transactions[i].state == TRANSACTION_ACTIVE ) { if ( (transactions[i].type == type) && (transactions[i].sub_type & sub_type) ) { trans = &transactions[i]; break; } } } xSemaphoreGiveRecursive(trans_mutex); ESP_LOGV(TAG, "transaction get: %x, %x, %x, %x", type, sub_type, (uint32_t) start, (uint32_t) trans); return trans; } void init_transactions(void) { uint8_t i; ESP_LOGI(TAG, "init transactions"); trans_mutex = xSemaphoreCreateRecursiveMutex(); for (i = 0; i < MAX_TRANSACTION_COUNT; i++) { transactions[i].event_group = xEventGroupCreate(); transaction_reset(&transactions[i]); } }