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