diff --git a/include/zephyr/mctp/mctp_uart.h b/include/zephyr/mctp/mctp_uart.h index 58c02aeea89e9e9..63b03d6e3ff76a5 100644 --- a/include/zephyr/mctp/mctp_uart.h +++ b/include/zephyr/mctp/mctp_uart.h @@ -8,37 +8,80 @@ #ifndef ZEPHYR_MCTP_UART_H_ #define ZEPHYR_MCTP_UART_H_ +#include +#include #include /** - * Provides a MCTP bus binding for Zephyr's UART interface + * @brief An MCTP binding for Zephyr's polling UART interface */ struct mctp_uart { - struct mctp_bus_binding binding; - const struct device *uart; + /** @cond INTERNAL_HIDDEN */ + struct mctp_binding binding; + const struct device *dev; + + /* receive buffer and state */ + uint8_t rxbuf[1024]; + 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; + + /* staging buffer for tx */ + uint8_t txbuf[256]; + /** @endcond INTERNAL_HIDDEN */ }; +/** + * @brief Poll the UART for a single character + * + * Polls the underlying UART peripheral for a single byte of information. If a byte + * is available it is fed into an internal state machine that decodes an MCTP packet. + * + * When a full packet becomes available, mctp_bus_rx is called internally eventually leading + * back to the assigned binding's rx_control function pointer callback. + * + * @param uart MCTP UART binding + * @param pkt MCTP packet to transmit + * + * @retval 0 success + * @retval -errno Error + */ +int mctp_uart_poll(struct mctpt_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 */ /** - * Statically define a MCTP bus binding for a UART + * @brief Statically define a MCTP bus binding for a UART * * @param name Symbolic name of the bus binding variable * @param dt_node Devicetree node */ -#define MCTP_UART_DT_DEFINE(name, dt_node) \ - struct mctp_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, \ - }, \ - .uart = DEVICE_DT_GET(dt_node), \ +#define MCTP_UART_DT_DEFINE(name, dt_node) \ + struct mctp_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 = DEVICE_DT_GET(dt_node), \ + .rx_state = STATE_WAIT_SYNC_START, \ + .rx_pkg = NULL, \ }; #endif /* ZEPHYR_MCTP_UART_H_ */ diff --git a/subsys/mctp/mctp_uart.c b/subsys/mctp/mctp_uart.c index 0fc483251c80a84..edefa2bfd245e44 100644 --- a/subsys/mctp/mctp_uart.c +++ b/subsys/mctp/mctp_uart.c @@ -5,5 +5,245 @@ * */ +#include #include #include + +#include +LOG_MODULE_REGISTER(mctp_uart, CONFIG_MCTP_LOG_LEVEL); + +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; + + __ASSERT_NO_MSG(pkt); + + if (valid) { + mctp_bus_rx(&uart->binding, pkt); + } + + uart->rx_pkt = NULL; +} + +static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, + uint8_t len) +{ + uart->rx_pkt = mctp_pktbuf_alloc(&uart->binding, len); +} + +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 == 0x7e || c == 0x7d) { + if (buf) + buf[j] = 0x7d; + 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("state: %d, char 0x%02x", 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_SERIAL_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_SERIAL_REVISION) { + uart->rx_state = STATE_WAIT_LEN; + uart->rx_fcs_calc = crc_16_ccitt_byte(FCS_INIT_16, c); + } else if (c == MCTP_SERIAL_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 contine to stay in + * STATE_WAIT_REVISION + */ + LOG_DEBUG( + "Received serial framing flag 0x%02x while waiting" + " for serial revision 0x%02x.", + c, MCTP_SERIAL_REVISION); + } else { + LOG_DEBUG("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_DEBUG("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_SERIAL_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_SERIAL_FRAMING_FLAG) { + valid = true; + } else { + valid = false; + LOG_DEBUG("missing end frame marker"); + } + } else { + valid = false; + LOG_DEBUG("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_DEBUG(" -> state: %d", uart->rx_state); +} + +int mctp_uart_poll(struct mctp_binding *binding) +{ + int res; + char in; + struct mctp_uart_binding *uart = binding_to_uart(binding); + + + res = uart_poll_in(uart->dev, &in); + + if (res != 0) { + LOG_ERR("failed polling uart, %d", res); + return res; + } + + return mctp_uart_consume(uart, in); +} + +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; + + /* the length field in the header excludes serial framing + * and escape sequences + */ + len = mctp_pktbuf_size(pkt); + + hdr = (void *)uart->txbuf; + hdr->flag = MCTP_SERIAL_FRAMING_FLAG; + hdr->revision = MCTP_SERIAL_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); + + buf = (void *)(hdr + 1); + + len = mctp_serial_pkt_escape(pkt, NULL); + if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(uart->txbuf)) + return -EMSGSIZE; + + mctp_serial_pkt_escape(pkt, buf); + + buf += len; + + tlr = (void *)buf; + tlr->flag = MCTP_SERIAL_FRAMING_FLAG; + tlr->fcs_msb = fcs >> 8; + tlr->fcs_lsb = fcs & 0xff; + + len += sizeof(*hdr) + sizeof(*tlr); + + /* TODO fix this to use uart_poll_out */ + /* return mctp_write_all(uart->tx_fn, uart->tx_fn_data, &uart->txbuf[0], len); */ + return 0; +} + +int mctp_uart_start(struct mctp_binding *binding) +{ + return 0; +}