xref: /openbmc/libmctp/serial.c (revision 4a09e1dc48831f20e15b5fe76bf3011eaf587dd9)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 
3 #include <assert.h>
4 #include <errno.h>
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include "crc-16-ccitt.h"
10 
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14 
15 #ifdef MCTP_HAVE_FILEIO
16 #include <fcntl.h>
17 #include <poll.h>
18 #include <unistd.h>
19 #else
write(int fd,const void * buf,size_t len)20 static const size_t write(int fd, const void *buf, size_t len)
21 {
22 	return -1;
23 }
24 #endif
25 
26 #define pr_fmt(x) "serial: " x
27 
28 #define SERIAL_BTU MCTP_BTU
29 
30 #include "libmctp.h"
31 #include "libmctp-alloc.h"
32 #include "libmctp-log.h"
33 #include "libmctp-serial.h"
34 #include "container_of.h"
35 
36 struct mctp_binding_serial {
37 	struct mctp_binding binding;
38 	int fd;
39 	unsigned long bus_id;
40 
41 	mctp_serial_tx_fn tx_fn;
42 	void *tx_fn_data;
43 
44 	/* receive buffer and state */
45 	uint8_t rxbuf[1024];
46 	struct mctp_pktbuf *rx_pkt;
47 	uint8_t rx_storage[MCTP_PKTBUF_SIZE(SERIAL_BTU)];
48 	uint8_t rx_exp_len;
49 	uint16_t rx_fcs;
50 	uint16_t rx_fcs_calc;
51 	enum {
52 		STATE_WAIT_SYNC_START,
53 		STATE_WAIT_REVISION,
54 		STATE_WAIT_LEN,
55 		STATE_DATA,
56 		STATE_DATA_ESCAPED,
57 		STATE_WAIT_FCS1,
58 		STATE_WAIT_FCS2,
59 		STATE_WAIT_SYNC_END,
60 	} rx_state;
61 
62 	/* temporary transmit buffer */
63 	uint8_t txbuf[256];
64 	/* used by the MCTP stack */
65 	uint8_t tx_storage[MCTP_PKTBUF_SIZE(SERIAL_BTU)];
66 };
67 
68 #define binding_to_serial(b)                                                   \
69 	container_of(b, struct mctp_binding_serial, binding)
70 
71 #define MCTP_SERIAL_REVISION	 0x01
72 #define MCTP_SERIAL_FRAMING_FLAG 0x7e
73 #define MCTP_SERIAL_ESCAPE	 0x7d
74 
75 struct mctp_serial_header {
76 	uint8_t flag;
77 	uint8_t revision;
78 	uint8_t len;
79 };
80 
81 struct mctp_serial_trailer {
82 	uint8_t fcs_msb;
83 	uint8_t fcs_lsb;
84 	uint8_t flag;
85 };
86 
87 /*
88  * @fn: A function that will copy data from the buffer at src into the dst object
89  * @dst: An opaque object to pass as state to fn
90  * @src: A pointer to the buffer of data to copy to dst
91  * @len: The length of the data pointed to by src
92  * @return: 0 on succes, negative error code on failure
93  *
94  * Pre-condition: fn returns a write count or a negative error code
95  * Post-condition: All bytes written or an error has occurred
96  */
mctp_write_all(mctp_serial_tx_fn fn,void * dst,uint8_t * src,size_t len)97 static ssize_t mctp_write_all(mctp_serial_tx_fn fn, void *dst, uint8_t *src,
98 			      size_t len)
99 {
100 	uint8_t *__src = src;
101 	ssize_t wrote;
102 	while (len) {
103 		wrote = fn(dst, __src, len);
104 		if (wrote < 0) {
105 			break;
106 		}
107 		__src += wrote;
108 		len -= wrote;
109 	}
110 	return len ? wrote : 0;
111 }
112 
mctp_serial_write(void * fildesp,void * buf,size_t nbyte)113 static int mctp_serial_write(void *fildesp, void *buf, size_t nbyte)
114 {
115 	ssize_t wrote;
116 	int fildes = *((int *)fildesp);
117 
118 	return ((wrote = write(fildes, buf, nbyte)) < 0) ? -errno : wrote;
119 }
120 
mctp_serial_pkt_escape(struct mctp_pktbuf * pkt,uint8_t * buf)121 static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
122 {
123 	uint8_t total_len;
124 	uint8_t *p;
125 	int i, j;
126 
127 	total_len = pkt->end - pkt->mctp_hdr_off;
128 
129 	p = (void *)mctp_pktbuf_hdr(pkt);
130 
131 	for (i = 0, j = 0; i < total_len; i++, j++) {
132 		uint8_t c = p[i];
133 		if (c == 0x7e || c == 0x7d) {
134 			if (buf)
135 				buf[j] = 0x7d;
136 			j++;
137 			c ^= 0x20;
138 		}
139 		if (buf)
140 			buf[j] = c;
141 	}
142 
143 	return j;
144 }
145 
mctp_binding_serial_tx(struct mctp_binding * b,struct mctp_pktbuf * pkt)146 static int mctp_binding_serial_tx(struct mctp_binding *b,
147 				  struct mctp_pktbuf *pkt)
148 {
149 	struct mctp_binding_serial *serial = binding_to_serial(b);
150 	struct mctp_serial_header *hdr;
151 	struct mctp_serial_trailer *tlr;
152 	uint8_t *buf;
153 	size_t len;
154 	uint16_t fcs;
155 
156 	/* the length field in the header excludes serial framing
157 	 * and escape sequences */
158 	len = mctp_pktbuf_size(pkt);
159 
160 	hdr = (void *)serial->txbuf;
161 	hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
162 	hdr->revision = MCTP_SERIAL_REVISION;
163 	hdr->len = len;
164 
165 	// Calculate fcs
166 	fcs = crc_16_ccitt(FCS_INIT_16, (const uint8_t *)hdr + 1, 2);
167 	fcs = crc_16_ccitt(fcs, (const uint8_t *)mctp_pktbuf_hdr(pkt), len);
168 
169 	buf = (void *)(hdr + 1);
170 
171 	len = mctp_serial_pkt_escape(pkt, NULL);
172 	if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
173 		return -EMSGSIZE;
174 
175 	mctp_serial_pkt_escape(pkt, buf);
176 
177 	buf += len;
178 
179 	tlr = (void *)buf;
180 	tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
181 	tlr->fcs_msb = fcs >> 8;
182 	tlr->fcs_lsb = fcs & 0xff;
183 
184 	len += sizeof(*hdr) + sizeof(*tlr);
185 
186 	if (!serial->tx_fn)
187 		return mctp_write_all(mctp_serial_write, &serial->fd,
188 				      &serial->txbuf[0], len);
189 
190 	return mctp_write_all(serial->tx_fn, serial->tx_fn_data,
191 			      &serial->txbuf[0], len);
192 }
193 
mctp_serial_finish_packet(struct mctp_binding_serial * serial,bool valid)194 static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
195 				      bool valid)
196 {
197 	struct mctp_pktbuf *pkt = serial->rx_pkt;
198 	assert(pkt);
199 
200 	if (valid)
201 		mctp_bus_rx(&serial->binding, pkt);
202 
203 	serial->rx_pkt = NULL;
204 }
205 
mctp_serial_start_packet(struct mctp_binding_serial * serial)206 static void mctp_serial_start_packet(struct mctp_binding_serial *serial)
207 {
208 	serial->rx_pkt = mctp_pktbuf_init(&serial->binding, serial->rx_storage);
209 }
210 
mctp_rx_consume_one(struct mctp_binding_serial * serial,uint8_t c)211 static void mctp_rx_consume_one(struct mctp_binding_serial *serial, uint8_t c)
212 {
213 	struct mctp_pktbuf *pkt = serial->rx_pkt;
214 	bool valid = false;
215 
216 	mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
217 
218 	assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
219 			serial->rx_state == STATE_WAIT_REVISION ||
220 			serial->rx_state == STATE_WAIT_LEN));
221 
222 	switch (serial->rx_state) {
223 	case STATE_WAIT_SYNC_START:
224 		if (c != MCTP_SERIAL_FRAMING_FLAG) {
225 			mctp_prdebug("lost sync, dropping packet");
226 			if (pkt)
227 				mctp_serial_finish_packet(serial, false);
228 		} else {
229 			serial->rx_state = STATE_WAIT_REVISION;
230 		}
231 		break;
232 
233 	case STATE_WAIT_REVISION:
234 		if (c == MCTP_SERIAL_REVISION) {
235 			serial->rx_state = STATE_WAIT_LEN;
236 			serial->rx_fcs_calc = crc_16_ccitt_byte(FCS_INIT_16, c);
237 		} else if (c == MCTP_SERIAL_FRAMING_FLAG) {
238 			/* Handle the case where there are bytes dropped in request,
239 			 * and the state machine is out of sync. The failed request's
240 			 * trailing footer i.e. 0x7e would be interpreted as next
241 			 * request's framing footer. So if we are in STATE_WAIT_REVISION
242 			 * and receive 0x7e byte, then contine to stay in
243 			 * STATE_WAIT_REVISION
244 			 */
245 			mctp_prdebug(
246 				"Received serial framing flag 0x%02x while waiting"
247 				" for serial revision 0x%02x.",
248 				c, MCTP_SERIAL_REVISION);
249 		} else {
250 			mctp_prdebug("invalid revision 0x%02x", c);
251 			serial->rx_state = STATE_WAIT_SYNC_START;
252 		}
253 		break;
254 	case STATE_WAIT_LEN:
255 		if (c > serial->binding.pkt_size ||
256 		    c < sizeof(struct mctp_hdr)) {
257 			mctp_prdebug("invalid size %d", c);
258 			serial->rx_state = STATE_WAIT_SYNC_START;
259 		} else {
260 			mctp_serial_start_packet(serial);
261 			pkt = serial->rx_pkt;
262 			serial->rx_exp_len = c;
263 			serial->rx_state = STATE_DATA;
264 			serial->rx_fcs_calc =
265 				crc_16_ccitt_byte(serial->rx_fcs_calc, c);
266 		}
267 		break;
268 
269 	case STATE_DATA:
270 		if (c == MCTP_SERIAL_ESCAPE) {
271 			serial->rx_state = STATE_DATA_ESCAPED;
272 		} else {
273 			mctp_pktbuf_push(pkt, &c, 1);
274 			serial->rx_fcs_calc =
275 				crc_16_ccitt_byte(serial->rx_fcs_calc, c);
276 			if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
277 				serial->rx_state = STATE_WAIT_FCS1;
278 		}
279 		break;
280 
281 	case STATE_DATA_ESCAPED:
282 		c ^= 0x20;
283 		mctp_pktbuf_push(pkt, &c, 1);
284 		serial->rx_fcs_calc = crc_16_ccitt_byte(serial->rx_fcs_calc, c);
285 		if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
286 			serial->rx_state = STATE_WAIT_FCS1;
287 		else
288 			serial->rx_state = STATE_DATA;
289 		break;
290 
291 	case STATE_WAIT_FCS1:
292 		serial->rx_fcs = c << 8;
293 		serial->rx_state = STATE_WAIT_FCS2;
294 		break;
295 	case STATE_WAIT_FCS2:
296 		serial->rx_fcs |= c;
297 		serial->rx_state = STATE_WAIT_SYNC_END;
298 		break;
299 
300 	case STATE_WAIT_SYNC_END:
301 		if (serial->rx_fcs == serial->rx_fcs_calc) {
302 			if (c == MCTP_SERIAL_FRAMING_FLAG) {
303 				valid = true;
304 			} else {
305 				valid = false;
306 				mctp_prdebug("missing end frame marker");
307 			}
308 		} else {
309 			valid = false;
310 			mctp_prdebug("invalid fcs : 0x%04x, expect 0x%04x",
311 				     serial->rx_fcs, serial->rx_fcs_calc);
312 		}
313 
314 		mctp_serial_finish_packet(serial, valid);
315 		serial->rx_state = STATE_WAIT_SYNC_START;
316 		break;
317 	}
318 
319 	mctp_prdebug(" -> state: %d", serial->rx_state);
320 }
mctp_rx_consume(struct mctp_binding_serial * serial,const void * buf,size_t len)321 static void mctp_rx_consume(struct mctp_binding_serial *serial, const void *buf,
322 			    size_t len)
323 {
324 	size_t i;
325 
326 	for (i = 0; i < len; i++)
327 		mctp_rx_consume_one(serial, ((const uint8_t *)buf)[i]);
328 }
329 
330 #ifdef MCTP_HAVE_FILEIO
mctp_serial_read(struct mctp_binding_serial * serial)331 int mctp_serial_read(struct mctp_binding_serial *serial)
332 {
333 	ssize_t len;
334 
335 	len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
336 	if (len == 0)
337 		return -1;
338 
339 	if (len < 0) {
340 		mctp_prerr("can't read from serial device: %s",
341 			   strerror(errno));
342 		return -1;
343 	}
344 
345 	mctp_rx_consume(serial, serial->rxbuf, len);
346 
347 	return 0;
348 }
349 
mctp_serial_init_pollfd(struct mctp_binding_serial * serial,struct pollfd * pollfd)350 int mctp_serial_init_pollfd(struct mctp_binding_serial *serial,
351 			    struct pollfd *pollfd)
352 {
353 	pollfd->fd = serial->fd;
354 	pollfd->events = POLLIN;
355 
356 	return 0;
357 }
358 
mctp_serial_open_path(struct mctp_binding_serial * serial,const char * device)359 int mctp_serial_open_path(struct mctp_binding_serial *serial,
360 			  const char *device)
361 {
362 	serial->fd = open(device, O_RDWR);
363 	if (serial->fd < 0)
364 		mctp_prerr("can't open device %s: %s", device, strerror(errno));
365 
366 	return 0;
367 }
368 
mctp_serial_open_fd(struct mctp_binding_serial * serial,int fd)369 void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
370 {
371 	serial->fd = fd;
372 }
373 #endif
374 
mctp_serial_set_tx_fn(struct mctp_binding_serial * serial,mctp_serial_tx_fn fn,void * data)375 void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
376 			   mctp_serial_tx_fn fn, void *data)
377 {
378 	serial->tx_fn = fn;
379 	serial->tx_fn_data = data;
380 }
381 
mctp_serial_rx(struct mctp_binding_serial * serial,const void * buf,size_t len)382 int mctp_serial_rx(struct mctp_binding_serial *serial, const void *buf,
383 		   size_t len)
384 {
385 	mctp_rx_consume(serial, buf, len);
386 	return 0;
387 }
388 
mctp_serial_core_start(struct mctp_binding * binding)389 static int mctp_serial_core_start(struct mctp_binding *binding)
390 {
391 	mctp_binding_set_tx_enabled(binding, true);
392 	return 0;
393 }
394 
mctp_binding_serial_core(struct mctp_binding_serial * b)395 struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
396 {
397 	return &b->binding;
398 }
399 
mctp_serial_init(void)400 struct mctp_binding_serial *mctp_serial_init(void)
401 {
402 	struct mctp_binding_serial *serial;
403 
404 	serial = __mctp_alloc(sizeof(*serial));
405 	memset(serial, 0, sizeof(*serial));
406 	serial->fd = -1;
407 	serial->rx_state = STATE_WAIT_SYNC_START;
408 	serial->rx_pkt = NULL;
409 	serial->binding.name = "serial";
410 	serial->binding.version = 1;
411 	serial->binding.pkt_size = MCTP_PACKET_SIZE(SERIAL_BTU);
412 	serial->binding.pkt_header = 0;
413 	serial->binding.pkt_trailer = 0;
414 	serial->binding.tx_storage = serial->tx_storage;
415 
416 	serial->binding.start = mctp_serial_core_start;
417 	serial->binding.tx = mctp_binding_serial_tx;
418 
419 	return serial;
420 }
421 
mctp_serial_destroy(struct mctp_binding_serial * serial)422 void mctp_serial_destroy(struct mctp_binding_serial *serial)
423 {
424 	__mctp_free(serial);
425 }
426