| /** |
| * Copyright (c) 2018 Linaro |
| * Copyright (c) 2020 ATL Electronics |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT inventek_eswifi_uart |
| |
| #include "eswifi_log.h" |
| LOG_MODULE_DECLARE(LOG_MODULE_NAME); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/sys/ring_buffer.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/uart.h> |
| |
| #include "eswifi.h" |
| |
| #define ESWIFI_RING_BUF_SIZE 2048 |
| |
| enum eswifi_uart_fsm { |
| ESWIFI_UART_FSM_WAIT_CR, |
| ESWIFI_UART_FSM_WAIT_LF, |
| ESWIFI_UART_FSM_WAIT_MARK, |
| ESWIFI_UART_FSM_WAIT_SPACE, |
| ESWIFI_UART_FSM_END, |
| }; |
| |
| struct eswifi_uart_data { |
| const struct device *dev; |
| enum eswifi_uart_fsm fsm; |
| size_t rx_count; |
| size_t rx_buf_size; |
| char *rx_buf; |
| |
| /* RX Ring Buf */ |
| uint8_t iface_rb_buf[ESWIFI_RING_BUF_SIZE]; |
| struct ring_buf rx_rb; |
| }; |
| |
| static struct eswifi_uart_data eswifi_uart0; /* Static instance */ |
| |
| static void eswifi_iface_uart_flush(struct eswifi_uart_data *uart) |
| { |
| uint8_t c; |
| |
| while (uart_fifo_read(uart->dev, &c, 1) > 0) { |
| continue; |
| } |
| } |
| |
| static void eswifi_iface_uart_isr(const struct device *uart_dev, |
| void *user_data) |
| { |
| struct eswifi_uart_data *uart = &eswifi_uart0; /* Static instance */ |
| int rx = 0; |
| uint8_t *dst; |
| uint32_t partial_size = 0; |
| uint32_t total_size = 0; |
| |
| ARG_UNUSED(user_data); |
| |
| while (uart_irq_update(uart->dev) && |
| uart_irq_rx_ready(uart->dev)) { |
| if (!partial_size) { |
| partial_size = ring_buf_put_claim(&uart->rx_rb, &dst, |
| UINT32_MAX); |
| } |
| if (!partial_size) { |
| LOG_ERR("Rx buffer doesn't have enough space"); |
| eswifi_iface_uart_flush(uart); |
| break; |
| } |
| |
| rx = uart_fifo_read(uart->dev, dst, partial_size); |
| if (rx <= 0) { |
| continue; |
| } |
| |
| dst += rx; |
| total_size += rx; |
| partial_size -= rx; |
| } |
| |
| ring_buf_put_finish(&uart->rx_rb, total_size); |
| } |
| |
| static char get_fsm_char(int fsm) |
| { |
| switch (fsm) { |
| case ESWIFI_UART_FSM_WAIT_CR: |
| return('C'); |
| case ESWIFI_UART_FSM_WAIT_LF: |
| return('L'); |
| case ESWIFI_UART_FSM_WAIT_MARK: |
| return('M'); |
| case ESWIFI_UART_FSM_WAIT_SPACE: |
| return('S'); |
| case ESWIFI_UART_FSM_END: |
| return('E'); |
| } |
| |
| return('?'); |
| } |
| |
| static int eswifi_uart_get_resp(struct eswifi_uart_data *uart) |
| { |
| uint8_t c; |
| |
| while (ring_buf_get(&uart->rx_rb, &c, 1) > 0) { |
| LOG_DBG("FSM: %c, RX: 0x%02x : %c", |
| get_fsm_char(uart->fsm), c, c); |
| |
| if (uart->rx_buf_size > 0) { |
| uart->rx_buf[uart->rx_count++] = c; |
| |
| if (uart->rx_count == uart->rx_buf_size) { |
| return -ENOMEM; |
| } |
| } |
| |
| switch (uart->fsm) { |
| case ESWIFI_UART_FSM_WAIT_CR: |
| if (c == '\r') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_LF; |
| } |
| break; |
| case ESWIFI_UART_FSM_WAIT_LF: |
| if (c == '\n') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_MARK; |
| } else if (c != '\r') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_CR; |
| } |
| break; |
| case ESWIFI_UART_FSM_WAIT_MARK: |
| if (c == '>') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_SPACE; |
| } else if (c == '\r') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_LF; |
| } else { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_CR; |
| } |
| break; |
| case ESWIFI_UART_FSM_WAIT_SPACE: |
| if (c == ' ') { |
| uart->fsm = ESWIFI_UART_FSM_END; |
| } else if (c == '\r') { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_LF; |
| } else { |
| uart->fsm = ESWIFI_UART_FSM_WAIT_CR; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int eswifi_uart_wait_prompt(struct eswifi_uart_data *uart) |
| { |
| unsigned int max_retries = 60 * 1000; /* 1 minute */ |
| int err; |
| |
| while (--max_retries) { |
| err = eswifi_uart_get_resp(uart); |
| if (err) { |
| LOG_DBG("Err: 0x%08x - %d", err, err); |
| return err; |
| } |
| |
| if (uart->fsm == ESWIFI_UART_FSM_END) { |
| LOG_DBG("Success!"); |
| return uart->rx_count; |
| } |
| |
| /* allow other threads to be scheduled */ |
| k_sleep(K_MSEC(1)); |
| } |
| |
| LOG_DBG("Timeout"); |
| return -ETIMEDOUT; |
| } |
| |
| static int eswifi_uart_request(struct eswifi_dev *eswifi, char *cmd, |
| size_t clen, char *rsp, size_t rlen) |
| { |
| struct eswifi_uart_data *uart = eswifi->bus_data; |
| int count; |
| int err; |
| |
| LOG_DBG("cmd=%p (%u byte), rsp=%p (%u byte)", cmd, clen, rsp, rlen); |
| |
| /* Send CMD */ |
| for (count = 0; count < clen; count++) { |
| uart_poll_out(uart->dev, cmd[count]); |
| } |
| |
| uart->fsm = ESWIFI_UART_FSM_WAIT_CR; |
| uart->rx_count = 0; |
| uart->rx_buf = rsp; |
| uart->rx_buf_size = rlen; |
| |
| err = eswifi_uart_wait_prompt(uart); |
| |
| if (err > 0) { |
| LOG_HEXDUMP_DBG(uart->rx_buf, uart->rx_count, "Stream"); |
| } |
| |
| return err; |
| } |
| |
| int eswifi_uart_init(struct eswifi_dev *eswifi) |
| { |
| struct eswifi_uart_data *uart = &eswifi_uart0; /* Static instance */ |
| |
| uart->dev = DEVICE_DT_GET(DT_INST_BUS(0)); |
| if (!device_is_ready(uart->dev)) { |
| LOG_ERR("Bus device is not ready"); |
| return -ENODEV; |
| } |
| |
| eswifi->bus_data = uart; |
| |
| uart_irq_rx_disable(uart->dev); |
| uart_irq_tx_disable(uart->dev); |
| eswifi_iface_uart_flush(uart); |
| uart_irq_callback_set(uart->dev, eswifi_iface_uart_isr); |
| uart_irq_rx_enable(uart->dev); |
| |
| ring_buf_init(&uart->rx_rb, sizeof(uart->iface_rb_buf), |
| uart->iface_rb_buf); |
| |
| LOG_DBG("success"); |
| |
| return 0; |
| } |
| |
| static struct eswifi_bus_ops eswifi_bus_ops_uart = { |
| .init = eswifi_uart_init, |
| .request = eswifi_uart_request, |
| }; |
| |
| struct eswifi_bus_ops *eswifi_get_bus(void) |
| { |
| return &eswifi_bus_ops_uart; |
| } |