serial: Support Frame Check Sequence

According to DSP0253, implementing frame check sequence via
crc-16-ccitt calculation. crc-16-ccitt implementation refer
to RFC1662 Appendix C.

Tested:
Verified on sending/receiving mctp packets with mctp-serial
kernel driver.

Change-Id: I8417d521589e9f40a0ca68596cdcfd061fd919cc
Signed-off-by: John Chung <john.chung@arm.com>
diff --git a/serial.c b/serial.c
index 8ac4468..47d250e 100644
--- a/serial.c
+++ b/serial.c
@@ -6,6 +6,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "crc-16-ccitt.h"
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -73,6 +75,7 @@
 	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,
@@ -140,6 +143,7 @@
 	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 */
@@ -150,6 +154,10 @@
 	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);
@@ -162,9 +170,8 @@
 
 	tlr = (void *)buf;
 	tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
-	/* todo: trailer FCS */
-	tlr->fcs_msb = 0;
-	tlr->fcs_lsb = 0;
+	tlr->fcs_msb = fcs >> 8;
+	tlr->fcs_lsb = fcs & 0xff;
 
 	len += sizeof(*hdr) + sizeof(*tlr);
 
@@ -197,6 +204,7 @@
 static void mctp_rx_consume_one(struct mctp_binding_serial *serial, uint8_t c)
 {
 	struct mctp_pktbuf *pkt = serial->rx_pkt;
+	bool valid = false;
 
 	mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
 
@@ -218,6 +226,7 @@
 	case STATE_WAIT_REVISION:
 		if (c == MCTP_SERIAL_REVISION) {
 			serial->rx_state = STATE_WAIT_LEN;
+			serial->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
@@ -245,6 +254,8 @@
 			pkt = serial->rx_pkt;
 			serial->rx_exp_len = c;
 			serial->rx_state = STATE_DATA;
+			serial->rx_fcs_calc =
+				crc_16_ccitt_byte(serial->rx_fcs_calc, c);
 		}
 		break;
 
@@ -253,6 +264,8 @@
 			serial->rx_state = STATE_DATA_ESCAPED;
 		} else {
 			mctp_pktbuf_push(pkt, &c, 1);
+			serial->rx_fcs_calc =
+				crc_16_ccitt_byte(serial->rx_fcs_calc, c);
 			if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
 				serial->rx_state = STATE_WAIT_FCS1;
 		}
@@ -261,6 +274,7 @@
 	case STATE_DATA_ESCAPED:
 		c ^= 0x20;
 		mctp_pktbuf_push(pkt, &c, 1);
+		serial->rx_fcs_calc = crc_16_ccitt_byte(serial->rx_fcs_calc, c);
 		if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
 			serial->rx_state = STATE_WAIT_FCS1;
 		else
@@ -273,17 +287,24 @@
 		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);
+		if (serial->rx_fcs == serial->rx_fcs_calc) {
+			if (c == MCTP_SERIAL_FRAMING_FLAG) {
+				valid = true;
+			} else {
+				valid = false;
+				mctp_prdebug("missing end frame marker");
+			}
 		} else {
-			mctp_prdebug("missing end frame marker");
-			mctp_serial_finish_packet(serial, false);
+			valid = false;
+			mctp_prdebug("invalid fcs : 0x%04x, expect 0x%04x",
+				     serial->rx_fcs, serial->rx_fcs_calc);
 		}
+
+		mctp_serial_finish_packet(serial, valid);
 		serial->rx_state = STATE_WAIT_SYNC_START;
 		break;
 	}