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