2758 lines
86 KiB
C
Executable File
2758 lines
86 KiB
C
Executable File
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2019 Marcin Borowicz
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#include "cat.h"
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#define CAT_CMD_STATE_NOT_MATCH (0)
|
|
#define CAT_CMD_STATE_PARTIAL_MATCH (1U)
|
|
#define CAT_CMD_STATE_FULL_MATCH (2U)
|
|
|
|
#define CAT_WRITE_STATE_BEFORE (0)
|
|
#define CAT_WRITE_STATE_MAIN_BUFFER (1U)
|
|
#define CAT_WRITE_STATE_AFTER (2U)
|
|
|
|
static inline char* get_atcmd_buf(struct cat_object *self)
|
|
{
|
|
return (char*)self->desc->buf;
|
|
}
|
|
|
|
static inline size_t get_atcmd_buf_size(struct cat_object *self)
|
|
{
|
|
return (self->desc->unsolicited_buf != NULL) ? self->desc->buf_size : self->desc->buf_size >> 1;
|
|
}
|
|
|
|
static inline char* get_unsolicited_buf(struct cat_object *self)
|
|
{
|
|
return (self->desc->unsolicited_buf != NULL) ? (char*)self->desc->unsolicited_buf : (char*)&self->desc->buf[self->desc->buf_size >> 1];
|
|
}
|
|
|
|
static inline size_t get_unsolicited_buf_size(struct cat_object *self)
|
|
{
|
|
return (self->desc->unsolicited_buf != NULL) ? self->desc->unsolicited_buf_size : self->desc->buf_size >> 1;
|
|
}
|
|
|
|
static struct cat_variable* get_var_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return (struct cat_variable*)self->var;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return (struct cat_variable*)self->unsolicited_fsm.var;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct cat_command* get_command_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return (struct cat_command*)self->cmd;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return (struct cat_command*)self->unsolicited_fsm.cmd;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline void* get_var_data(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
size_t dummy;
|
|
void *data;
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (var->data_getter == NULL) {
|
|
data = var->data;
|
|
} else {
|
|
data = var->data_getter(var, cmd->context, &dummy);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static inline size_t get_var_data_size(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
size_t data_size;
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (var->data_getter == NULL) {
|
|
data_size = var->data_size;
|
|
} else {
|
|
var->data_getter(var, cmd->context, &data_size);
|
|
}
|
|
return data_size;
|
|
}
|
|
|
|
static char to_upper(char ch)
|
|
{
|
|
return (ch >= 'a' && ch <= 'z') ? ch - ('a' - 'A') : ch;
|
|
}
|
|
|
|
static void reset_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (self->hold_state_flag == false) {
|
|
self->state = CAT_STATE_IDLE;
|
|
self->cr_flag = false;
|
|
} else {
|
|
self->state = CAT_STATE_HOLD;
|
|
}
|
|
self->cmd = NULL;
|
|
self->cmd_type = CAT_CMD_TYPE_NONE;
|
|
}
|
|
|
|
static void unsolicited_reset_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->unsolicited_fsm.cmd = NULL;
|
|
self->unsolicited_fsm.cmd_type = CAT_CMD_TYPE_NONE;
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_IDLE;
|
|
}
|
|
|
|
static cat_status is_busy(struct cat_object *self)
|
|
{
|
|
return (self->state != CAT_STATE_IDLE) ? CAT_STATUS_BUSY : CAT_STATUS_OK;
|
|
}
|
|
|
|
cat_status cat_is_busy(struct cat_object *self)
|
|
{
|
|
cat_status s;
|
|
|
|
assert(self != NULL);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
s = is_busy(self);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return s;
|
|
}
|
|
|
|
static cat_status is_hold(struct cat_object *self)
|
|
{
|
|
return (self->hold_state_flag != false) ? CAT_STATUS_HOLD : CAT_STATUS_OK;
|
|
}
|
|
|
|
cat_status cat_is_hold(struct cat_object *self)
|
|
{
|
|
cat_status s;
|
|
|
|
assert(self != NULL);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
s = is_hold(self);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return s;
|
|
}
|
|
|
|
static bool is_variables_access_possible(struct cat_object *self, const struct cat_command *cmd, cat_var_access access)
|
|
{
|
|
size_t i;
|
|
bool ok;
|
|
struct cat_variable const *var;
|
|
(void)self;
|
|
|
|
if (cmd->var == NULL)
|
|
return false;
|
|
|
|
ok = false;
|
|
for (i = 0; i < cmd->var_num; i++) {
|
|
var = &cmd->var[i];
|
|
if ((var->access == CAT_VAR_ACCESS_READ_WRITE) || (var->access == access)) {
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static bool is_unsolicited_buffer_full(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
return (self->unsolicited_fsm.unsolicited_cmd_buffer_items_count == CAT_UNSOLICITED_CMD_BUFFER_SIZE) ? true : false;
|
|
}
|
|
|
|
static bool is_unsolicited_buffer_empty(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
return (self->unsolicited_fsm.unsolicited_cmd_buffer_items_count == 0) ? true : false;
|
|
}
|
|
|
|
static cat_status pop_unsolicited_cmd(struct cat_object *self, struct cat_command const **cmd, cat_cmd_type *type)
|
|
{
|
|
struct cat_unsolicited_cmd *item;
|
|
|
|
assert(self != NULL);
|
|
assert(cmd != NULL);
|
|
assert(type != NULL);
|
|
|
|
if (is_unsolicited_buffer_empty(self) != false)
|
|
return CAT_STATUS_ERROR_BUFFER_EMPTY;
|
|
|
|
item = &self->unsolicited_fsm.unsolicited_cmd_buffer[self->unsolicited_fsm.unsolicited_cmd_buffer_head];
|
|
|
|
*cmd = item->cmd;
|
|
*type = item->type;
|
|
|
|
if (++self->unsolicited_fsm.unsolicited_cmd_buffer_head >= CAT_UNSOLICITED_CMD_BUFFER_SIZE)
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_head = 0;
|
|
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_items_count--;
|
|
|
|
return CAT_STATUS_OK;
|
|
}
|
|
|
|
static cat_status push_unsolicited_cmd(struct cat_object *self, struct cat_command const *cmd, cat_cmd_type type)
|
|
{
|
|
struct cat_unsolicited_cmd *item;
|
|
|
|
assert(self != NULL);
|
|
assert(cmd != NULL);
|
|
assert(((type == CAT_CMD_TYPE_READ) || (type == CAT_CMD_TYPE_TEST)));
|
|
|
|
if (is_unsolicited_buffer_full(self) != false)
|
|
return CAT_STATUS_ERROR_BUFFER_FULL;
|
|
|
|
item = &self->unsolicited_fsm.unsolicited_cmd_buffer[self->unsolicited_fsm.unsolicited_cmd_buffer_tail];
|
|
|
|
item->cmd = cmd;
|
|
item->type = type;
|
|
|
|
if (++self->unsolicited_fsm.unsolicited_cmd_buffer_tail >= CAT_UNSOLICITED_CMD_BUFFER_SIZE)
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_tail = 0;
|
|
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_items_count++;
|
|
|
|
return CAT_STATUS_OK;
|
|
}
|
|
|
|
cat_status cat_is_unsolicited_buffer_full(struct cat_object *self)
|
|
{
|
|
bool s;
|
|
|
|
assert(self != NULL);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
s = is_unsolicited_buffer_full(self);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return (s != false) ? CAT_STATUS_ERROR_BUFFER_FULL : CAT_STATUS_OK;
|
|
}
|
|
|
|
struct cat_command const* cat_get_processed_command(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
return get_command_by_fsm(self, fsm);
|
|
}
|
|
|
|
cat_status cat_is_unsolicited_event_buffered(struct cat_object *self, struct cat_command const *cmd, cat_cmd_type type)
|
|
{
|
|
assert(self != NULL);
|
|
assert(cmd != NULL);
|
|
assert(type < CAT_CMD_TYPE__TOTAL_NUM);
|
|
|
|
size_t num = self->unsolicited_fsm.unsolicited_cmd_buffer_items_count;
|
|
size_t index = self->unsolicited_fsm.unsolicited_cmd_buffer_head;
|
|
cat_status ret = CAT_STATUS_OK;
|
|
struct cat_unsolicited_cmd *item;
|
|
|
|
if ((self->unsolicited_fsm.cmd == cmd) && ((type == CAT_CMD_TYPE_NONE) || (self->unsolicited_fsm.cmd_type == type)))
|
|
ret = CAT_STATUS_BUSY;
|
|
|
|
while ((num > 0) && (ret == CAT_STATUS_OK)) {
|
|
item = &self->unsolicited_fsm.unsolicited_cmd_buffer[index];
|
|
if ((item->cmd == cmd) && ((type == CAT_CMD_TYPE_NONE) || (item->type == type)))
|
|
ret = CAT_STATUS_BUSY;
|
|
|
|
--num;
|
|
if (++index >= CAT_UNSOLICITED_CMD_BUFFER_SIZE)
|
|
index = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const char *get_new_line_chars(struct cat_object *self)
|
|
{
|
|
static const char *crlf = "\r\n";
|
|
return &crlf[(self->cr_flag != false) ? 0 : 1];
|
|
}
|
|
|
|
static void start_flush_io_buffer(struct cat_object *self, cat_state state_after)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->position = 0;
|
|
self->write_buf = get_new_line_chars(self);
|
|
self->write_state = CAT_WRITE_STATE_BEFORE;
|
|
self->write_state_after = state_after;
|
|
self->state = CAT_STATE_FLUSH_IO_WRITE_WAIT;
|
|
}
|
|
|
|
static void unsolicited_start_flush_io_buffer(struct cat_object *self, cat_unsolicited_state state_after)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->unsolicited_fsm.position = 0;
|
|
self->unsolicited_fsm.write_buf = get_new_line_chars(self);
|
|
self->unsolicited_fsm.write_state = CAT_WRITE_STATE_BEFORE;
|
|
self->unsolicited_fsm.write_state_after = state_after;
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE_WAIT;
|
|
}
|
|
|
|
static void start_flush_io_buffer_raw(struct cat_object *self, cat_state state_after)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->position = 0;
|
|
self->write_buf = get_atcmd_buf(self);
|
|
self->write_state = CAT_WRITE_STATE_AFTER;
|
|
self->write_state_after = state_after;
|
|
self->state = CAT_STATE_FLUSH_IO_WRITE_WAIT;
|
|
}
|
|
|
|
static void ack_error(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
strncpy(get_atcmd_buf(self), "ERROR", get_atcmd_buf_size(self));
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_RESET);
|
|
return;
|
|
}
|
|
|
|
static void ack_ok(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
strncpy(get_atcmd_buf(self), "OK", get_atcmd_buf_size(self));
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_RESET);
|
|
return;
|
|
}
|
|
|
|
static size_t get_left_buffer_space_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return get_atcmd_buf_size(self) - self->position;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return get_unsolicited_buf_size(self) - self->unsolicited_fsm.position;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char* get_current_buffer_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return &(get_atcmd_buf(self)[self->position]);
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return &(get_unsolicited_buf(self)[self->unsolicited_fsm.position]);
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void move_position_by_fsm(struct cat_object *self, size_t offset, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->position += offset;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.position += offset;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static int print_string_to_buf(struct cat_object *self, const char *str, cat_fsm_type fsm)
|
|
{
|
|
int written;
|
|
size_t len;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
len = get_left_buffer_space_by_fsm(self, fsm);
|
|
written = snprintf(get_current_buffer_by_fsm(self, fsm), len, "%s", str);
|
|
|
|
if ((written < 0) || ((size_t)written >= len))
|
|
return -1;
|
|
|
|
move_position_by_fsm(self, written, fsm);
|
|
return 0;
|
|
}
|
|
|
|
static int read_cmd_char(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (self->io->read(&self->current_char) == 0)
|
|
return 0;
|
|
|
|
if (self->state != CAT_STATE_PARSE_COMMAND_ARGS)
|
|
self->current_char = to_upper(self->current_char);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static struct cat_command const* get_command_by_index(struct cat_object *self, size_t index)
|
|
{
|
|
size_t i, j;
|
|
struct cat_command_group const *cmd_group;
|
|
|
|
assert(self != NULL);
|
|
assert(index < self->commands_num);
|
|
|
|
j = 0;
|
|
for (i = 0; i < self->desc->cmd_group_num; i++) {
|
|
cmd_group = self->desc->cmd_group[i];
|
|
|
|
if (index >= j + cmd_group->cmd_num) {
|
|
j += cmd_group->cmd_num;
|
|
continue;
|
|
}
|
|
|
|
return &cmd_group->cmd[index - j];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void unsolicited_init(struct cat_object *self)
|
|
{
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_tail = 0;
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_head = 0;
|
|
self->unsolicited_fsm.unsolicited_cmd_buffer_items_count = 0;
|
|
|
|
unsolicited_reset_state(self);
|
|
}
|
|
|
|
void cat_init(struct cat_object *self, const struct cat_descriptor *desc, const struct cat_io_interface *io, const struct cat_mutex_interface *mutex)
|
|
{
|
|
size_t i, j;
|
|
struct cat_command_group const *cmd_group;
|
|
|
|
assert(self != NULL);
|
|
assert(desc != NULL);
|
|
assert(io != NULL);
|
|
|
|
assert(desc->cmd_group != NULL);
|
|
assert(desc->cmd_group_num > 0);
|
|
|
|
self->commands_num = 0;
|
|
for (i = 0; i < desc->cmd_group_num; i++) {
|
|
cmd_group = desc->cmd_group[i];
|
|
|
|
assert(cmd_group->cmd != NULL);
|
|
assert(cmd_group->cmd_num > 0);
|
|
|
|
self->commands_num += cmd_group->cmd_num;
|
|
|
|
for (j = 0; j < cmd_group->cmd_num; j++)
|
|
assert(cmd_group->cmd[j].name != NULL);
|
|
}
|
|
|
|
assert(desc->buf != NULL);
|
|
assert(desc->buf_size * 4U >= self->commands_num);
|
|
|
|
self->desc = desc;
|
|
self->io = io;
|
|
self->mutex = mutex;
|
|
self->hold_state_flag = false;
|
|
self->hold_exit_status = 0;
|
|
|
|
reset_state(self);
|
|
|
|
unsolicited_init(self);
|
|
}
|
|
|
|
static cat_status error_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case '\n':
|
|
ack_error(self);
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static void prepare_parse_command(struct cat_object *self)
|
|
{
|
|
uint8_t val = (CAT_CMD_STATE_PARTIAL_MATCH << 0) | (CAT_CMD_STATE_PARTIAL_MATCH << 2) | (CAT_CMD_STATE_PARTIAL_MATCH << 4) |
|
|
(CAT_CMD_STATE_PARTIAL_MATCH << 6);
|
|
|
|
assert(self != NULL);
|
|
|
|
memset(get_atcmd_buf(self), val, get_atcmd_buf_size(self));
|
|
|
|
self->index = 0;
|
|
self->length = 0;
|
|
self->cmd_type = CAT_CMD_TYPE_RUN;
|
|
}
|
|
|
|
static cat_status parse_prefix(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case 'T':
|
|
prepare_parse_command(self);
|
|
self->state = CAT_STATE_PARSE_COMMAND_CHAR;
|
|
break;
|
|
case '\n':
|
|
ack_error(self);
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
default:
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static void prepare_search_command(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->index = 0;
|
|
self->partial_cntr = 0;
|
|
self->cmd = NULL;
|
|
}
|
|
|
|
static int is_valid_cmd_name_char(const char ch)
|
|
{
|
|
return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '#') || (ch == '$') || (ch == '@') || (ch == '_');
|
|
}
|
|
|
|
static int is_valid_dec_char(const char ch)
|
|
{
|
|
return (ch >= '0' && ch <= '9');
|
|
}
|
|
|
|
static int is_valid_hex_char(const char ch)
|
|
{
|
|
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F');
|
|
}
|
|
|
|
static uint8_t convert_hex_char_to_value(const char ch)
|
|
{
|
|
return ((ch >= '0') && (ch <= '9')) ? (uint8_t)(ch - '0') : (uint8_t)(ch - 'A' + 10U);
|
|
}
|
|
|
|
|
|
static void end_processing_with_error(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
ack_error(self);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_reset_state(self);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static void end_processing_with_ok(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
ack_ok(self);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_reset_state(self);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static void reset_position(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->position = 0;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.position = 0;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static int print_response_test(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (cmd->description != NULL) {
|
|
if (print_string_to_buf(self, get_new_line_chars(self), fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, cmd->description, fsm) != 0)
|
|
return -1;
|
|
}
|
|
|
|
if (cmd->test != NULL) {
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->state = CAT_STATE_TEST_LOOP;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_TEST_LOOP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static cat_status parse_command(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case '\n':
|
|
if (self->length != 0) {
|
|
prepare_search_command(self);
|
|
self->state = CAT_STATE_SEARCH_COMMAND;
|
|
break;
|
|
}
|
|
ack_ok(self);
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
case '?':
|
|
if (self->length == 0) {
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE_READ;
|
|
self->state = CAT_STATE_WAIT_READ_ACKNOWLEDGE;
|
|
break;
|
|
case '=':
|
|
if (self->length == 0) {
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE_WRITE;
|
|
prepare_search_command(self);
|
|
self->state = CAT_STATE_SEARCH_COMMAND;
|
|
break;
|
|
default:
|
|
if (is_valid_cmd_name_char(self->current_char) != 0) {
|
|
self->length++;
|
|
self->state = CAT_STATE_UPDATE_COMMAND_STATE;
|
|
break;
|
|
}
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static bool is_command_disable(struct cat_object *self, size_t index)
|
|
{
|
|
size_t i, j;
|
|
struct cat_command_group const *cmd_group;
|
|
|
|
assert(self != NULL);
|
|
assert(index < self->commands_num);
|
|
|
|
j = 0;
|
|
for (i = 0; i < self->desc->cmd_group_num; i++) {
|
|
cmd_group = self->desc->cmd_group[i];
|
|
|
|
if (index >= j + cmd_group->cmd_num) {
|
|
j += cmd_group->cmd_num;
|
|
continue;
|
|
}
|
|
|
|
if (cmd_group->disable != false)
|
|
return true;
|
|
|
|
if (cmd_group->cmd[index - j].disable != false)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static uint8_t get_cmd_state(struct cat_object *self, size_t i)
|
|
{
|
|
uint8_t s;
|
|
|
|
assert(self != NULL);
|
|
assert(i < self->commands_num);
|
|
|
|
s = get_atcmd_buf(self)[i >> 2];
|
|
s >>= (i % 4) << 1;
|
|
s &= 0x03;
|
|
|
|
if (is_command_disable(self, i) != false)
|
|
return CAT_CMD_STATE_NOT_MATCH;
|
|
|
|
return s;
|
|
}
|
|
|
|
static void set_cmd_state(struct cat_object *self, size_t i, uint8_t state)
|
|
{
|
|
uint8_t s;
|
|
size_t n;
|
|
uint8_t k;
|
|
|
|
n = i >> 2;
|
|
k = ((i % 4) << 1);
|
|
|
|
s = get_atcmd_buf(self)[n];
|
|
s &= ~(0x03 << k);
|
|
s |= (state & 0x03) << k;
|
|
get_atcmd_buf(self)[n] = s;
|
|
}
|
|
|
|
static cat_status update_command(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
struct cat_command const *cmd = get_command_by_index(self, self->index);
|
|
size_t cmd_name_len;
|
|
|
|
if (get_cmd_state(self, self->index) != CAT_CMD_STATE_NOT_MATCH) {
|
|
cmd_name_len = strlen(cmd->name);
|
|
|
|
if (self->length > cmd_name_len) {
|
|
set_cmd_state(self, self->index, CAT_CMD_STATE_NOT_MATCH);
|
|
} else if (to_upper(cmd->name[self->length - 1]) != self->current_char) {
|
|
set_cmd_state(self, self->index, CAT_CMD_STATE_NOT_MATCH);
|
|
} else if (self->length == cmd_name_len) {
|
|
set_cmd_state(self, self->index, CAT_CMD_STATE_FULL_MATCH);
|
|
}
|
|
}
|
|
|
|
if (++self->index >= self->commands_num) {
|
|
self->index = 0;
|
|
self->state = CAT_STATE_PARSE_COMMAND_CHAR;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status wait_read_acknowledge(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case '\n':
|
|
prepare_search_command(self);
|
|
self->state = CAT_STATE_SEARCH_COMMAND;
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
default:
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static void start_processing_format_test_args(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
reset_position(self, fsm);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (print_string_to_buf(self, cmd->name, fsm) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return;
|
|
}
|
|
|
|
if (print_string_to_buf(self, "=", fsm) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return;
|
|
}
|
|
|
|
if ((cmd->var != NULL) && (cmd->var_num > 0)) {
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->state = CAT_STATE_FORMAT_TEST_ARGS;
|
|
self->index = 0;
|
|
self->var = cmd->var;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_FORMAT_TEST_ARGS;
|
|
self->unsolicited_fsm.index = 0;
|
|
self->unsolicited_fsm.var = cmd->var;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (print_response_test(self, fsm) == 0)
|
|
return;
|
|
|
|
end_processing_with_error(self, fsm);
|
|
}
|
|
|
|
static cat_status wait_test_acknowledge(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case '\n':
|
|
start_processing_format_test_args(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
default:
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status search_command(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
uint8_t cmd_state = get_cmd_state(self, self->index);
|
|
|
|
if (cmd_state != CAT_CMD_STATE_NOT_MATCH) {
|
|
if (cmd_state == CAT_CMD_STATE_PARTIAL_MATCH) {
|
|
if ((self->cmd != NULL) && ((self->index + 1) == self->commands_num)) {
|
|
self->state = (self->current_char == '\n') ? CAT_STATE_COMMAND_NOT_FOUND : CAT_STATE_ERROR;
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
self->cmd = get_command_by_index(self, self->index);
|
|
self->partial_cntr++;
|
|
} else if (cmd_state == CAT_CMD_STATE_FULL_MATCH) {
|
|
self->cmd = get_command_by_index(self, self->index);
|
|
self->state = CAT_STATE_COMMAND_FOUND;
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
}
|
|
|
|
if (++self->index >= self->commands_num) {
|
|
if (self->cmd == NULL) {
|
|
self->state = (self->current_char == '\n') ? CAT_STATE_COMMAND_NOT_FOUND : CAT_STATE_ERROR;
|
|
} else {
|
|
self->state = (self->partial_cntr == 1) ? CAT_STATE_COMMAND_FOUND : CAT_STATE_COMMAND_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static void start_processing_format_read_args(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
reset_position(self, fsm);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (print_string_to_buf(self, cmd->name, fsm) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return;
|
|
}
|
|
|
|
if (print_string_to_buf(self, "=", fsm) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return;
|
|
}
|
|
|
|
if (is_variables_access_possible(self, cmd, CAT_VAR_ACCESS_READ_ONLY) != false) {
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->state = CAT_STATE_FORMAT_READ_ARGS;
|
|
self->index = 0;
|
|
self->var = cmd->var;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_FORMAT_READ_ARGS;
|
|
self->unsolicited_fsm.index = 0;
|
|
self->unsolicited_fsm.var = cmd->var;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
if (cmd->read == NULL) {
|
|
end_processing_with_error(self, fsm);
|
|
return;
|
|
}
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->state = CAT_STATE_READ_LOOP;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_READ_LOOP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static cat_status command_found(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
switch (self->cmd_type) {
|
|
case CAT_CMD_TYPE_RUN:
|
|
if (self->cmd->only_test != false) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
if (self->cmd->run == NULL) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
|
|
self->state = CAT_STATE_RUN_LOOP;
|
|
break;
|
|
case CAT_CMD_TYPE_READ:
|
|
if (self->cmd->only_test != false) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
start_processing_format_read_args(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case CAT_CMD_TYPE_WRITE:
|
|
self->length = 0;
|
|
get_atcmd_buf(self)[0] = 0;
|
|
self->state = CAT_STATE_PARSE_COMMAND_ARGS;
|
|
break;
|
|
default:
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status command_not_found(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static int parse_int_decimal(struct cat_object *self, int64_t *ret)
|
|
{
|
|
assert(self != NULL);
|
|
assert(ret != NULL);
|
|
|
|
char ch;
|
|
int64_t val = 0;
|
|
int64_t sign = 0;
|
|
int ok = 0;
|
|
|
|
while (1) {
|
|
ch = get_atcmd_buf(self)[self->position++];
|
|
|
|
if ((ok != 0) && ((ch == 0) || (ch == ','))) {
|
|
val *= sign;
|
|
*ret = val;
|
|
return (ch == ',') ? 1 : 0;
|
|
}
|
|
|
|
if (sign == 0) {
|
|
if (ch == '-') {
|
|
sign = -1;
|
|
} else if (ch == '+') {
|
|
sign = 1;
|
|
} else if (is_valid_dec_char(ch) != 0) {
|
|
sign = 1;
|
|
val = ch - '0';
|
|
ok = 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (is_valid_dec_char(ch) != 0) {
|
|
ok = 1;
|
|
val *= 10;
|
|
val += ch - '0';
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int parse_uint_decimal(struct cat_object *self, uint64_t *ret)
|
|
{
|
|
assert(self != NULL);
|
|
assert(ret != NULL);
|
|
|
|
char ch;
|
|
uint64_t val = 0;
|
|
int ok = 0;
|
|
|
|
while (1) {
|
|
ch = get_atcmd_buf(self)[self->position++];
|
|
|
|
if ((ok != 0) && ((ch == 0) || (ch == ','))) {
|
|
*ret = val;
|
|
return (ch == ',') ? 1 : 0;
|
|
}
|
|
|
|
if (is_valid_dec_char(ch) != 0) {
|
|
ok = 1;
|
|
val *= 10;
|
|
val += ch - '0';
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int parse_num_hexadecimal(struct cat_object *self, uint64_t *ret)
|
|
{
|
|
assert(self != NULL);
|
|
assert(ret != NULL);
|
|
|
|
char ch;
|
|
uint64_t val = 0;
|
|
int state = 0;
|
|
|
|
while (1) {
|
|
ch = get_atcmd_buf(self)[self->position++];
|
|
ch = to_upper(ch);
|
|
|
|
if ((state >= 3) && ((ch == 0) || (ch == ','))) {
|
|
*ret = val;
|
|
return (ch == ',') ? 1 : 0;
|
|
}
|
|
|
|
if (state == 0) {
|
|
if (ch != '0')
|
|
return -1;
|
|
state = 1;
|
|
} else if (state == 1) {
|
|
if (ch != 'X')
|
|
return -1;
|
|
state = 2;
|
|
} else if (state >= 2) {
|
|
if (is_valid_hex_char(ch) != 0) {
|
|
state = 3;
|
|
val <<= 4;
|
|
val += convert_hex_char_to_value(ch);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int parse_buffer_hexadecimal(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
char ch;
|
|
uint8_t byte = 0;
|
|
int state = 0;
|
|
size_t size = 0;
|
|
size_t var_size = get_var_data_size(self, CAT_FSM_TYPE_ATCMD);
|
|
|
|
while (1) {
|
|
ch = get_atcmd_buf(self)[self->position++];
|
|
ch = to_upper(ch);
|
|
|
|
if ((size > 0) && (state == 0) && ((ch == 0) || (ch == ','))) {
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
self->write_size = 0;
|
|
} else {
|
|
self->write_size = size;
|
|
}
|
|
return (ch == ',') ? 1 : 0;
|
|
}
|
|
|
|
if (is_valid_hex_char(ch) == 0)
|
|
return -1;
|
|
|
|
byte <<= 4;
|
|
byte += convert_hex_char_to_value(ch);
|
|
|
|
if (state != 0) {
|
|
if (var_size > 0) {
|
|
if (size >= var_size)
|
|
return -1;
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
size++;
|
|
} else {
|
|
((uint8_t*)get_var_data(self, CAT_FSM_TYPE_ATCMD))[size++] = byte;
|
|
}
|
|
} else {
|
|
size++;
|
|
}
|
|
byte = 0;
|
|
}
|
|
|
|
state = !state;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int parse_buffer_string(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
char ch;
|
|
int state = 0;
|
|
size_t size = 0;
|
|
size_t var_size = get_var_data_size(self, CAT_FSM_TYPE_ATCMD);
|
|
|
|
while (1) {
|
|
ch = get_atcmd_buf(self)[self->position++];
|
|
|
|
switch (state) {
|
|
case 0:
|
|
if (ch != '"')
|
|
return -1;
|
|
state = 1;
|
|
break;
|
|
case 1:
|
|
if (ch == 0)
|
|
return -1;
|
|
if (ch == '\\') {
|
|
state = 2;
|
|
break;
|
|
}
|
|
if (ch == '"') {
|
|
state = 3;
|
|
break;
|
|
}
|
|
if (var_size > 0) {
|
|
if (size >= var_size)
|
|
return -1;
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
size++;
|
|
} else {
|
|
((char*)get_var_data(self, CAT_FSM_TYPE_ATCMD))[size++] = ch;
|
|
}
|
|
} else {
|
|
size++;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (ch) {
|
|
case '\\':
|
|
ch = '\\';
|
|
break;
|
|
case '"':
|
|
ch = '"';
|
|
break;
|
|
case 'n':
|
|
ch = '\n';
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
if (var_size > 0) {
|
|
if (size >= var_size)
|
|
return -1;
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
size++;
|
|
} else {
|
|
((char*)get_var_data(self, CAT_FSM_TYPE_ATCMD))[size++] = ch;
|
|
}
|
|
} else {
|
|
size++;
|
|
}
|
|
state = 1;
|
|
break;
|
|
case 3:
|
|
if ((ch == 0) || (ch == ',')) {
|
|
if (var_size > 0) {
|
|
if (size >= var_size)
|
|
return -1;
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
self->write_size = 0;
|
|
} else {
|
|
((char*)get_var_data(self, CAT_FSM_TYPE_ATCMD))[size] = 0;
|
|
self->write_size = size;
|
|
}
|
|
} else {
|
|
self->write_size = 0;
|
|
}
|
|
return (ch == ',') ? 1 : 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int validate_int_range(struct cat_object *self, int64_t val)
|
|
{
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
self->write_size = 0;
|
|
return 0;
|
|
}
|
|
|
|
size_t data_size = get_var_data_size(self, CAT_FSM_TYPE_ATCMD);
|
|
uint8_t *data = get_var_data(self, CAT_FSM_TYPE_ATCMD);
|
|
|
|
switch (data_size) {
|
|
case 1:
|
|
if ((val < INT8_MIN) || (val > INT8_MAX))
|
|
return -1;
|
|
*(int8_t *)(data) = val;
|
|
break;
|
|
case 2:
|
|
if ((val < INT16_MIN) || (val > INT16_MAX))
|
|
return -1;
|
|
*(int16_t *)(data) = val;
|
|
break;
|
|
case 4:
|
|
if ((val < INT32_MIN) || (val > INT32_MAX))
|
|
return -1;
|
|
*(int32_t *)(data) = val;
|
|
break;
|
|
default:
|
|
self->write_size = 0;
|
|
return 0;
|
|
}
|
|
self->write_size = data_size;
|
|
return 0;
|
|
}
|
|
|
|
static int validate_uint_range(struct cat_object *self, uint64_t val)
|
|
{
|
|
if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
|
|
self->write_size = 0;
|
|
return 0;
|
|
}
|
|
|
|
size_t data_size = get_var_data_size(self, CAT_FSM_TYPE_ATCMD);
|
|
uint8_t *data = get_var_data(self, CAT_FSM_TYPE_ATCMD);
|
|
|
|
switch (data_size) {
|
|
case 1:
|
|
if (val > UINT8_MAX)
|
|
return -1;
|
|
*(uint8_t *)(data) = val;
|
|
break;
|
|
case 2:
|
|
if (val > UINT16_MAX)
|
|
return -1;
|
|
*(uint16_t *)(data) = val;
|
|
break;
|
|
case 4:
|
|
if (val > UINT32_MAX)
|
|
return -1;
|
|
*(uint32_t *)(data) = val;
|
|
break;
|
|
default:
|
|
self->write_size = 0;
|
|
return 0;
|
|
}
|
|
self->write_size = data_size;
|
|
return 0;
|
|
}
|
|
|
|
static cat_status parse_write_args(struct cat_object *self)
|
|
{
|
|
int64_t val;
|
|
cat_status stat;
|
|
|
|
assert(self != NULL);
|
|
|
|
switch (self->var->type) {
|
|
case CAT_VAR_INT_DEC:
|
|
stat = parse_int_decimal(self, &val);
|
|
if (stat < 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
if (validate_int_range(self, val) != 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
case CAT_VAR_UINT_DEC:
|
|
stat = parse_uint_decimal(self, (uint64_t *)&val);
|
|
if (stat < 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
if (validate_uint_range(self, val) != 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
case CAT_VAR_NUM_HEX:
|
|
stat = parse_num_hexadecimal(self, (uint64_t *)&val);
|
|
if (stat < 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
if (validate_uint_range(self, val) != 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
case CAT_VAR_BUF_HEX:
|
|
stat = parse_buffer_hexadecimal(self);
|
|
if (stat < 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
case CAT_VAR_BUF_STRING:
|
|
stat = parse_buffer_string(self);
|
|
if (stat < 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((self->var->write != NULL) || (self->var->write_ex != NULL)) {
|
|
if (self->var->write != NULL) {
|
|
if (self->var->write(self->var, self->write_size) != 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
} else {
|
|
if (self->var->write_ex(self->var, self->cmd, self->write_size) != 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((++self->index < self->cmd->var_num) && (stat > 0)) {
|
|
self->var = &self->cmd->var[self->index];
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if (stat > 0) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if ((self->cmd->need_all_vars != false) && (self->index != self->cmd->var_num)) {
|
|
ack_error(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if (self->cmd->write == NULL) {
|
|
ack_ok(self);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
self->state = CAT_STATE_WRITE_LOOP;
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static int print_format_num(struct cat_object *self, char *fmt, uint32_t val, cat_fsm_type fsm)
|
|
{
|
|
int written;
|
|
size_t len;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
len = get_left_buffer_space_by_fsm(self, fsm);
|
|
written = snprintf(get_current_buffer_by_fsm(self, fsm), len, fmt, val);
|
|
|
|
if ((written < 0) || ((size_t)written >= len))
|
|
return -1;
|
|
|
|
move_position_by_fsm(self, written, fsm);
|
|
return 0;
|
|
}
|
|
|
|
static int format_int_decimal(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
int32_t val;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
size_t data_size = get_var_data_size(self, fsm);
|
|
uint8_t *data = get_var_data(self, fsm);
|
|
|
|
switch (data_size) {
|
|
case 1:
|
|
val = *(int8_t *)data;
|
|
break;
|
|
case 2:
|
|
val = *(int16_t *)data;
|
|
break;
|
|
case 4:
|
|
val = *(int32_t *)data;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (var->access == CAT_VAR_ACCESS_WRITE_ONLY)
|
|
val = 0;
|
|
|
|
if (print_format_num(self, "%d", val, fsm) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int format_uint_decimal(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
uint32_t val;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
size_t data_size = get_var_data_size(self, fsm);
|
|
uint8_t *data = get_var_data(self, fsm);
|
|
|
|
switch (data_size) {
|
|
case 1:
|
|
val = *(uint8_t *)data;
|
|
break;
|
|
case 2:
|
|
val = *(uint16_t *)data;
|
|
break;
|
|
case 4:
|
|
val = *(uint32_t *)data;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (var->access == CAT_VAR_ACCESS_WRITE_ONLY)
|
|
val = 0;
|
|
|
|
if (print_format_num(self, "%u", val, fsm) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int format_num_hexadecimal(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
uint32_t val;
|
|
char fstr[8];
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
size_t data_size = get_var_data_size(self, fsm);
|
|
uint8_t *data = get_var_data(self, fsm);
|
|
|
|
switch (data_size) {
|
|
case 1:
|
|
val = *(uint8_t *)data;
|
|
strcpy(fstr, "0x%02X");
|
|
break;
|
|
case 2:
|
|
val = *(uint16_t *)data;
|
|
strcpy(fstr, "0x%04X");
|
|
break;
|
|
case 4:
|
|
val = *(uint32_t *)data;
|
|
strcpy(fstr, "0x%08X");
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (var->access == CAT_VAR_ACCESS_WRITE_ONLY)
|
|
val = 0;
|
|
|
|
if (print_format_num(self, fstr, val, fsm) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int format_buffer_hexadecimal(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
size_t i;
|
|
uint8_t *buf;
|
|
uint8_t val;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
size_t data_size = get_var_data_size(self, fsm);
|
|
|
|
buf = get_var_data(self, fsm);
|
|
for (i = 0; i < data_size; i++) {
|
|
if (var->access == CAT_VAR_ACCESS_WRITE_ONLY) {
|
|
val = 0;
|
|
} else {
|
|
val = buf[i];
|
|
}
|
|
|
|
if (print_format_num(self, "%02X", val, fsm) != 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int format_buffer_string(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
size_t i = 0;
|
|
char *buf;
|
|
size_t buf_size;
|
|
char ch;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
if (var->access == CAT_VAR_ACCESS_WRITE_ONLY) {
|
|
buf_size = 0;
|
|
} else {
|
|
buf_size = get_var_data_size(self, fsm);
|
|
}
|
|
|
|
if (print_string_to_buf(self, "\"", fsm) != 0)
|
|
return -1;
|
|
|
|
buf = (char*)get_var_data(self, fsm);
|
|
for (i = 0; i < buf_size; i++) {
|
|
ch = buf[i];
|
|
if (ch == 0)
|
|
break;
|
|
if (ch == '\\') {
|
|
if (print_string_to_buf(self, "\\\\", fsm) != 0)
|
|
return -1;
|
|
} else if (ch == '"') {
|
|
if (print_string_to_buf(self, "\\\"", fsm) != 0)
|
|
return -1;
|
|
} else if (ch == '\n') {
|
|
if (print_string_to_buf(self, "\\n", fsm) != 0)
|
|
return -1;
|
|
} else {
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
if (self->position >= get_atcmd_buf_size(self))
|
|
return -1;
|
|
get_atcmd_buf(self)[self->position++] = ch;
|
|
if (self->position >= get_atcmd_buf_size(self))
|
|
return -1;
|
|
get_atcmd_buf(self)[self->position] = 0;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
if (self->unsolicited_fsm.position >= get_unsolicited_buf_size(self))
|
|
return -1;
|
|
get_unsolicited_buf(self)[self->unsolicited_fsm.position++] = ch;
|
|
if (self->unsolicited_fsm.position >= get_unsolicited_buf_size(self))
|
|
return -1;
|
|
get_unsolicited_buf(self)[self->unsolicited_fsm.position] = 0;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (print_string_to_buf(self, "\"", fsm) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int format_info_type(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
char var_type[8];
|
|
char accessor[8];
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
switch (var->access) {
|
|
case CAT_VAR_ACCESS_READ_WRITE:
|
|
strcpy(accessor, "RW");
|
|
break;
|
|
case CAT_VAR_ACCESS_READ_ONLY:
|
|
strcpy(accessor, "RO");
|
|
break;
|
|
case CAT_VAR_ACCESS_WRITE_ONLY:
|
|
strcpy(accessor, "WO");
|
|
break;
|
|
default:
|
|
strcpy(accessor, "??");
|
|
break;
|
|
}
|
|
|
|
size_t data_size = get_var_data_size(self, fsm);
|
|
|
|
switch (var->type) {
|
|
case CAT_VAR_INT_DEC:
|
|
switch (data_size) {
|
|
case 1:
|
|
strcpy(var_type, "INT8");
|
|
break;
|
|
case 2:
|
|
strcpy(var_type, "INT16");
|
|
break;
|
|
case 4:
|
|
strcpy(var_type, "INT32");
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
break;
|
|
case CAT_VAR_UINT_DEC:
|
|
switch (data_size) {
|
|
case 1:
|
|
strcpy(var_type, "UINT8");
|
|
break;
|
|
case 2:
|
|
strcpy(var_type, "UINT16");
|
|
break;
|
|
case 4:
|
|
strcpy(var_type, "UINT32");
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
break;
|
|
case CAT_VAR_NUM_HEX:
|
|
switch (data_size) {
|
|
case 1:
|
|
strcpy(var_type, "HEX8");
|
|
break;
|
|
case 2:
|
|
strcpy(var_type, "HEX16");
|
|
break;
|
|
case 4:
|
|
strcpy(var_type, "HEX32");
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
break;
|
|
case CAT_VAR_BUF_HEX:
|
|
strcpy(var_type, "HEXBUF");
|
|
break;
|
|
case CAT_VAR_BUF_STRING:
|
|
strcpy(var_type, "STRING");
|
|
break;
|
|
}
|
|
|
|
if (print_string_to_buf(self, "<", fsm) != 0)
|
|
return -1;
|
|
if (var->name != NULL) {
|
|
if (print_string_to_buf(self, var->name, fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, ":", fsm) != 0)
|
|
return -1;
|
|
}
|
|
if (print_string_to_buf(self, var_type, fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, "[", fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, accessor, fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, "]", fsm) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, ">", fsm) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static cat_status next_format_var_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
if (++self->index < cmd->var_num) {
|
|
if (self->position >= get_atcmd_buf_size(self)) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
get_atcmd_buf(self)[self->position++] = ',';
|
|
self->var = &cmd->var[self->index];
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
if (++self->unsolicited_fsm.index < cmd->var_num) {
|
|
if (self->unsolicited_fsm.position >= get_unsolicited_buf_size(self)) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
get_unsolicited_buf(self)[self->unsolicited_fsm.position++] = ',';
|
|
self->unsolicited_fsm.var = &cmd->var[self->unsolicited_fsm.index];
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return CAT_STATUS_OK;
|
|
}
|
|
|
|
static cat_status format_read_args(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
cat_status stat;
|
|
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_variable *var = get_var_by_fsm(self, fsm);
|
|
|
|
if ((var->read != NULL) || (var->read_ex != NULL)) {
|
|
if (var->read != NULL) {
|
|
if (var->read(var) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
} else {
|
|
if (var->read_ex(var, get_command_by_fsm(self, fsm)) != 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (var->type) {
|
|
case CAT_VAR_INT_DEC:
|
|
stat = format_int_decimal(self, fsm);
|
|
break;
|
|
case CAT_VAR_UINT_DEC:
|
|
stat = format_uint_decimal(self, fsm);
|
|
break;
|
|
case CAT_VAR_NUM_HEX:
|
|
stat = format_num_hexadecimal(self, fsm);
|
|
break;
|
|
case CAT_VAR_BUF_HEX:
|
|
stat = format_buffer_hexadecimal(self, fsm);
|
|
break;
|
|
case CAT_VAR_BUF_STRING:
|
|
stat = format_buffer_string(self, fsm);
|
|
break;
|
|
}
|
|
|
|
if (stat < 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
stat = next_format_var_by_fsm(self, fsm);
|
|
if (stat != CAT_STATUS_OK)
|
|
return stat;
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
if (cmd->read != NULL) {
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
self->state = CAT_STATE_READ_LOOP;
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_READ_LOOP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status format_test_args(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
if (format_info_type(self, fsm) < 0) {
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
cat_status stat = next_format_var_by_fsm(self, fsm);
|
|
if (stat != CAT_STATUS_OK)
|
|
return stat;
|
|
|
|
if (print_response_test(self, fsm) == 0)
|
|
return CAT_STATUS_BUSY;
|
|
|
|
end_processing_with_error(self, fsm);
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status parse_command_args(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case '\n':
|
|
if (self->cmd->only_test != false) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
if (is_variables_access_possible(self, self->cmd, CAT_VAR_ACCESS_WRITE_ONLY) != false) {
|
|
self->state = CAT_STATE_PARSE_WRITE_ARGS;
|
|
self->position = 0;
|
|
self->index = 0;
|
|
self->var = &self->cmd->var[self->index];
|
|
break;
|
|
}
|
|
if (self->cmd->write == NULL) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
self->index = 0;
|
|
self->state = CAT_STATE_WRITE_LOOP;
|
|
break;
|
|
case '\r':
|
|
self->cr_flag = true;
|
|
break;
|
|
default:
|
|
if ((self->length == 0) && (self->current_char == '?')) {
|
|
if ((self->cmd->test != NULL) || ((self->cmd->var != NULL) && (self->cmd->var_num > 0))) {
|
|
self->cmd_type = CAT_CMD_TYPE_TEST;
|
|
self->state = CAT_STATE_WAIT_TEST_ACKNOWLEDGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self->length >= get_atcmd_buf_size(self)) {
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
get_atcmd_buf(self)[self->length++] = self->current_char;
|
|
if (self->length < get_atcmd_buf_size(self)) {
|
|
get_atcmd_buf(self)[self->length] = 0;
|
|
} else {
|
|
self->state = CAT_STATE_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
cat_status cat_trigger_unsolicited_event(struct cat_object *self, struct cat_command const *cmd, cat_cmd_type type)
|
|
{
|
|
cat_status s;
|
|
|
|
assert(self != NULL);
|
|
assert(cmd != NULL);
|
|
assert(((type == CAT_CMD_TYPE_READ) || (type == CAT_CMD_TYPE_TEST)));
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
s = push_unsolicited_cmd(self, cmd, type);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return s;
|
|
}
|
|
|
|
cat_status cat_trigger_unsolicited_read(struct cat_object *self, struct cat_command const *cmd)
|
|
{
|
|
return cat_trigger_unsolicited_event(self, cmd, CAT_CMD_TYPE_READ);
|
|
}
|
|
|
|
cat_status cat_trigger_unsolicited_test(struct cat_object *self, struct cat_command const *cmd)
|
|
{
|
|
return cat_trigger_unsolicited_event(self, cmd, CAT_CMD_TYPE_TEST);
|
|
}
|
|
|
|
static void check_unsolicited_buffers(struct cat_object *self)
|
|
{
|
|
cat_cmd_type type;
|
|
|
|
assert(self != NULL);
|
|
|
|
if (pop_unsolicited_cmd(self, &self->unsolicited_fsm.cmd, &type) != CAT_STATUS_OK)
|
|
return;
|
|
|
|
self->unsolicited_fsm.cmd_type = type;
|
|
|
|
switch (type) {
|
|
case CAT_CMD_TYPE_READ:
|
|
start_processing_format_read_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
case CAT_CMD_TYPE_TEST:
|
|
start_processing_format_test_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static cat_status process_idle_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (read_cmd_char(self) == 0)
|
|
return CAT_STATUS_OK;
|
|
|
|
switch (self->current_char) {
|
|
case 'A':
|
|
self->state = CAT_STATE_PARSE_PREFIX;
|
|
break;
|
|
case '\n':
|
|
case '\r':
|
|
break;
|
|
default:
|
|
self->state = CAT_STATE_ERROR;
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static void enable_hold_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
self->state = CAT_STATE_HOLD;
|
|
self->hold_state_flag = true;
|
|
self->hold_exit_status = 0;
|
|
}
|
|
|
|
static cat_status hold_exit(struct cat_object *self, cat_status status)
|
|
{
|
|
cat_status s;
|
|
|
|
assert(self != NULL);
|
|
|
|
if (self->hold_state_flag == false) {
|
|
s = CAT_STATUS_ERROR_NOT_HOLD;
|
|
} else {
|
|
self->hold_exit_status = (status == CAT_STATUS_OK) ? 1 : -1;
|
|
s = CAT_STATUS_OK;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static void start_print_cmd_list(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (self->commands_num == 0) {
|
|
ack_ok(self);
|
|
return;
|
|
}
|
|
|
|
self->index = 0;
|
|
self->length = 0;
|
|
self->cmd_type = CAT_CMD_TYPE_NONE;
|
|
self->state = CAT_STATE_PRINT_CMD;
|
|
}
|
|
|
|
static bool cmd_list_next_cmd(struct cat_object *self)
|
|
{
|
|
if (++self->index >= self->commands_num)
|
|
return false;
|
|
|
|
self->length = 0;
|
|
self->cmd_type = CAT_CMD_TYPE_NONE;
|
|
self->state = CAT_STATE_PRINT_CMD;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int print_current_cmd_full_name(struct cat_object *self, const char *suffix)
|
|
{
|
|
if (self->length == 0) {
|
|
if (print_string_to_buf(self, get_new_line_chars(self), CAT_FSM_TYPE_ATCMD) != 0)
|
|
return -1;
|
|
self->length = 1;
|
|
}
|
|
|
|
if (print_string_to_buf(self, "AT", CAT_FSM_TYPE_ATCMD) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, self->cmd->name, CAT_FSM_TYPE_ATCMD) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, suffix, CAT_FSM_TYPE_ATCMD) != 0)
|
|
return -1;
|
|
if (print_string_to_buf(self, get_new_line_chars(self), CAT_FSM_TYPE_ATCMD) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void print_cmd_list(struct cat_object *self)
|
|
{
|
|
self->cmd = get_command_by_index(self, self->index);
|
|
|
|
switch (self->cmd_type) {
|
|
case CAT_CMD_TYPE_NONE:
|
|
if (self->cmd->disable != false) {
|
|
if (cmd_list_next_cmd(self) == false)
|
|
ack_ok(self);
|
|
break;
|
|
}
|
|
|
|
self->cmd_type = (self->cmd->only_test != false) ? CAT_CMD_TYPE_TEST : CAT_CMD_TYPE_RUN;
|
|
break;
|
|
case CAT_CMD_TYPE_RUN:
|
|
if (self->cmd->run != NULL) {
|
|
self->position = 0;
|
|
if (print_current_cmd_full_name(self, "") != 0) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
start_flush_io_buffer_raw(self, CAT_STATE_PRINT_CMD);
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE_READ;
|
|
break;
|
|
case CAT_CMD_TYPE_READ:
|
|
if ((self->cmd->read != NULL) || (is_variables_access_possible(self, self->cmd, CAT_VAR_ACCESS_READ_ONLY) != false)) {
|
|
self->position = 0;
|
|
if (print_current_cmd_full_name(self, "?") != 0) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
start_flush_io_buffer_raw(self, CAT_STATE_PRINT_CMD);
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE_WRITE;
|
|
break;
|
|
case CAT_CMD_TYPE_WRITE:
|
|
if (self->cmd->write != NULL || (is_variables_access_possible(self, self->cmd, CAT_VAR_ACCESS_WRITE_ONLY) != false)) {
|
|
self->position = 0;
|
|
if (print_current_cmd_full_name(self, "=") != 0) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
start_flush_io_buffer_raw(self, CAT_STATE_PRINT_CMD);
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE_TEST;
|
|
break;
|
|
case CAT_CMD_TYPE_TEST:
|
|
if (self->cmd->test != NULL || ((self->cmd->var != NULL) && (self->cmd->var_num > 0))) {
|
|
self->position = 0;
|
|
if (print_current_cmd_full_name(self, "=?") != 0) {
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
start_flush_io_buffer_raw(self, CAT_STATE_PRINT_CMD);
|
|
}
|
|
self->cmd_type = CAT_CMD_TYPE__TOTAL_NUM;
|
|
break;
|
|
case CAT_CMD_TYPE__TOTAL_NUM:
|
|
if (cmd_list_next_cmd(self) == false)
|
|
ack_ok(self);
|
|
break;
|
|
default:
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static cat_status process_write_loop(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
switch (self->cmd->write(self->cmd, (uint8_t*)get_atcmd_buf(self), self->length, self->index)) {
|
|
case CAT_RETURN_STATE_OK:
|
|
case CAT_RETURN_STATE_DATA_OK:
|
|
ack_ok(self);
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_NEXT:
|
|
case CAT_RETURN_STATE_NEXT:
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD:
|
|
enable_hold_state(self);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_OK:
|
|
case CAT_RETURN_STATE_HOLD_EXIT_ERROR:
|
|
case CAT_RETURN_STATE_ERROR:
|
|
case CAT_RETURN_STATE_PRINT_CMD_LIST_OK:
|
|
default:
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status process_run_loop(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
switch (self->cmd->run(self->cmd)) {
|
|
case CAT_RETURN_STATE_OK:
|
|
case CAT_RETURN_STATE_DATA_OK:
|
|
ack_ok(self);
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_NEXT:
|
|
case CAT_RETURN_STATE_NEXT:
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD:
|
|
enable_hold_state(self);
|
|
break;
|
|
case CAT_RETURN_STATE_PRINT_CMD_LIST_OK:
|
|
start_print_cmd_list(self);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_OK:
|
|
case CAT_RETURN_STATE_HOLD_EXIT_ERROR:
|
|
case CAT_RETURN_STATE_ERROR:
|
|
default:
|
|
ack_error(self);
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_return_state call_cmd_read_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return cmd->read(cmd, (uint8_t*)get_atcmd_buf(self), &self->position, get_atcmd_buf_size(self));
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return cmd->read(cmd, (uint8_t*)get_unsolicited_buf(self), &self->unsolicited_fsm.position, get_unsolicited_buf_size(self));
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static cat_status process_read_loop(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (call_cmd_read_by_fsm(self, fsm)) {
|
|
case CAT_RETURN_STATE_OK:
|
|
end_processing_with_ok(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_OK:
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_NEXT:
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_FORMAT_READ_ARGS);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_READ_ARGS);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
break;
|
|
case CAT_RETURN_STATE_NEXT:
|
|
start_processing_format_read_args(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD:
|
|
enable_hold_state(self);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_OK:
|
|
hold_exit(self, CAT_STATUS_OK);
|
|
end_processing_with_ok(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_ERROR:
|
|
hold_exit(self, CAT_STATUS_ERROR);
|
|
end_processing_with_error(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_PRINT_CMD_LIST_OK:
|
|
case CAT_RETURN_STATE_ERROR:
|
|
default:
|
|
end_processing_with_error(self, fsm);
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_return_state call_cmd_test_by_fsm(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
struct cat_command *cmd = get_command_by_fsm(self, fsm);
|
|
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
return cmd->test(cmd, (uint8_t*)get_atcmd_buf(self), &self->position, get_atcmd_buf_size(self));
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
return cmd->test(cmd, (uint8_t*)get_unsolicited_buf(self), &self->unsolicited_fsm.position, get_unsolicited_buf_size(self));
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static cat_status process_test_loop(struct cat_object *self, cat_fsm_type fsm)
|
|
{
|
|
assert(self != NULL);
|
|
assert(fsm < CAT_FSM_TYPE__TOTAL_NUM);
|
|
|
|
switch (call_cmd_test_by_fsm(self, fsm)) {
|
|
case CAT_RETURN_STATE_OK:
|
|
end_processing_with_ok(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_OK:
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
break;
|
|
case CAT_RETURN_STATE_DATA_NEXT:
|
|
switch (fsm) {
|
|
case CAT_FSM_TYPE_ATCMD:
|
|
start_flush_io_buffer(self, CAT_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS);
|
|
break;
|
|
case CAT_FSM_TYPE_UNSOLICITED:
|
|
unsolicited_start_flush_io_buffer(self, CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
break;
|
|
case CAT_RETURN_STATE_NEXT:
|
|
start_processing_format_test_args(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD:
|
|
enable_hold_state(self);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_OK:
|
|
hold_exit(self, CAT_STATUS_OK);
|
|
end_processing_with_ok(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_HOLD_EXIT_ERROR:
|
|
hold_exit(self, CAT_STATUS_ERROR);
|
|
end_processing_with_error(self, fsm);
|
|
break;
|
|
case CAT_RETURN_STATE_PRINT_CMD_LIST_OK:
|
|
if (fsm == CAT_FSM_TYPE_ATCMD) {
|
|
start_print_cmd_list(self);
|
|
} else {
|
|
end_processing_with_ok(self, fsm);
|
|
}
|
|
break;
|
|
case CAT_RETURN_STATE_ERROR:
|
|
default:
|
|
end_processing_with_error(self, fsm);
|
|
break;
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status process_hold_state(struct cat_object *self)
|
|
{
|
|
assert(self != NULL);
|
|
|
|
if (self->hold_exit_status == 0)
|
|
return CAT_STATUS_BUSY;
|
|
|
|
self->hold_state_flag = false;
|
|
if (self->hold_exit_status < 0) {
|
|
ack_error(self);
|
|
} else {
|
|
ack_ok(self);
|
|
}
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
cat_status cat_hold_exit(struct cat_object *self, cat_status status)
|
|
{
|
|
cat_status s;
|
|
|
|
assert(self != NULL);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
s = hold_exit(self, status);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return s;
|
|
}
|
|
|
|
struct cat_command const* cat_search_command_by_name(struct cat_object *self, const char *name)
|
|
{
|
|
size_t i;
|
|
struct cat_command const *cmd;
|
|
|
|
assert(self != NULL);
|
|
assert(name != NULL);
|
|
|
|
for (i = 0; i < self->commands_num; i++) {
|
|
cmd = get_command_by_index(self, i);
|
|
if (strcmp(cmd->name, name) == 0)
|
|
return cmd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct cat_command_group const* cat_search_command_group_by_name(struct cat_object *self, const char *name)
|
|
{
|
|
size_t i;
|
|
struct cat_command_group const *cmd_group;
|
|
|
|
assert(self != NULL);
|
|
assert(name != NULL);
|
|
|
|
for (i = 0; i < self->desc->cmd_group_num; i++) {
|
|
cmd_group = self->desc->cmd_group[i];
|
|
if ((cmd_group->name != NULL) && (strcmp(cmd_group->name, name) == 0))
|
|
return cmd_group;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct cat_variable const* cat_search_variable_by_name(struct cat_object *self, struct cat_command const *cmd, const char *name)
|
|
{
|
|
size_t i;
|
|
struct cat_variable const *var;
|
|
|
|
assert(self != NULL);
|
|
assert(cmd != NULL);
|
|
assert(name != NULL);
|
|
|
|
for (i = 0; i < cmd->var_num; i++) {
|
|
var = &cmd->var[i];
|
|
if ((var->name != NULL) && (strcmp(var->name, name) == 0))
|
|
return var;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cat_status process_io_write_wait(struct cat_object *self)
|
|
{
|
|
if (self->unsolicited_fsm.state != CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE)
|
|
self->state = CAT_STATE_FLUSH_IO_WRITE;
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status unsolicited_process_io_write_wait(struct cat_object *self)
|
|
{
|
|
if (self->state != CAT_STATE_FLUSH_IO_WRITE)
|
|
self->unsolicited_fsm.state = CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE;
|
|
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status process_io_write(struct cat_object *self)
|
|
{
|
|
char ch = self->write_buf[self->position];
|
|
|
|
if (ch == '\0') {
|
|
switch (self->write_state) {
|
|
case CAT_WRITE_STATE_BEFORE:
|
|
self->position = 0;
|
|
self->write_buf = get_atcmd_buf(self);
|
|
self->write_state = CAT_WRITE_STATE_MAIN_BUFFER;
|
|
break;
|
|
case CAT_WRITE_STATE_MAIN_BUFFER:
|
|
self->position = 0;
|
|
self->write_buf = get_new_line_chars(self);
|
|
self->write_state = CAT_WRITE_STATE_AFTER;
|
|
break;
|
|
case CAT_WRITE_STATE_AFTER:
|
|
self->state = self->write_state_after;
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if (self->io->write(ch) != 1)
|
|
return CAT_STATUS_BUSY;
|
|
|
|
self->position++;
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status unsolicited_process_io_write(struct cat_object *self)
|
|
{
|
|
char ch = self->unsolicited_fsm.write_buf[self->unsolicited_fsm.position];
|
|
|
|
if (ch == '\0') {
|
|
switch (self->unsolicited_fsm.write_state) {
|
|
case CAT_WRITE_STATE_BEFORE:
|
|
self->unsolicited_fsm.position = 0;
|
|
self->unsolicited_fsm.write_buf = get_unsolicited_buf(self);
|
|
self->unsolicited_fsm.write_state = CAT_WRITE_STATE_MAIN_BUFFER;
|
|
break;
|
|
case CAT_WRITE_STATE_MAIN_BUFFER:
|
|
self->unsolicited_fsm.position = 0;
|
|
self->unsolicited_fsm.write_buf = get_new_line_chars(self);
|
|
self->unsolicited_fsm.write_state = CAT_WRITE_STATE_AFTER;
|
|
break;
|
|
case CAT_WRITE_STATE_AFTER:
|
|
self->unsolicited_fsm.state = self->unsolicited_fsm.write_state_after;
|
|
break;
|
|
}
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if (self->io->write(ch) != 1)
|
|
return CAT_STATUS_BUSY;
|
|
|
|
self->unsolicited_fsm.position++;
|
|
return CAT_STATUS_BUSY;
|
|
}
|
|
|
|
static cat_status unsolicited_events_service(struct cat_object *self)
|
|
{
|
|
cat_status s = CAT_STATUS_OK;
|
|
|
|
switch (self->unsolicited_fsm.state) {
|
|
case CAT_UNSOLICITED_STATE_IDLE:
|
|
check_unsolicited_buffers(self);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_FORMAT_READ_ARGS:
|
|
s = format_read_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_FORMAT_TEST_ARGS:
|
|
s = format_test_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_READ_LOOP:
|
|
s = process_read_loop(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_TEST_LOOP:
|
|
s = process_test_loop(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE_WAIT:
|
|
s = unsolicited_process_io_write_wait(self);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_FLUSH_IO_WRITE:
|
|
s = unsolicited_process_io_write(self);
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_AFTER_FLUSH_RESET:
|
|
unsolicited_reset_state(self);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_AFTER_FLUSH_OK:
|
|
end_processing_with_ok(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_READ_ARGS:
|
|
start_processing_format_read_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_UNSOLICITED_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS:
|
|
start_processing_format_test_args(self, CAT_FSM_TYPE_UNSOLICITED);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static bool is_unsolicited_fsm_busy(struct cat_object *self)
|
|
{
|
|
return (self->unsolicited_fsm.state != CAT_UNSOLICITED_STATE_IDLE);
|
|
}
|
|
|
|
cat_status cat_service(struct cat_object *self)
|
|
{
|
|
cat_status s;
|
|
cat_status unsolicited_stat;
|
|
|
|
assert(self != NULL);
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->lock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_LOCK;
|
|
|
|
unsolicited_stat = unsolicited_events_service(self);
|
|
|
|
switch (self->state) {
|
|
case CAT_STATE_ERROR:
|
|
s = error_state(self);
|
|
break;
|
|
case CAT_STATE_IDLE:
|
|
s = process_idle_state(self);
|
|
break;
|
|
case CAT_STATE_PARSE_PREFIX:
|
|
s = parse_prefix(self);
|
|
break;
|
|
case CAT_STATE_PARSE_COMMAND_CHAR:
|
|
s = parse_command(self);
|
|
break;
|
|
case CAT_STATE_UPDATE_COMMAND_STATE:
|
|
s = update_command(self);
|
|
break;
|
|
case CAT_STATE_WAIT_READ_ACKNOWLEDGE:
|
|
s = wait_read_acknowledge(self);
|
|
break;
|
|
case CAT_STATE_SEARCH_COMMAND:
|
|
s = search_command(self);
|
|
break;
|
|
case CAT_STATE_COMMAND_FOUND:
|
|
s = command_found(self);
|
|
break;
|
|
case CAT_STATE_COMMAND_NOT_FOUND:
|
|
s = command_not_found(self);
|
|
break;
|
|
case CAT_STATE_PARSE_COMMAND_ARGS:
|
|
s = parse_command_args(self);
|
|
break;
|
|
case CAT_STATE_PARSE_WRITE_ARGS:
|
|
s = parse_write_args(self);
|
|
break;
|
|
case CAT_STATE_FORMAT_READ_ARGS:
|
|
s = format_read_args(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case CAT_STATE_WAIT_TEST_ACKNOWLEDGE:
|
|
s = wait_test_acknowledge(self);
|
|
break;
|
|
case CAT_STATE_FORMAT_TEST_ARGS:
|
|
s = format_test_args(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case CAT_STATE_WRITE_LOOP:
|
|
s = process_write_loop(self);
|
|
break;
|
|
case CAT_STATE_READ_LOOP:
|
|
s = process_read_loop(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case CAT_STATE_TEST_LOOP:
|
|
s = process_test_loop(self, CAT_FSM_TYPE_ATCMD);
|
|
break;
|
|
case CAT_STATE_RUN_LOOP:
|
|
s = process_run_loop(self);
|
|
break;
|
|
case CAT_STATE_HOLD:
|
|
s = process_hold_state(self);
|
|
break;
|
|
case CAT_STATE_FLUSH_IO_WRITE_WAIT:
|
|
s = process_io_write_wait(self);
|
|
break;
|
|
case CAT_STATE_FLUSH_IO_WRITE:
|
|
s = process_io_write(self);
|
|
break;
|
|
case CAT_STATE_AFTER_FLUSH_RESET:
|
|
reset_state(self);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_STATE_AFTER_FLUSH_OK:
|
|
ack_ok(self);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_STATE_AFTER_FLUSH_FORMAT_READ_ARGS:
|
|
start_processing_format_read_args(self, CAT_FSM_TYPE_ATCMD);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_STATE_AFTER_FLUSH_FORMAT_TEST_ARGS:
|
|
start_processing_format_test_args(self, CAT_FSM_TYPE_ATCMD);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
case CAT_STATE_PRINT_CMD:
|
|
print_cmd_list(self);
|
|
s = CAT_STATUS_BUSY;
|
|
break;
|
|
default:
|
|
s = CAT_STATUS_ERROR_UNKNOWN_STATE;
|
|
break;
|
|
}
|
|
|
|
if ((unsolicited_stat != CAT_STATUS_OK) || (is_unsolicited_fsm_busy(self) != false)) {
|
|
s = CAT_STATUS_BUSY;
|
|
}
|
|
|
|
if ((self->mutex != NULL) && (self->mutex->unlock() != 0))
|
|
return CAT_STATUS_ERROR_MUTEX_UNLOCK;
|
|
|
|
return s;
|
|
}
|