Initial MCTP core code

Just a skeleton of the MCTP library at present.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
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;
+}
+