blob: 47d250e12b3ee53b23c29152daa752ba87d8c665 [file] [log] [blame]
Jeremy Kerr3d36ee22019-05-30 11:15:37 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08002
3#include <assert.h>
Andrew Jeffery89a28782022-09-29 11:46:31 +09304#include <errno.h>
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08005#include <stdbool.h>
6#include <stdlib.h>
7#include <string.h>
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08008
John Chungb3de3432024-04-19 11:47:01 +08009#include "crc-16-ccitt.h"
10
Jeremy Kerrc7e764a2019-05-28 16:49:03 +080011#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif
14
15#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080016#include <fcntl.h>
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093017#include <poll.h>
18#include <unistd.h>
Jeremy Kerr597b3692019-09-03 16:24:48 +080019#else
20static const size_t write(int fd, void *buf, size_t len)
21{
22 return -1;
23}
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080024#endif
25
26#define pr_fmt(x) "serial: " x
27
Andrew Jeffery89a28782022-09-29 11:46:31 +093028/*
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
52static 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}
Andrew Jefferyad772b92020-01-10 16:04:18 +103058
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080059#include "libmctp.h"
60#include "libmctp-alloc.h"
61#include "libmctp-log.h"
62#include "libmctp-serial.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010063#include "container_of.h"
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080064
65struct mctp_binding_serial {
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093066 struct mctp_binding binding;
67 int fd;
68 unsigned long bus_id;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080069
Patrick Williamsa721c2d2022-12-04 14:30:26 -060070 mctp_serial_tx_fn tx_fn;
71 void *tx_fn_data;
Jeremy Kerr597b3692019-09-03 16:24:48 +080072
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080073 /* receive buffer and state */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060074 uint8_t rxbuf[1024];
75 struct mctp_pktbuf *rx_pkt;
76 uint8_t rx_exp_len;
77 uint16_t rx_fcs;
John Chungb3de3432024-04-19 11:47:01 +080078 uint16_t rx_fcs_calc;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080079 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080089
90 /* temporary transmit buffer */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060091 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080092};
93
Patrick Williamsa721c2d2022-12-04 14:30:26 -060094#define binding_to_serial(b) \
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080095 container_of(b, struct mctp_binding_serial, binding)
96
Patrick Williamsa721c2d2022-12-04 14:30:26 -060097#define MCTP_SERIAL_REVISION 0x01
98#define MCTP_SERIAL_FRAMING_FLAG 0x7e
99#define MCTP_SERIAL_ESCAPE 0x7d
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800100
101struct mctp_serial_header {
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600102 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800103 uint8_t revision;
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600104 uint8_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800105};
106
107struct mctp_serial_trailer {
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600108 uint8_t fcs_msb;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800109 uint8_t fcs_lsb;
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600110 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800111};
112
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800113static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800114{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800115 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800116 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800117 int i, j;
118
119 total_len = pkt->end - pkt->mctp_hdr_off;
120
121 p = (void *)mctp_pktbuf_hdr(pkt);
122
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800123 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800124 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800125 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800126 if (buf)
127 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800128 j++;
129 c ^= 0x20;
130 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800131 if (buf)
132 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800133 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800134
135 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800136}
137
138static int mctp_binding_serial_tx(struct mctp_binding *b,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600139 struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800140{
141 struct mctp_binding_serial *serial = binding_to_serial(b);
142 struct mctp_serial_header *hdr;
143 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800144 uint8_t *buf;
145 size_t len;
John Chungb3de3432024-04-19 11:47:01 +0800146 uint16_t fcs;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800147
148 /* the length field in the header excludes serial framing
149 * and escape sequences */
150 len = mctp_pktbuf_size(pkt);
151
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800152 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800153 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
154 hdr->revision = MCTP_SERIAL_REVISION;
155 hdr->len = len;
156
John Chungb3de3432024-04-19 11:47:01 +0800157 // 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
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800161 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800162
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800163 len = mctp_serial_pkt_escape(pkt, NULL);
164 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
Andrew Jeffery0721f582022-09-29 12:12:39 +0930165 return -EMSGSIZE;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800166
167 mctp_serial_pkt_escape(pkt, buf);
168
169 buf += len;
170
171 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800172 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
John Chungb3de3432024-04-19 11:47:01 +0800173 tlr->fcs_msb = fcs >> 8;
174 tlr->fcs_lsb = fcs & 0xff;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800175
Jeremy Kerr597b3692019-09-03 16:24:48 +0800176 len += sizeof(*hdr) + sizeof(*tlr);
177
Andrew Jefferyad772b92020-01-10 16:04:18 +1030178 if (!serial->tx_fn)
Andrew Jeffery89a28782022-09-29 11:46:31 +0930179 return mctp_write_all(mctp_serial_write, serial->fd,
180 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800181
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600182 return mctp_write_all(serial->tx_fn, serial->tx_fn_data,
183 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800184}
185
186static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600187 bool valid)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800188{
189 struct mctp_pktbuf *pkt = serial->rx_pkt;
190 assert(pkt);
191
192 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800193 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800194
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800195 serial->rx_pkt = NULL;
196}
197
198static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600199 uint8_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800200{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800201 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800202}
203
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600204static void mctp_rx_consume_one(struct mctp_binding_serial *serial, uint8_t c)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800205{
206 struct mctp_pktbuf *pkt = serial->rx_pkt;
John Chungb3de3432024-04-19 11:47:01 +0800207 bool valid = false;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800208
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;
John Chungb3de3432024-04-19 11:47:01 +0800229 serial->rx_fcs_calc = crc_16_ccitt_byte(FCS_INIT_16, c);
Nikhil Namjoshi1fe58992024-01-09 04:00:12 +0000230 } 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);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800242 } 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:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800248 if (c > serial->binding.pkt_size ||
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600249 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800250 mctp_prdebug("invalid size %d", c);
251 serial->rx_state = STATE_WAIT_SYNC_START;
252 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800253 mctp_serial_start_packet(serial, 0);
254 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800255 serial->rx_exp_len = c;
256 serial->rx_state = STATE_DATA;
John Chungb3de3432024-04-19 11:47:01 +0800257 serial->rx_fcs_calc =
258 crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800259 }
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);
John Chungb3de3432024-04-19 11:47:01 +0800267 serial->rx_fcs_calc =
268 crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800269 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);
John Chungb3de3432024-04-19 11:47:01 +0800277 serial->rx_fcs_calc = crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800278 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;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800290 serial->rx_state = STATE_WAIT_SYNC_END;
291 break;
292
293 case STATE_WAIT_SYNC_END:
John Chungb3de3432024-04-19 11:47:01 +0800294 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 }
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800301 } else {
John Chungb3de3432024-04-19 11:47:01 +0800302 valid = false;
303 mctp_prdebug("invalid fcs : 0x%04x, expect 0x%04x",
304 serial->rx_fcs, serial->rx_fcs_calc);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800305 }
John Chungb3de3432024-04-19 11:47:01 +0800306
307 mctp_serial_finish_packet(serial, valid);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800308 serial->rx_state = STATE_WAIT_SYNC_START;
309 break;
310 }
311
312 mctp_prdebug(" -> state: %d", serial->rx_state);
313}
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600314static void mctp_rx_consume(struct mctp_binding_serial *serial, const void *buf,
315 size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800316{
317 size_t i;
318
319 for (i = 0; i < len; i++)
320 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
321}
322
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800323#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800324int 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: %m");
334 return -1;
335 }
336
337 mctp_rx_consume(serial, serial->rxbuf, len);
338
339 return 0;
340}
341
Andrew Jeffery1111c6a2022-07-25 20:44:39 +0930342int mctp_serial_init_pollfd(struct mctp_binding_serial *serial,
343 struct pollfd *pollfd)
344{
345 pollfd->fd = serial->fd;
346 pollfd->events = POLLIN;
347
348 return 0;
349}
350
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800351int mctp_serial_open_path(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600352 const char *device)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800353{
354 serial->fd = open(device, O_RDWR);
355 if (serial->fd < 0)
356 mctp_prerr("can't open device %s: %m", device);
357
358 return 0;
359}
360
361void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
362{
363 serial->fd = fd;
364}
365#endif
366
Jeremy Kerr597b3692019-09-03 16:24:48 +0800367void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600368 mctp_serial_tx_fn fn, void *data)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800369{
370 serial->tx_fn = fn;
371 serial->tx_fn_data = data;
372}
373
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600374int mctp_serial_rx(struct mctp_binding_serial *serial, const void *buf,
375 size_t len)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800376{
377 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030378 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800379}
380
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800381static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800382{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800383 mctp_binding_set_tx_enabled(binding, true);
384 return 0;
385}
386
387struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
388{
389 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800390}
391
392struct mctp_binding_serial *mctp_serial_init(void)
393{
394 struct mctp_binding_serial *serial;
395
396 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800397 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800398 serial->fd = -1;
399 serial->rx_state = STATE_WAIT_SYNC_START;
400 serial->rx_pkt = NULL;
401 serial->binding.name = "serial";
402 serial->binding.version = 1;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030403 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Andrew Jeffery39da3d02021-03-12 16:51:26 +1030404 serial->binding.pkt_header = 0;
Konstantin Aladyshev2a2a0f62023-08-25 23:40:02 +0300405 serial->binding.pkt_trailer = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800406
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800407 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800408 serial->binding.tx = mctp_binding_serial_tx;
409
410 return serial;
411}
412
Andrew Jefferyf8b47492020-03-10 23:48:59 +1030413void mctp_serial_destroy(struct mctp_binding_serial *serial)
414{
415 __mctp_free(serial);
416}