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