Initial MCTP core code
Just a skeleton of the MCTP library at present.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ee1eee8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+
+CC = gcc
+AR = ar
+CFLAGS = -Wall -Wextra -Werror -ggdb
+CPPFLAGS = -DMCTP_LOG_STDERR -DMCTP_FILEIO -I$(LIBMCTP_DIR)
+
+LIBMCTP_DIR=./
+
+include Makefile.inc
+
+all: $(LIBMCTP)
+
+libmctp.a:
+ $(AR) rcsTPD $@ $^
+
+tests/%: tests/%.o libmctp.a
+ $(LINK.o) -o $@ $^
+
+clean:
+ rm -f $(LIBMCTP)
+ rm -f $(LIBMCTP_OBJS)
+ rm -f tests/*.o
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..99edaac
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,6 @@
+LIBMCTP_DIR ?= libmctp/
+LIBMCTP_OBJS = core.o alloc.o serial.o
+
+LIBMCTP = $(LIBMCTP_DIR)libmctp.a
+
+$(LIBMCTP): $(LIBMCTP_OBJS:%=$(LIBMCTP_DIR)%)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d58f51b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+libmctp: Implementation of MCTP (DTMF DSP0236)
+==============================================
+
+This library is intended to be a portable implementation of the Management
+Component Transport Protocol (MCTP), as defined by DMTF standard "DSP0236",
+plus transport binding specifications.
+
+Currently, the library is is only at prototyping stage. Interfaces will likely
+change, and are missing lots of components of the standard.
+
+Core API
+--------
+
+To initialise the MCTP stack with a single hardware bus:
+
+ * `mctp = mctp_init()`: Initialise the MCTP core
+ * `binding = mctp_<binding>_init()`: Initialise a hardware binding
+ * `mctp_register_bus(mctp, binding, eid)`: Register the hardware binding with
+ the core, using a predefined EID
+
+Then, register a function call to be invoked when a message is received:
+
+ * `mctp_set_rx_all(mctp, function)`: Provide a callback to be invoked when a
+ MCTP message is received
+
+Or transmit a message:
+
+ * `mctp_message_tx(mctp, message, len)`: Transmit a MCTP message
+
+The binding may require you to notify it to receive packets. For example,
+for the serial binding, the `mctp_serial_read()` function should be invoked
+when the file-descriptor for the serial device has data available.
+
+Environment configuration
+-------------------------
+
+This library is intended to be portable to be used in a range of environments,
+but the main targets are:
+
+ - Linux userspace, typically for BMC use-cases
+ - Low-level firmware environments
+
+For the latter, we need to support customisation of the functions that libmctp
+uses (for example, POSIX file IO is not available).
+
+In order to support these, we have a couple of compile-time definitions:
+
+ - `MCTP_FILEIO`: define if POSIX file io is available, allowing the
+ serial hardware binding to access char devices for IO.
+
+ - `MCTP_LOG_`: allows selection of a logging backend. Currently available
+ are:
+
+ - `MCTP_LOG_STDERR`: use `fprintf(stderr, ...)` for log output
+
+ - `MCTP_LOG_SYSLOG`: use `syslog()` for log output
+
+ - `MCTP_LOG_CUSTOM`: provide your own macro for logging, of
+ the format: ```#define mctp_prlog(level, fmt, ...) (....)```
+
+TODO
+----
+
+ - Message packetisation and reassembly
+ - Control messages
+ - Message- and packet-buffer pools and preallocation
+ - C++ API
+ - Non-file-based serial binding
diff --git a/alloc.c b/alloc.c
new file mode 100644
index 0000000..2389dd3
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include <assert.h>
+
+#include "libmctp-alloc.h"
+
+struct {
+ void *(*alloc)(size_t);
+ void (*free)(void *);
+ void *(*realloc)(void *, size_t);
+} alloc_ops = {
+ malloc,
+ free,
+ realloc,
+};
+
+/* internal-only allocation functions */
+void *__mctp_alloc(size_t size)
+{
+ if (alloc_ops.alloc)
+ return alloc_ops.alloc(size);
+ if (alloc_ops.realloc)
+ return alloc_ops.realloc(NULL, size);
+ assert(0);
+}
+
+void __mctp_free(void *ptr)
+{
+ if (alloc_ops.free)
+ alloc_ops.free(ptr);
+ else if (alloc_ops.realloc)
+ alloc_ops.realloc(ptr, 0);
+ else
+ assert(0);
+}
+
+void *__mctp_realloc(void *ptr, size_t size)
+{
+ if (alloc_ops.realloc)
+ return alloc_ops.realloc(ptr, size);
+ assert(0);
+}
+
+void mctp_set_alloc_ops(void *(*alloc)(size_t),
+ void (*free)(void *),
+ void *(realloc)(void *, size_t))
+{
+ alloc_ops.alloc = alloc;
+ alloc_ops.free = free;
+ alloc_ops.realloc = realloc;
+}
diff --git a/core.c b/core.c
new file mode 100644
index 0000000..575d14f
--- /dev/null
+++ b/core.c
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "core: " fmt
+
+#include "libmctp.h"
+#include "libmctp-alloc.h"
+#include "libmctp-log.h"
+
+/* Internal data structures */
+
+struct mctp_bus {
+ mctp_eid_t eid;
+ struct mctp_binding *binding;
+
+ /* todo: routing */
+};
+
+struct mctp {
+ /* todo: multiple busses */
+ struct mctp_bus busses[1];
+
+ struct mctp_pktbuf txbuf;
+
+ /* Message RX callback */
+ mctp_rx_fn message_rx;
+ void *message_rx_data;
+};
+
+#ifndef BUILD_ASSERT
+#define BUILD_ASSERT(x) \
+ do { (void)sizeof(char[0-(!(x))]); } while (0)
+#endif
+
+struct mctp_pktbuf *mctp_pktbuf_alloc(uint8_t len)
+{
+ struct mctp_pktbuf *buf;
+
+ BUILD_ASSERT(MCTP_PKTBUF_SIZE <= 0xff);
+
+ /* todo: pools */
+ buf = __mctp_alloc(sizeof(*buf));
+
+ buf->start = MCTP_PKTBUF_BINDING_PAD;
+ buf->end = buf->start + len;
+ buf->mctp_hdr_off = buf->start;
+
+ return buf;
+}
+
+void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
+{
+ __mctp_free(pkt);
+}
+
+struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
+{
+ return (void *)pkt->data + pkt->mctp_hdr_off;
+}
+
+void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
+{
+ return (void *)pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
+}
+
+uint8_t mctp_pktbuf_size(struct mctp_pktbuf *pkt)
+{
+ return pkt->end - pkt->start;
+}
+
+void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, uint8_t size)
+{
+ assert(size <= pkt->start);
+ pkt->start -= size;
+ return pkt->data + pkt->start;
+}
+
+void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, uint8_t size)
+{
+ void *buf;
+
+ assert(size < (MCTP_PKTBUF_SIZE - pkt->end));
+ buf = pkt->data + pkt->end;
+ pkt->end += size;
+ return buf;
+}
+
+int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, uint8_t len)
+{
+ void *p;
+
+ assert(pkt->end + len <= MCTP_PKTBUF_SIZE);
+
+ if (pkt->end + len > MCTP_PKTBUF_SIZE)
+ return -1;
+
+ p = pkt->data + pkt->end;
+
+ pkt->end += len;
+ memcpy(p, data, len);
+
+ return 0;
+}
+
+struct mctp *mctp_init(void)
+{
+ struct mctp *mctp;
+
+ mctp = __mctp_alloc(sizeof(*mctp));
+ memset(mctp, 0, sizeof(*mctp));
+
+ return mctp;
+}
+
+int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
+{
+ mctp->message_rx = fn;
+ mctp->message_rx_data = data;
+ return 0;
+}
+
+static struct mctp_bus *find_bus_for_eid(struct mctp *mctp,
+ mctp_eid_t dest __attribute__((unused)))
+{
+ return &mctp->busses[0];
+}
+
+unsigned long mctp_register_bus(struct mctp *mctp,
+ struct mctp_binding *binding,
+ mctp_eid_t eid)
+{
+ assert(!mctp->busses[0].binding);
+ mctp->busses[0].binding = binding;
+ mctp->busses[0].eid = eid;
+ return 0;
+}
+
+void mctp_bus_rx(struct mctp *mctp, unsigned long bus_id,
+ struct mctp_pktbuf *pkt)
+{
+ struct mctp_bus *bus = &mctp->busses[bus_id];
+ size_t len;
+ void *p;
+
+ len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
+ p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr),
+ mctp->message_rx(bus->eid, mctp->message_rx_data, p, len);
+}
+
+static int mctp_packet_tx(struct mctp *mctp __attribute__((unused)),
+ struct mctp_bus *bus,
+ struct mctp_pktbuf *pkt)
+{
+ return bus->binding->tx(bus->binding, pkt);
+}
+
+int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid,
+ void *msg, size_t msg_len)
+{
+ struct mctp_pktbuf *pkt;
+ struct mctp_hdr *hdr;
+ struct mctp_bus *bus;
+ int rc;
+
+ /* todo: multiple-packet messages, sequence numbers */
+ assert(msg_len <= MCTP_MTU);
+
+ bus = find_bus_for_eid(mctp, eid);
+
+ pkt = mctp_pktbuf_alloc(msg_len + sizeof(*hdr));
+ hdr = mctp_pktbuf_hdr(pkt);
+
+ /* todo: tags */
+ hdr->ver = bus->binding->version & 0xf;
+ hdr->dest = eid;
+ hdr->src = bus->eid;
+ hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
+ MCTP_HDR_FLAG_EOM |
+ (0 << MCTP_HDR_SEQ_SHIFT) |
+ MCTP_HDR_FLAG_TO |
+ (0 << MCTP_HDR_TAG_SHIFT);
+
+ /* todo: zero copy? */
+ memcpy(mctp_pktbuf_data(pkt), msg, msg_len);
+
+ rc = mctp_packet_tx(mctp, bus, pkt);
+
+ return rc;
+}
diff --git a/libmctp-alloc.h b/libmctp-alloc.h
new file mode 100644
index 0000000..e7b00f4
--- /dev/null
+++ b/libmctp-alloc.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#ifndef _LIBMCTP_ALLOC_H
+#define _LIBMCTP_ALLOC_H
+
+#include <stdlib.h>
+
+void *__mctp_alloc(size_t size);
+void __mctp_free(void *ptr);
+void *__mctp_realloc(void *ptr, size_t size);
+
+#endif /* _LIBMCTP_ALLOC_H */
diff --git a/libmctp-log.h b/libmctp-log.h
new file mode 100644
index 0000000..e24738a
--- /dev/null
+++ b/libmctp-log.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#ifndef _LIBMCTP_LOG_H
+#define _LIBMCTP_LOG_H
+
+/* libmctp-internal logging */
+#ifndef pr_fmt
+#define pr_fmt
+#endif
+
+#if defined(MCTP_LOG_STDERR)
+
+#include <stdio.h>
+
+#define MCTP_LOG_ERR 0
+#define MCTP_LOG_WARNING 0
+#define MCTP_LOG_NOTICE 0
+#define MCTP_LOG_INFO 0
+#define MCTP_LOG_DEBUG 0
+
+#define mctp_prlog(x, fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
+
+#elif defined(MCTP_LOG_SYSLOG)
+
+#include <syslog.h>
+
+#define MCTP_LOG_ERR LOG_ERR
+#define MCTP_LOG_WARNING LOG_WARNING
+#define MCTP_LOG_NOTICE LOG_NOTICE
+#define MCTP_LOG_INFO LOG_INFO
+#define MCTP_LOG_DEBUG LOG_DEBUG
+
+#define mctp_prlog(x, fmt, ...) syslog(x, fmt, ##__VA_ARGS__)
+
+#elif defined(MCTP_LOG_CUSTOM)
+
+#include <config.h>
+
+#if !defined(mctp_prlog)
+#error Custom logging implementation enabled, but no definition for mctp_prlog
+#endif
+
+
+#else
+#error No log implementation found
+#endif
+
+#define mctp_prerr(fmt, ...) mctp_prlog(MCTP_LOG_ERR, fmt, ##__VA_ARGS__)
+#define mctp_prwarn(fmt, ...) mctp_prlog(MCTP_LOG_WARNING, fmt, ##__VA_ARGS__)
+#define mctp_prinfo(fmt, ...) mctp_prlog(MCTP_LOG_INFO, fmt, ##__VA_ARGS__)
+#define mctp_prdebug(fmt, ...) mctp_prlog(MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__)
+
+
+#endif /* _LIBMCTP_LOG_H */
diff --git a/libmctp-serial.h b/libmctp-serial.h
new file mode 100644
index 0000000..0d3b972
--- /dev/null
+++ b/libmctp-serial.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#ifndef _LIBMCTP_SERIAL_H
+#define _LIBMCTP_SERIAL_H
+
+#include "libmctp.h"
+
+struct mctp_binding_serial;
+
+struct mctp_binding_serial *mctp_serial_init(void);
+int mctp_serial_get_fd(struct mctp_binding_serial *serial);
+void mctp_serial_register_bus(struct mctp_binding_serial *serial,
+ struct mctp *mctp, mctp_eid_t eid);
+int mctp_serial_read(struct mctp_binding_serial *serial);
+int mctp_serial_open_path(struct mctp_binding_serial *serial,
+ const char *path);
+void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd);
+
+#endif /* _LIBMCTP_SERIAL_H */
diff --git a/libmctp.h b/libmctp.h
new file mode 100644
index 0000000..5400788
--- /dev/null
+++ b/libmctp.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#ifndef _LIBMCTP_H
+#define _LIBMCTP_H
+
+#include <stdint.h>
+
+typedef uint8_t mctp_eid_t;
+
+/* MCTP packet definitions */
+struct mctp_hdr {
+ uint8_t ver;
+ uint8_t dest;
+ uint8_t src;
+ uint8_t flags_seq_tag;
+};
+
+/* Definitions for flags_seq_tag field */
+#define MCTP_HDR_FLAG_SOM (1<<7)
+#define MCTP_HDR_FLAG_EOM (1<<6)
+#define MCTP_HDR_FLAG_TO (1<<3)
+#define MCTP_HDR_SEQ_SHIFT (5)
+#define MCTP_HDR_SEQ_MASK (0x3)
+#define MCTP_HDR_TAG_SHIFT (0)
+#define MCTP_HDR_TAG_MASK (0x7)
+
+/* Maximum size of *payload* data in a MCTP packet
+ * @todo: dynamic sixing based on channel implementation.
+ */
+#define MCTP_MTU 64
+
+/* packet buffers */
+
+/* Allow a little space before the MCTP header in the packet, for bindings that
+ * may add their own header
+ */
+#define MCTP_PKTBUF_BINDING_PAD 2
+
+#define MCTP_PKTBUF_SIZE (MCTP_PKTBUF_BINDING_PAD + \
+ (sizeof(struct mctp_hdr) + MCTP_MTU)
+
+struct mctp_pktbuf {
+ unsigned char data[MCTP_PKTBUF_SIZE];
+ uint8_t start, end;
+ uint8_t mctp_hdr_off;
+};
+
+struct mctp_pktbuf *mctp_pktbuf_alloc(uint8_t len);
+void mctp_pktbuf_free(struct mctp_pktbuf *pkt);
+struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt);
+void *mctp_pktbuf_data(struct mctp_pktbuf *pkt);
+uint8_t mctp_pktbuf_size(struct mctp_pktbuf *pkt);
+void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, uint8_t size);
+void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, uint8_t size);
+int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, uint8_t len);
+
+/* MCTP core */
+struct mctp;
+struct mctp_binding;
+
+struct mctp *mctp_init(void);
+
+unsigned long mctp_register_bus(struct mctp *mctp,
+ struct mctp_binding *binding,
+ mctp_eid_t eid);
+
+typedef void (*mctp_rx_fn)(uint8_t src_eid, void *data,
+ void *msg, size_t len);
+
+int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data);
+
+int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid,
+ void *msg, size_t msg_len);
+
+/* hardware bindings */
+struct mctp_binding {
+ const char *name;
+ uint8_t version;
+ int (*tx)(struct mctp_binding *binding,
+ struct mctp_pktbuf *pkt);
+};
+
+void mctp_bus_rx(struct mctp *mctp, unsigned long bus_id,
+ struct mctp_pktbuf *pkt);
+
+/* environment-specific allocation */
+void mctp_set_alloc_ops(void *(*alloc)(size_t),
+ void (*free)(void *),
+ void *(realloc)(void *, size_t));
+
+
+#endif /* _LIBMCTP_H */
diff --git a/serial.c b/serial.c
new file mode 100644
index 0000000..7c4e9e3
--- /dev/null
+++ b/serial.c
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef MCTP_FILEIO
+#include <fcntl.h>
+#endif
+
+#define pr_fmt(x) "serial: " x
+
+#include "libmctp.h"
+#include "libmctp-alloc.h"
+#include "libmctp-log.h"
+#include "libmctp-serial.h"
+
+struct mctp_binding_serial {
+ struct mctp_binding binding;
+ struct mctp *mctp;
+ int fd;
+ unsigned long bus_id;
+
+ /* receive buffer and state */
+ uint8_t rxbuf[1024];
+ struct mctp_pktbuf *rx_pkt;
+ uint8_t rx_exp_len;
+ uint16_t rx_fcs;
+ 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;
+};
+
+#ifndef container_of
+#define container_of(ptr, type, member) \
+ (type *)((char *)(ptr) - (char *)&((type *)0)->member)
+#endif
+
+#define binding_to_serial(b) \
+ container_of(b, struct mctp_binding_serial, binding)
+
+#define MCTP_SERIAL_REVISION 0x01
+#define MCTP_SERIAL_FRAMING_FLAG 0x7e
+#define MCTP_SERIAL_ESCAPE 0x7d
+
+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 void mctp_serial_pkt_escape(struct mctp_pktbuf *pkt)
+{
+ uint8_t buf[MCTP_MTU + sizeof(struct mctp_hdr)], *p;
+ uint8_t total_len;
+ int i, j;
+
+ total_len = pkt->end - pkt->mctp_hdr_off;
+
+ p = (void *)mctp_pktbuf_hdr(pkt);
+
+ memcpy(buf, p, total_len);
+
+ for (i = 0, j = 0; i < total_len; i++, j++) {
+ uint8_t c = buf[i];
+ if (c == 0x7e || c == 0x7d) {
+ p[j] = 0x7d;
+ mctp_pktbuf_alloc_end(pkt, 1);
+ j++;
+ c ^= 0x20;
+ }
+ p[j] = c;
+ }
+}
+
+static int mctp_binding_serial_tx(struct mctp_binding *b,
+ struct mctp_pktbuf *pkt)
+{
+ struct mctp_binding_serial *serial = binding_to_serial(b);
+ struct mctp_serial_header *hdr;
+ struct mctp_serial_trailer *tlr;
+ uint8_t len;
+
+ /* the length field in the header excludes serial framing
+ * and escape sequences */
+ len = mctp_pktbuf_size(pkt);
+
+ hdr = mctp_pktbuf_alloc_start(pkt, 3);
+ hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
+ hdr->revision = MCTP_SERIAL_REVISION;
+ hdr->len = len;
+
+ mctp_serial_pkt_escape(pkt);
+
+ tlr = mctp_pktbuf_alloc_end(pkt, 3);
+ /* todo: trailer FCS */
+ tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
+
+ write(serial->fd, pkt->data + pkt->start, pkt->end - pkt->start);
+
+ return 0;
+}
+
+static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
+ bool valid)
+{
+ struct mctp_pktbuf *pkt = serial->rx_pkt;
+ assert(pkt);
+
+ if (valid)
+ mctp_bus_rx(serial->mctp, serial->bus_id, pkt);
+
+ mctp_pktbuf_free(pkt);
+ serial->rx_pkt = NULL;
+}
+
+static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
+ uint8_t len)
+{
+ serial->rx_pkt = mctp_pktbuf_alloc(len);
+}
+
+static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
+ uint8_t c)
+{
+ struct mctp_pktbuf *pkt = serial->rx_pkt;
+
+ mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
+
+ assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
+ serial->rx_state == STATE_WAIT_REVISION ||
+ serial->rx_state == STATE_WAIT_LEN));
+
+ switch (serial->rx_state) {
+ case STATE_WAIT_SYNC_START:
+ if (c != MCTP_SERIAL_FRAMING_FLAG) {
+ mctp_prdebug("lost sync, dropping packet");
+ if (pkt)
+ mctp_serial_finish_packet(serial, false);
+ } else {
+ serial->rx_state = STATE_WAIT_REVISION;
+ }
+ break;
+
+ case STATE_WAIT_REVISION:
+ if (c == MCTP_SERIAL_REVISION) {
+ serial->rx_state = STATE_WAIT_LEN;
+ } else {
+ mctp_prdebug("invalid revision 0x%02x", c);
+ serial->rx_state = STATE_WAIT_SYNC_START;
+ }
+ break;
+ case STATE_WAIT_LEN:
+ if (c > MCTP_MTU || c < sizeof(struct mctp_hdr)) {
+ mctp_prdebug("invalid size %d", c);
+ serial->rx_state = STATE_WAIT_SYNC_START;
+ } else {
+ uint8_t *p;
+
+ mctp_serial_start_packet(serial, 0);
+ pkt = serial->rx_pkt;
+ p = mctp_pktbuf_alloc_start(pkt, 3);
+ p[0] = MCTP_SERIAL_FRAMING_FLAG;
+ p[1] = MCTP_SERIAL_REVISION;
+ p[2] = c;
+ serial->rx_exp_len = c;
+ serial->rx_state = STATE_DATA;
+ }
+ break;
+
+ case STATE_DATA:
+ if (c == MCTP_SERIAL_ESCAPE) {
+ serial->rx_state = STATE_DATA_ESCAPED;
+ } else {
+ mctp_pktbuf_push(pkt, &c, 1);
+ if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
+ serial->rx_state = STATE_WAIT_FCS1;
+ }
+ break;
+
+ case STATE_DATA_ESCAPED:
+ c ^= 0x20;
+ mctp_pktbuf_push(pkt, &c, 1);
+ if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
+ serial->rx_state = STATE_WAIT_FCS1;
+ else
+ serial->rx_state = STATE_DATA;
+ break;
+
+ case STATE_WAIT_FCS1:
+ serial->rx_fcs = c << 8;
+ serial->rx_state = STATE_WAIT_FCS2;
+ break;
+ case STATE_WAIT_FCS2:
+ serial->rx_fcs |= c;
+ /* todo: check fcs */
+ serial->rx_state = STATE_WAIT_SYNC_END;
+ break;
+
+ case STATE_WAIT_SYNC_END:
+ if (c == MCTP_SERIAL_FRAMING_FLAG) {
+ mctp_serial_finish_packet(serial, true);
+ } else {
+ mctp_prdebug("missing end frame marker");
+ mctp_serial_finish_packet(serial, false);
+ }
+ serial->rx_state = STATE_WAIT_SYNC_START;
+ break;
+ }
+
+ mctp_prdebug(" -> state: %d", serial->rx_state);
+}
+static void __attribute__((used)) mctp_rx_consume(struct mctp_binding_serial *serial,
+ void *buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
+}
+
+#ifdef MCTP_FILEIO
+int mctp_serial_read(struct mctp_binding_serial *serial)
+{
+ ssize_t len;
+
+ len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
+ if (len == 0)
+ return -1;
+
+ if (len < 0) {
+ mctp_prerr("can't read from serial device: %m");
+ return -1;
+ }
+
+ mctp_rx_consume(serial, serial->rxbuf, len);
+
+ return 0;
+}
+
+int mctp_serial_get_fd(struct mctp_binding_serial *serial)
+{
+ return serial->fd;
+}
+
+int mctp_serial_open_path(struct mctp_binding_serial *serial,
+ const char *device)
+{
+ serial->fd = open(device, O_RDWR);
+ if (serial->fd < 0)
+ mctp_prerr("can't open device %s: %m", device);
+
+ return 0;
+}
+
+void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
+{
+ serial->fd = fd;
+}
+#endif
+
+void mctp_serial_register_bus(struct mctp_binding_serial *serial,
+ struct mctp *mctp, mctp_eid_t eid)
+{
+ assert(serial->fd >= 0);
+ serial->mctp = mctp;
+ serial->bus_id = mctp_register_bus(mctp, &serial->binding, eid);
+}
+
+struct mctp_binding_serial *mctp_serial_init(void)
+{
+ struct mctp_binding_serial *serial;
+
+ serial = __mctp_alloc(sizeof(*serial));
+ serial->fd = -1;
+ serial->rx_state = STATE_WAIT_SYNC_START;
+ serial->rx_pkt = NULL;
+ serial->binding.name = "serial";
+ serial->binding.version = 1;
+
+ serial->binding.tx = mctp_binding_serial_tx;
+
+ return serial;
+}
+
diff --git a/tests/mctp-in.c b/tests/mctp-in.c
new file mode 100644
index 0000000..8444d59
--- /dev/null
+++ b/tests/mctp-in.c
@@ -0,0 +1,46 @@
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include "libmctp.h"
+#include "libmctp-serial.h"
+
+static void rx_message(uint8_t eid, void *data, void *msg, size_t len)
+{
+ (void)eid;
+ (void)data;
+ write(STDOUT_FILENO, msg, len);
+}
+
+int main(void)
+{
+ struct mctp_binding_serial *serial;
+ struct mctp *mctp;
+ int rc;
+
+ mctp = mctp_init();
+ assert(mctp);
+
+ serial = mctp_serial_init();
+ assert(serial);
+
+ mctp_serial_open_fd(serial, STDIN_FILENO);
+
+ mctp_serial_register_bus(serial, mctp, 8);
+
+ mctp_set_rx_all(mctp, rx_message, NULL);
+
+ for (;;) {
+ rc = mctp_serial_read(serial);
+ if (rc)
+ break;
+ }
+
+ return EXIT_SUCCESS;
+
+}
diff --git a/tests/mctp-pipe.c b/tests/mctp-pipe.c
new file mode 100644
index 0000000..9c37578
--- /dev/null
+++ b/tests/mctp-pipe.c
@@ -0,0 +1,80 @@
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include "libmctp.h"
+#include "libmctp-serial.h"
+
+static void rx_message(uint8_t eid, void *data, void *msg, size_t len)
+{
+ (void)eid;
+ (void)data;
+ write(STDOUT_FILENO, msg, len);
+}
+
+int main(void)
+{
+ struct mctp_binding_serial *serial[2];
+ mctp_eid_t eids[] = {8, 9};
+ struct pollfd pollfds[3];
+ struct mctp *mctp[2];
+ int rc, mctp_fds[2];
+
+ mctp[0] = mctp_init();
+ mctp[1] = mctp_init();
+
+ assert(mctp[0] && mctp[1]);
+
+ serial[0] = mctp_serial_init();
+ serial[1] = mctp_serial_init();
+
+ assert(serial[0] && serial[1]);
+
+ rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, mctp_fds);
+ if (rc)
+ err(EXIT_FAILURE, "Can't create sockets");
+
+ mctp_serial_open_fd(serial[0], mctp_fds[0]);
+ mctp_serial_open_fd(serial[1], mctp_fds[1]);
+
+ mctp_serial_register_bus(serial[0], mctp[0], eids[0]);
+ mctp_serial_register_bus(serial[1], mctp[1], eids[1]);
+
+ mctp_set_rx_all(mctp[1], rx_message, NULL);
+
+ pollfds[0].fd = mctp_fds[0];
+ pollfds[0].events = POLLIN;
+ pollfds[1].fd = mctp_fds[1];
+ pollfds[1].events = POLLIN;
+ pollfds[2].fd = STDIN_FILENO;
+ pollfds[2].events = POLLIN;
+
+ for (;;) {
+ uint8_t buf[1024];
+
+ rc = poll(pollfds, 3, 0);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ if (pollfds[0].revents)
+ mctp_serial_read(serial[0]);
+ if (pollfds[1].revents)
+ mctp_serial_read(serial[1]);
+ if (pollfds[2].revents) {
+ rc = read(STDIN_FILENO, buf, sizeof(buf));
+ if (rc == 0)
+ break;
+ else if (rc < 0)
+ err(EXIT_FAILURE, "read");
+ mctp_message_tx(mctp[0], eids[1], buf, rc);
+ }
+ }
+
+ return EXIT_SUCCESS;
+
+}