266 lines
8.5 KiB
C
266 lines
8.5 KiB
C
#include <stdio.h>
|
|
|
|
#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]);
|
|
}
|
|
}
|