Skip to content

Commit

Permalink
WIP: uart binding
Browse files Browse the repository at this point in the history
Signed-off-by: Tom Burdick <[email protected]>
  • Loading branch information
teburd committed Sep 6, 2024
1 parent c40de15 commit e1f9cbd
Show file tree
Hide file tree
Showing 2 changed files with 299 additions and 16 deletions.
75 changes: 59 additions & 16 deletions include/zephyr/mctp/mctp_uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,80 @@
#ifndef ZEPHYR_MCTP_UART_H_
#define ZEPHYR_MCTP_UART_H_

#include <stdint.h>
#include <zephyr/device.h>
#include <libmctp.h>

/**
* 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_ */
240 changes: 240 additions & 0 deletions subsys/mctp/mctp_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,245 @@
*
*/

#include <zephyr/sys/__assert.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>

#include <zephyr/logging/log.h>
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)
{

Check notice on line 22 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:22 -static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, - bool valid) +static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, bool valid)

Check notice on line 22 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:22 -static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, - bool valid) +static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, bool valid)

Check notice on line 22 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:22 -static void mctp_uart_finish_pkt(struct mctp_binding_uart *uart, - bool valid) +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)
{

Check notice on line 36 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:36 -static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, - uint8_t len) +static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, uint8_t len)

Check notice on line 36 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:36 -static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, - uint8_t len) +static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, uint8_t len)

Check notice on line 36 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:36 -static void mctp_uart_start_pkt(struct mctp_binding_uart *uart, - uint8_t len) +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++;

Check notice on line 56 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:56 - if (buf) + if (buf) { buf[j] = 0x7d; + }

Check notice on line 56 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:56 - if (buf) + if (buf) { buf[j] = 0x7d; + }

Check notice on line 56 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:56 - if (buf) + if (buf) { buf[j] = 0x7d; + }
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 {

Check notice on line 90 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:90 - uart->rx_state == STATE_WAIT_REVISION || - uart->rx_state == STATE_WAIT_LEN)); + 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) + if (pkt) { mctp_uart_finish_pkt(uart, false); + }

Check notice on line 90 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:90 - uart->rx_state == STATE_WAIT_REVISION || - uart->rx_state == STATE_WAIT_LEN)); + 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) + if (pkt) { mctp_uart_finish_pkt(uart, false); + }

Check notice on line 90 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:90 - uart->rx_state == STATE_WAIT_REVISION || - uart->rx_state == STATE_WAIT_LEN)); + 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) + if (pkt) { mctp_uart_finish_pkt(uart, false); + }
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);

Check notice on line 119 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:119 - LOG_DEBUG( - "Received serial framing flag 0x%02x while waiting" - " for serial revision 0x%02x.", - c, MCTP_SERIAL_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)) { + if (c > uart->binding.pkt_size || c < sizeof(struct mctp_hdr)) {

Check notice on line 119 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:119 - LOG_DEBUG( - "Received serial framing flag 0x%02x while waiting" - " for serial revision 0x%02x.", - c, MCTP_SERIAL_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)) { + if (c > uart->binding.pkt_size || c < sizeof(struct mctp_hdr)) {

Check notice on line 119 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:119 - LOG_DEBUG( - "Received serial framing flag 0x%02x while waiting" - " for serial revision 0x%02x.", - c, MCTP_SERIAL_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)) { + if (c > uart->binding.pkt_size || c < sizeof(struct mctp_hdr)) {
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);
}

Check notice on line 128 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:128 - uart->rx_fcs_calc = - crc_16_ccitt_byte(uart->rx_fcs_calc, c); + uart->rx_fcs_calc = crc_16_ccitt_byte(uart->rx_fcs_calc, c);

Check notice on line 128 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:128 - uart->rx_fcs_calc = - crc_16_ccitt_byte(uart->rx_fcs_calc, c); + uart->rx_fcs_calc = crc_16_ccitt_byte(uart->rx_fcs_calc, c);

Check notice on line 128 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:128 - uart->rx_fcs_calc = - crc_16_ccitt_byte(uart->rx_fcs_calc, c); + 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;
}

Check notice on line 140 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:140 - 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_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; + }

Check notice on line 140 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:140 - 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_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; + }

Check notice on line 140 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:140 - 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_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;

Check notice on line 151 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:151 - if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) + if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) { uart->rx_state = STATE_WAIT_FCS1; - else + } else { uart->rx_state = STATE_DATA; + }

Check notice on line 151 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:151 - if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) + if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) { uart->rx_state = STATE_WAIT_FCS1; - else + } else { uart->rx_state = STATE_DATA; + }

Check notice on line 151 in subsys/mctp/mctp_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/mctp/mctp_uart.c:151 - if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) + if (pkt->end - pkt->mctp_hdr_off == uart->rx_exp_len) { uart->rx_state = STATE_WAIT_FCS1; - else + } else { uart->rx_state = STATE_DATA; + }

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;
}

0 comments on commit e1f9cbd

Please sign in to comment.