|  | /* | 
|  | * Copyright Runtime.io 2018. All rights reserved. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @file | 
|  | * @brief A driver for sending and receiving mcumgr packets over UART. | 
|  | */ | 
|  |  | 
|  | #include <string.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/drivers/uart.h> | 
|  | #include <zephyr/mgmt/mcumgr/transport/serial.h> | 
|  | #include <zephyr/drivers/console/uart_mcumgr.h> | 
|  |  | 
|  | static const struct device *const uart_mcumgr_dev = | 
|  | DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr)); | 
|  |  | 
|  | /** Callback to execute when a valid fragment has been received. */ | 
|  | static uart_mcumgr_recv_fn *uart_mgumgr_recv_cb; | 
|  |  | 
|  | /** Contains the fragment currently being received. */ | 
|  | static struct uart_mcumgr_rx_buf *uart_mcumgr_cur_buf; | 
|  |  | 
|  | /** | 
|  | * Whether the line currently being read should be ignored.  This is true if | 
|  | * the line is too long or if there is no buffer available to hold it. | 
|  | */ | 
|  | static bool uart_mcumgr_ignoring; | 
|  |  | 
|  | /** Contains buffers to hold incoming request fragments. */ | 
|  | K_MEM_SLAB_DEFINE(uart_mcumgr_slab, sizeof(struct uart_mcumgr_rx_buf), | 
|  | CONFIG_UART_MCUMGR_RX_BUF_COUNT, 1); | 
|  |  | 
|  | #if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC) | 
|  | uint8_t async_buffer[CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUFS] | 
|  | [CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUF_SIZE]; | 
|  | static int async_current; | 
|  | #endif | 
|  |  | 
|  | static struct uart_mcumgr_rx_buf *uart_mcumgr_alloc_rx_buf(void) | 
|  | { | 
|  | struct uart_mcumgr_rx_buf *rx_buf; | 
|  | void *block; | 
|  | int rc; | 
|  |  | 
|  | rc = k_mem_slab_alloc(&uart_mcumgr_slab, &block, K_NO_WAIT); | 
|  | if (rc != 0) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | rx_buf = block; | 
|  | rx_buf->length = 0; | 
|  | return rx_buf; | 
|  | } | 
|  |  | 
|  | void uart_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf *rx_buf) | 
|  | { | 
|  | void *block; | 
|  |  | 
|  | block = rx_buf; | 
|  | k_mem_slab_free(&uart_mcumgr_slab, block); | 
|  | } | 
|  |  | 
|  | #if !defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC) | 
|  | /** | 
|  | * Reads a chunk of received data from the UART. | 
|  | */ | 
|  | static int uart_mcumgr_read_chunk(void *buf, int capacity) | 
|  | { | 
|  | if (!uart_irq_rx_ready(uart_mcumgr_dev)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return uart_fifo_read(uart_mcumgr_dev, buf, capacity); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /** | 
|  | * Processes a single incoming byte. | 
|  | */ | 
|  | static struct uart_mcumgr_rx_buf *uart_mcumgr_rx_byte(uint8_t byte) | 
|  | { | 
|  | struct uart_mcumgr_rx_buf *rx_buf; | 
|  |  | 
|  | if (!uart_mcumgr_ignoring) { | 
|  | if (uart_mcumgr_cur_buf == NULL) { | 
|  | uart_mcumgr_cur_buf = uart_mcumgr_alloc_rx_buf(); | 
|  | if (uart_mcumgr_cur_buf == NULL) { | 
|  | /* Insufficient buffers; drop this fragment. */ | 
|  | uart_mcumgr_ignoring = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | rx_buf = uart_mcumgr_cur_buf; | 
|  | if (!uart_mcumgr_ignoring) { | 
|  | if (rx_buf->length >= sizeof(rx_buf->data)) { | 
|  | /* Line too long; drop this fragment. */ | 
|  | uart_mcumgr_free_rx_buf(uart_mcumgr_cur_buf); | 
|  | uart_mcumgr_cur_buf = NULL; | 
|  | uart_mcumgr_ignoring = true; | 
|  | } else { | 
|  | rx_buf->data[rx_buf->length++] = byte; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (byte == '\n') { | 
|  | /* Fragment complete. */ | 
|  | if (uart_mcumgr_ignoring) { | 
|  | uart_mcumgr_ignoring = false; | 
|  | } else { | 
|  | uart_mcumgr_cur_buf = NULL; | 
|  | return rx_buf; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC) | 
|  | static void uart_mcumgr_async(const struct device *dev, struct uart_event *evt, void *user_data) | 
|  | { | 
|  | struct uart_mcumgr_rx_buf *rx_buf; | 
|  | uint8_t *p; | 
|  | int len; | 
|  |  | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | switch (evt->type) { | 
|  | case UART_TX_DONE: | 
|  | case UART_TX_ABORTED: | 
|  | break; | 
|  | case UART_RX_RDY: | 
|  | len = evt->data.rx.len; | 
|  | p = &evt->data.rx.buf[evt->data.rx.offset]; | 
|  |  | 
|  | for (int i = 0; i < len; i++) { | 
|  | rx_buf = uart_mcumgr_rx_byte(p[i]); | 
|  | if (rx_buf != NULL) { | 
|  | uart_mgumgr_recv_cb(rx_buf); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case UART_RX_DISABLED: | 
|  | async_current = 0; | 
|  | break; | 
|  | case UART_RX_BUF_REQUEST: | 
|  | /* | 
|  | * Note that when buffer gets filled, the UART_RX_BUF_RELEASED will be reported, | 
|  | * aside to UART_RX_RDY.  The UART_RX_BUF_RELEASED is not processed because | 
|  | * it has been assumed that the mcumgr will be able to consume bytes faster | 
|  | * than UART will receive them and, since there is nothing to release, only | 
|  | * UART_RX_BUF_REQUEST is processed. | 
|  | */ | 
|  | ++async_current; | 
|  | async_current %= CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUFS; | 
|  | uart_rx_buf_rsp(dev, async_buffer[async_current], | 
|  | sizeof(async_buffer[async_current])); | 
|  | break; | 
|  | case UART_RX_BUF_RELEASED: | 
|  | case UART_RX_STOPPED: | 
|  | break; | 
|  | } | 
|  | } | 
|  | #else | 
|  | /** | 
|  | * ISR that is called when UART bytes are received. | 
|  | */ | 
|  | static void uart_mcumgr_isr(const struct device *unused, void *user_data) | 
|  | { | 
|  | struct uart_mcumgr_rx_buf *rx_buf; | 
|  | uint8_t buf[32]; | 
|  | int chunk_len; | 
|  | int i; | 
|  |  | 
|  | ARG_UNUSED(unused); | 
|  | ARG_UNUSED(user_data); | 
|  |  | 
|  | while (uart_irq_update(uart_mcumgr_dev) && | 
|  | uart_irq_is_pending(uart_mcumgr_dev)) { | 
|  |  | 
|  | chunk_len = uart_mcumgr_read_chunk(buf, sizeof(buf)); | 
|  | if (chunk_len == 0) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < chunk_len; i++) { | 
|  | rx_buf = uart_mcumgr_rx_byte(buf[i]); | 
|  | if (rx_buf != NULL) { | 
|  | uart_mgumgr_recv_cb(rx_buf); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /** | 
|  | * Sends raw data over the UART. | 
|  | */ | 
|  | static int uart_mcumgr_send_raw(const void *data, int len) | 
|  | { | 
|  | const uint8_t *u8p; | 
|  |  | 
|  | u8p = data; | 
|  | while (len--) { | 
|  | uart_poll_out(uart_mcumgr_dev, *u8p++); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int uart_mcumgr_send(const uint8_t *data, int len) | 
|  | { | 
|  | return mcumgr_serial_tx_pkt(data, len, uart_mcumgr_send_raw); | 
|  | } | 
|  |  | 
|  |  | 
|  | #if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC) | 
|  | static void uart_mcumgr_setup(const struct device *uart) | 
|  | { | 
|  | uart_callback_set(uart, uart_mcumgr_async, NULL); | 
|  |  | 
|  | uart_rx_enable(uart, async_buffer[0], sizeof(async_buffer[0]), 0); | 
|  | } | 
|  | #else | 
|  | static void uart_mcumgr_setup(const struct device *uart) | 
|  | { | 
|  | uart_irq_rx_disable(uart); | 
|  | uart_irq_tx_disable(uart); | 
|  |  | 
|  | uart_irq_callback_set(uart, uart_mcumgr_isr); | 
|  |  | 
|  | uart_irq_rx_enable(uart); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void uart_mcumgr_register(uart_mcumgr_recv_fn *cb) | 
|  | { | 
|  | uart_mgumgr_recv_cb = cb; | 
|  |  | 
|  | if (device_is_ready(uart_mcumgr_dev)) { | 
|  | uart_mcumgr_setup(uart_mcumgr_dev); | 
|  | } | 
|  | } |