diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 060fe0be7698..dd1cd5b8685d 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -5034,6 +5034,18 @@ West: labels: - "area: Rust" +"West project: libmctp": + status: maintained + maintainers: + - teburd + collaborators: + - nashif + - inteljiangwe1 + files: + - samples/modules/mctp/ + labels: + - "area: MCTP" + "West project: libmetal": status: odd fixes collaborators: diff --git a/include/zephyr/mctp/mctp_uart.h b/include/zephyr/mctp/mctp_uart.h new file mode 100644 index 000000000000..63eb1de9f258 --- /dev/null +++ b/include/zephyr/mctp/mctp_uart.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef ZEPHYR_MCTP_UART_H_ +#define ZEPHYR_MCTP_UART_H_ + +#include +#include +#include +#include + +/** + * @brief An MCTP binding for Zephyr's asynchronous UART interface + */ +struct mctp_binding_uart { + /** @cond INTERNAL_HIDDEN */ + struct mctp_binding binding; + const struct device *dev; + + /* receive buffers and state */ + uint8_t rx_buf[2][256]; + bool rx_buf_used[2]; + struct mctp_pktbuf *rx_pkt; + uint8_t rx_exp_len; + uint16_t rx_fcs; + uint16_t rx_fcs_calc; + enum { + STATE_WAIT_SYNC_START, + STATE_WAIT_REVISION, + STATE_WAIT_LEN, + STATE_DATA, + STATE_DATA_ESCAPED, + STATE_WAIT_FCS1, + STATE_WAIT_FCS2, + STATE_WAIT_SYNC_END, + } rx_state; + int rx_res; + + /* staging buffer for tx */ + uint8_t tx_buf[256]; + int tx_res; + + /** @endcond INTERNAL_HIDDEN */ +}; + +/** + * @brief Start the receive of a single mctp message + * + * Will read a single mctp message from the uart. + * + * @param uart MCTP UART binding + */ +void mctp_uart_start_rx(struct mctp_binding_uart *uart); + +/** @cond INTERNAL_HIDDEN */ +int mctp_uart_start(struct mctp_binding *binding); +int mctp_uart_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt); +/** @endcond INTERNAL_HIDDEN */ + +/** + * @brief Statically define a MCTP bus binding for a UART + * + * @param _name Symbolic name of the bus binding variable + * @param _dev UART device + */ +#define MCTP_UART_DT_DEFINE(_name, _dev) \ + struct mctp_binding_uart _name = { \ + .binding = \ + { \ + .name = STRINGIFY(_name), .version = 1, \ + .pkt_size = MCTP_PACKET_SIZE(MCTP_BTU), \ + .pkt_header = 0, .pkt_trailer = 0, \ + .start = mctp_uart_start, .tx = mctp_uart_tx, \ + }, \ + .dev = _dev, \ + .rx_state = STATE_WAIT_SYNC_START, \ + .rx_pkt = NULL, \ + .rx_res = 0, \ + .tx_res = 0, \ + }; + +#endif /* ZEPHYR_MCTP_UART_H_ */ diff --git a/samples/modules/mctp/mctp.rst b/samples/modules/mctp/mctp.rst new file mode 100644 index 000000000000..6bfebf7c4744 --- /dev/null +++ b/samples/modules/mctp/mctp.rst @@ -0,0 +1,5 @@ +.. zephyr:code-sample-category:: mctp + :name: MCTP + :show-listing: + + These samples demonstrate how to build communicating firmwares using MCTP in Zephyr. diff --git a/samples/modules/mctp/mctp_endpoint/CMakeLists.txt b/samples/modules/mctp/mctp_endpoint/CMakeLists.txt new file mode 100644 index 000000000000..e3f4e04676ad --- /dev/null +++ b/samples/modules/mctp/mctp_endpoint/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(mctp_endpoint) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/modules/mctp/mctp_endpoint/README.rst b/samples/modules/mctp/mctp_endpoint/README.rst new file mode 100644 index 000000000000..6f7d7b98a5f2 --- /dev/null +++ b/samples/modules/mctp/mctp_endpoint/README.rst @@ -0,0 +1,41 @@ +.. zephyr:code-sample:: mctp_endpoint_sample + :name: MCTP Endpoint Sample + + Create an MCTP endpoint over UART. + +Overview +******** +Sets up an MCTP node that listens on a UART for messages targeting a particular +MCTP endpoint id with the message "hello". Responds to this "hello" message with +"world". + +Requirements +************ +A board and SoC that provide access to a UART and a driver that implements the +UART async API. + +Wiring +****** +The listening UART pins should be wired to a board which will run the MCTP host +sample such that this board's UART tx pin connects to the host board's rx pin, +and this board's UART rx pin connects to the host board's tx pin. The boards' +grounds should also be wired together. + +Optionally a logic analyzer can be wired up and listening to the UART to inspect +the data flowing. + +Building and Running +******************** + + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/mctp/mctp_endpoint + :host-os: unix + :board: nrf52840_nrf52840dk + :goals: run + :compact: + +References +********** + +`MCTP Base Specification 2019 `_ diff --git a/samples/modules/mctp/mctp_endpoint/boards/nrf52840dk_nrf52840.overlay b/samples/modules/mctp/mctp_endpoint/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 000000000000..48c7f840dcab --- /dev/null +++ b/samples/modules/mctp/mctp_endpoint/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,3 @@ +&arduino_serial{ + status = "okay"; +}; diff --git a/samples/modules/mctp/mctp_endpoint/prj.conf b/samples/modules/mctp/mctp_endpoint/prj.conf new file mode 100644 index 000000000000..495c9ca12fa9 --- /dev/null +++ b/samples/modules/mctp/mctp_endpoint/prj.conf @@ -0,0 +1,8 @@ +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y +CONFIG_MCTP=y +CONFIG_MCTP_UART=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_MCTP_LOG_LEVEL_DBG=y +CONFIG_ISR_STACK_SIZE=4096 diff --git a/samples/modules/mctp/mctp_endpoint/src/main.c b/samples/modules/mctp/mctp_endpoint/src/main.c new file mode 100644 index 000000000000..430447ad2fe4 --- /dev/null +++ b/samples/modules/mctp/mctp_endpoint/src/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mctp_endpoint); + +#include + +static struct mctp *mctp_ctx; + +#define LOCAL_HELLO_EID 10 + +#define REMOTE_HELLO_EID 20 + +K_SEM_DEFINE(mctp_rx, 0, 1); + +static void rx_message(uint8_t eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg, + size_t len) +{ + switch (eid) { + case REMOTE_HELLO_EID: + LOG_INF("got mctp message %s for eid %d, replying to 5 with \"world\"", (char *)msg, + eid); + mctp_message_tx(mctp_ctx, LOCAL_HELLO_EID, false, 0, "world", sizeof("world")); + break; + default: + LOG_INF("Unknown endpoint %d", eid); + break; + } + + k_sem_give(&mctp_rx); +} + +MCTP_UART_DT_DEFINE(mctp_endpoint, DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); + +#define RX_BUF_SZ 128 + +int main(void) +{ + LOG_INF("MCTP Endpoint EID:%d on %s\n", LOCAL_HELLO_EID, CONFIG_BOARD_TARGET); + + mctp_set_alloc_ops(malloc, free, realloc); + mctp_ctx = mctp_init(); + __ASSERT_NO_MSG(mctp_ctx != NULL); + mctp_register_bus(mctp_ctx, &mctp_endpoint.binding, LOCAL_HELLO_EID); + mctp_set_rx_all(mctp_ctx, rx_message, NULL); + + /* MCTP poll loop */ + while (true) { + mctp_uart_start_rx(&mctp_endpoint); + k_sem_take(&mctp_rx, K_FOREVER); + } + + LOG_INF("exiting"); + return 0; +} diff --git a/samples/modules/mctp/mctp_host/CMakeLists.txt b/samples/modules/mctp/mctp_host/CMakeLists.txt new file mode 100644 index 000000000000..4b19d992eb2b --- /dev/null +++ b/samples/modules/mctp/mctp_host/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mctp_host) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/modules/mctp/mctp_host/README.rst b/samples/modules/mctp/mctp_host/README.rst new file mode 100644 index 000000000000..d80d02d2137e --- /dev/null +++ b/samples/modules/mctp/mctp_host/README.rst @@ -0,0 +1,40 @@ +.. zephyr:code-sample:: mctp_host_sample + :name: MCTP Host Sample + + Create an MCTP host over UART. + +Overview +******** +Sets up an MCTP node that sends a request on a UART targeting a particular MCTP +endpoint id with the message "hello". Expects and waits for a response to this +"hello" message containing "world". + +Requirements +************ +A board and SoC that provide access to a UART and a driver that implements the +UART async API. + +Wiring +****** +The UART pins should be wired to a board which will run the MCTP endpoint +sample such that this board's UART tx pin connects to the endpoint board's rx +pin, and this board's UART rx pin connects to the endpoint board's tx pin. The +boards' grounds should also be wired together. + +Optionally a logic analyzer can be wired up and listening to the UART to inspect +the data flowing. + +Building and Running +******************** + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/mctp/mctp_host + :host-os: unix + :board: nrf52840_nrf52840dk + :goals: run + :compact: + +References +********** + +`MCTP Base Specification 2019 `_ diff --git a/samples/modules/mctp/mctp_host/boards/nrf52840dk_nrf52840.overlay b/samples/modules/mctp/mctp_host/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 000000000000..48c7f840dcab --- /dev/null +++ b/samples/modules/mctp/mctp_host/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,3 @@ +&arduino_serial{ + status = "okay"; +}; diff --git a/samples/modules/mctp/mctp_host/prj.conf b/samples/modules/mctp/mctp_host/prj.conf new file mode 100644 index 000000000000..341787ecbd3e --- /dev/null +++ b/samples/modules/mctp/mctp_host/prj.conf @@ -0,0 +1,8 @@ +# nothing here +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y +CONFIG_MCTP=y +CONFIG_MCTP_UART=y +CONFIG_MCTP_LOG_LEVEL_DBG=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 diff --git a/samples/modules/mctp/mctp_host/src/main.c b/samples/modules/mctp/mctp_host/src/main.c new file mode 100644 index 000000000000..7fa9423c24cd --- /dev/null +++ b/samples/modules/mctp/mctp_host/src/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mctp_host); + +#define LOCAL_HELLO_EID 20 + +#define REMOTE_HELLO_EID 10 + +K_SEM_DEFINE(mctp_rx, 0, 1); + +static void rx_message(uint8_t eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg, + size_t len) +{ + LOG_INF("received message %s for endpoint %d, msg_tag %d, len %zu", (char *)msg, eid, + msg_tag, len); + k_sem_give(&mctp_rx); +} + +MCTP_UART_DT_DEFINE(mctp_host, DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); + +int main(void) +{ + int rc; + struct mctp *mctp_ctx; + + LOG_INF("MCTP Host EID:%d on %s\n", LOCAL_HELLO_EID, CONFIG_BOARD_TARGET); + + mctp_set_alloc_ops(malloc, free, realloc); + mctp_ctx = mctp_init(); + __ASSERT_NO_MSG(mctp_ctx != NULL); + mctp_register_bus(mctp_ctx, &mctp_host.binding, LOCAL_HELLO_EID); + mctp_set_rx_all(mctp_ctx, rx_message, NULL); + mctp_uart_start_rx(&mctp_host); + + /* MCTP poll loop, send "hello" and get "world" back */ + while (true) { + rc = mctp_message_tx(mctp_ctx, REMOTE_HELLO_EID, false, 0, "hello", + sizeof("hello")); + if (rc != 0) { + LOG_WRN("Failed to send message, errno %d\n", rc); + k_msleep(1000); + } else { + k_sem_take(&mctp_rx, K_MSEC(10)); + } + rc = 0; + } + + return 0; +} diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index b4ea9eb47628..61f7f3cf940d 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -46,6 +46,7 @@ add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu) add_subdirectory_ifdef(CONFIG_INPUT input) add_subdirectory_ifdef(CONFIG_JWT jwt) add_subdirectory_ifdef(CONFIG_LLEXT llext) +add_subdirectory_ifdef(CONFIG_MCTP mctp) add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem) add_subdirectory_ifdef(CONFIG_NETWORKING net) add_subdirectory_ifdef(CONFIG_PROFILING profiling) diff --git a/subsys/Kconfig b/subsys/Kconfig index 561d52634226..93880693f51b 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -26,6 +26,7 @@ source "subsys/jwt/Kconfig" source "subsys/llext/Kconfig" source "subsys/logging/Kconfig" source "subsys/lorawan/Kconfig" +source "subsys/mctp/Kconfig" source "subsys/mem_mgmt/Kconfig" source "subsys/mgmt/Kconfig" source "subsys/modbus/Kconfig" diff --git a/subsys/mctp/CMakeLists.txt b/subsys/mctp/CMakeLists.txt new file mode 100644 index 000000000000..23858ad28827 --- /dev/null +++ b/subsys/mctp/CMakeLists.txt @@ -0,0 +1,2 @@ +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_MCTP_UART mctp_uart.c) diff --git a/subsys/mctp/Kconfig b/subsys/mctp/Kconfig new file mode 100644 index 000000000000..276a2080e154 --- /dev/null +++ b/subsys/mctp/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig MCTP + bool "Management Component Transport Protocol" + help + Enable the MCTP Subsystem and Module Usage + +if MCTP + +config MCTP_UART + bool "MCTP UART Binding" + depends on UART_ASYNC_API + help + Build the MCTP UART binding to use MCTP over Zephyr's async UART + interface. + +module = MCTP +module-str = MCTP +source "subsys/logging/Kconfig.template.log_config" + +endif diff --git a/subsys/mctp/mctp_uart.c b/subsys/mctp/mctp_uart.c new file mode 100644 index 000000000000..fe60cc159c97 --- /dev/null +++ b/subsys/mctp/mctp_uart.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mctp_uart, CONFIG_MCTP_LOG_LEVEL); + +#define MCTP_UART_REVISION 0x01 +#define MCTP_UART_FRAMING_FLAG 0x7e +#define MCTP_UART_ESCAPE 0x7d + +const char *UART_EVENT_STRING[] = { + "TX Done", "TX Aborted", "RX Ready", "RX Buffer Request", "RX Buffer Released", + "RX Disabled", "RX Stopped", +}; + +const char *MCTP_STATE_STRING[] = { + "Wait: Sync Start", "Wait: Revision", "Wait: Len", "Data", + "Data: Escaped", "Wait: FCS1", "Wait: FCS2", "Wait: Sync End", +}; + +struct mctp_serial_header { + uint8_t flag; + uint8_t revision; + uint8_t len; +}; + +struct mctp_serial_trailer { + uint8_t fcs_msb; + uint8_t fcs_lsb; + uint8_t flag; +}; + +static inline struct mctp_binding_uart *binding_to_uart(struct mctp_binding *b) +{ + return (struct mctp_binding_uart *)b; +} + +static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, bool valid) +{ + struct mctp_pktbuf *pkt = uart->rx_pkt; + + + if (valid) { + __ASSERT_NO_MSG(pkt); + + mctp_bus_rx(&uart->binding, pkt); + } + + uart->rx_pkt = NULL; +} + +static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, uint8_t len) +{ + __ASSERT_NO_MSG(uart->rx_pkt == NULL); + + uart->rx_pkt = mctp_pktbuf_alloc(&uart->binding, len); + + __ASSERT_NO_MSG(uart->rx_pkt); +} + +static size_t mctp_uart_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf) +{ + uint8_t total_len; + uint8_t *p; + int i, j; + + total_len = pkt->end - pkt->mctp_hdr_off; + + p = (void *)mctp_pktbuf_hdr(pkt); + + for (i = 0, j = 0; i < total_len; i++, j++) { + uint8_t c = p[i]; + + if (c == MCTP_UART_FRAMING_FLAG || c == MCTP_UART_ESCAPE) { + if (buf) { + buf[j] = MCTP_UART_ESCAPE; + } + j++; + c ^= 0x20; + } + if (buf) { + buf[j] = c; + } + } + + return j; +} + +/* + * Each byte coming from the uart is run through this state machine which + * does the MCTP packet decoding. + * + * The actual packet and buffer being read into is owned by the binding! + */ +static void mctp_uart_consume(struct mctp_binding_uart *uart, uint8_t c) +{ + struct mctp_pktbuf *pkt = uart->rx_pkt; + bool valid = false; + + LOG_DBG("uart consume start state: %d:%s, char 0x%02x", uart->rx_state, + MCTP_STATE_STRING[uart->rx_state], c); + + __ASSERT_NO_MSG(!pkt == (uart->rx_state == STATE_WAIT_SYNC_START || + uart->rx_state == STATE_WAIT_REVISION || + uart->rx_state == STATE_WAIT_LEN)); + + switch (uart->rx_state) { + case STATE_WAIT_SYNC_START: + if (c != MCTP_UART_FRAMING_FLAG) { + LOG_DBG("lost sync, dropping packet"); + if (pkt) { + mctp_uart_finish_pkt(uart, false); + } + } else { + uart->rx_state = STATE_WAIT_REVISION; + } + break; + case STATE_WAIT_REVISION: + if (c == MCTP_UART_REVISION) { + uart->rx_state = STATE_WAIT_LEN; + uart->rx_fcs_calc = crc_16_ccitt_byte(FCS_INIT_16, c); + } else if (c == MCTP_UART_FRAMING_FLAG) { + /* Handle the case where there are bytes dropped in request, + * and the state machine is out of sync. The failed request's + * trailing footer i.e. 0x7e would be interpreted as next + * request's framing footer. So if we are in STATE_WAIT_REVISION + * and receive 0x7e byte, then continue to stay in + * STATE_WAIT_REVISION + */ + LOG_DBG("Received serial framing flag 0x%02x while waiting" + " for serial revision 0x%02x.", + c, MCTP_UART_REVISION); + } else { + LOG_DBG("invalid revision 0x%02x", c); + uart->rx_state = STATE_WAIT_SYNC_START; + } + break; + case STATE_WAIT_LEN: + if (c > uart->binding.pkt_size || c < sizeof(struct mctp_hdr)) { + LOG_DBG("invalid size %d", c); + uart->rx_state = STATE_WAIT_SYNC_START; + } else { + mctp_uart_start_pkt(uart, 0); + pkt = uart->rx_pkt; + uart->rx_exp_len = c; + uart->rx_state = STATE_DATA; + uart->rx_fcs_calc = crc_16_ccitt_byte(uart->rx_fcs_calc, c); + } + break; + case STATE_DATA: + if (c == MCTP_UART_ESCAPE) { + uart->rx_state = STATE_DATA_ESCAPED; + } else { + mctp_pktbuf_push(pkt, &c, 1); + uart->rx_fcs_calc = crc_16_ccitt_byte(uart->rx_fcs_calc, c); + if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) { + uart->rx_state = STATE_WAIT_FCS1; + } + } + break; + case STATE_DATA_ESCAPED: + c ^= 0x20; + mctp_pktbuf_push(pkt, &c, 1); + uart->rx_fcs_calc = crc_16_ccitt_byte(uart->rx_fcs_calc, c); + if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) { + uart->rx_state = STATE_WAIT_FCS1; + } else { + uart->rx_state = STATE_DATA; + } + break; + + case STATE_WAIT_FCS1: + uart->rx_fcs = c << 8; + uart->rx_state = STATE_WAIT_FCS2; + break; + case STATE_WAIT_FCS2: + uart->rx_fcs |= c; + uart->rx_state = STATE_WAIT_SYNC_END; + break; + case STATE_WAIT_SYNC_END: + if (uart->rx_fcs == uart->rx_fcs_calc) { + if (c == MCTP_UART_FRAMING_FLAG) { + valid = true; + } else { + valid = false; + LOG_DBG("missing end frame marker"); + } + } else { + valid = false; + LOG_DBG("invalid fcs : 0x%04x, expect 0x%04x", uart->rx_fcs, + uart->rx_fcs_calc); + } + + mctp_uart_finish_pkt(uart, valid); + uart->rx_state = STATE_WAIT_SYNC_START; + break; + } + + LOG_DBG("uart consume end state: %d:%s, char 0x%02x", uart->rx_state, + MCTP_STATE_STRING[uart->rx_state], c); +} + +static void mctp_uart_callback(const struct device *dev, struct uart_event *evt, void *userdata) +{ + struct mctp_binding_uart *binding = userdata; + + switch (evt->type) { + case UART_TX_DONE: + binding->tx_res = 0; + break; + case UART_TX_ABORTED: + binding->tx_res = -EIO; + break; + case UART_RX_RDY: + /* buffer being read into is ready */ + binding->rx_res = evt->data.rx.len; + /* parse the buffer */ + for (size_t i = 0; i < evt->data.rx.len; i++) { + mctp_uart_consume(binding, evt->data.rx.buf[evt->data.rx.offset + i]); + } + break; + case UART_RX_BUF_REQUEST: + for (int i = 0; i < sizeof(binding->rx_buf_used); i++) { + if (!binding->rx_buf_used[i]) { + binding->rx_buf_used[i] = true; + uart_rx_buf_rsp(dev, binding->rx_buf[i], + sizeof(binding->rx_buf[i])); + break; + } + } + break; + case UART_RX_BUF_RELEASED: + for (int i = 0; i < sizeof(binding->rx_buf_used); i++) { + if (binding->rx_buf[i] == evt->data.rx_buf.buf) { + binding->rx_buf_used[i] = false; + break; + } + } + break; + case UART_RX_STOPPED: + break; + case UART_RX_DISABLED: + break; + } +} + +void mctp_uart_start_rx(struct mctp_binding_uart *uart) +{ + int res = uart_callback_set(uart->dev, mctp_uart_callback, uart); + + __ASSERT_NO_MSG(res == 0); + uart->rx_buf_used[0] = true; + res = uart_rx_enable(uart->dev, uart->rx_buf[0], sizeof(uart->rx_buf[0]), 1000); + __ASSERT_NO_MSG(res == 0); +} + +int mctp_uart_tx(struct mctp_binding *b, struct mctp_pktbuf *pkt) +{ + struct mctp_binding_uart *uart = binding_to_uart(b); + struct mctp_serial_header *hdr; + struct mctp_serial_trailer *tlr; + uint8_t *buf; + size_t len; + uint16_t fcs; + + LOG_DBG("uart tx pkt %p", pkt); + + /* the length field in the header excludes serial framing + * and escape sequences + */ + len = mctp_pktbuf_size(pkt); + + hdr = (void *)uart->tx_buf; + hdr->flag = MCTP_UART_FRAMING_FLAG; + hdr->revision = MCTP_UART_REVISION; + hdr->len = len; + + /* Calculate fcs */ + fcs = crc_16_ccitt(FCS_INIT_16, (const uint8_t *)hdr + 1, 2); + fcs = crc_16_ccitt(fcs, (const uint8_t *)mctp_pktbuf_hdr(pkt), len); + LOG_DBG("calculated crc %d", fcs); + + buf = (void *)(hdr + 1); + + len = mctp_uart_pkt_escape(pkt, NULL); + if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(uart->tx_buf)) { + return -EMSGSIZE; + } + + mctp_uart_pkt_escape(pkt, buf); + + buf += len; + + tlr = (void *)buf; + tlr->flag = MCTP_UART_FRAMING_FLAG; + tlr->fcs_msb = fcs >> 8; + tlr->fcs_lsb = fcs & 0xff; + + len += sizeof(*hdr) + sizeof(*tlr); + + int res = uart_tx(uart->dev, (const uint8_t *)uart->tx_buf, len, SYS_FOREVER_US); + + if (res != 0) { + LOG_ERR("Failed sending data, %d", res); + return res; + } + + return uart->tx_res; +} + +int mctp_uart_start(struct mctp_binding *binding) +{ + mctp_binding_set_tx_enabled(binding, true); + return 0; +} diff --git a/west.yml b/west.yml index 2b971edc5f40..e52f61165989 100644 --- a/west.yml +++ b/west.yml @@ -278,6 +278,9 @@ manifest: - name: liblc3 revision: bb85f7dde4195bfc0fca9e9c7c2eed0f8694203c path: modules/lib/liblc3 + - name: libmctp + revision: fa152665577a124cd957756f78fb81419c095c87 + path: modules/lib/libmctp - name: libmetal revision: 3e8781aae9d7285203118c05bc01d4eb0ca565a7 path: modules/hal/libmetal