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