blob: 77b6bae4d632ada768dbfb864f07735a8eb9d225 [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
Matt Johnston6e0c5d62024-09-10 16:03:13 +080020static const size_t write(int fd, const void *buf, size_t len)
Jeremy Kerr597b3692019-09-03 16:24:48 +080021{
22 return -1;
23}
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080024#endif
25
26#define pr_fmt(x) "serial: " x
27
28#include "libmctp.h"
29#include "libmctp-alloc.h"
30#include "libmctp-log.h"
31#include "libmctp-serial.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010032#include "container_of.h"
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080033
34struct mctp_binding_serial {
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093035 struct mctp_binding binding;
36 int fd;
37 unsigned long bus_id;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080038
Patrick Williamsa721c2d2022-12-04 14:30:26 -060039 mctp_serial_tx_fn tx_fn;
40 void *tx_fn_data;
Jeremy Kerr597b3692019-09-03 16:24:48 +080041
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080042 /* receive buffer and state */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060043 uint8_t rxbuf[1024];
44 struct mctp_pktbuf *rx_pkt;
45 uint8_t rx_exp_len;
46 uint16_t rx_fcs;
John Chungb3de3432024-04-19 11:47:01 +080047 uint16_t rx_fcs_calc;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080048 enum {
49 STATE_WAIT_SYNC_START,
50 STATE_WAIT_REVISION,
51 STATE_WAIT_LEN,
52 STATE_DATA,
53 STATE_DATA_ESCAPED,
54 STATE_WAIT_FCS1,
55 STATE_WAIT_FCS2,
56 STATE_WAIT_SYNC_END,
57 } rx_state;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080058
59 /* temporary transmit buffer */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060060 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080061};
62
Patrick Williamsa721c2d2022-12-04 14:30:26 -060063#define binding_to_serial(b) \
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080064 container_of(b, struct mctp_binding_serial, binding)
65
Patrick Williamsa721c2d2022-12-04 14:30:26 -060066#define MCTP_SERIAL_REVISION 0x01
67#define MCTP_SERIAL_FRAMING_FLAG 0x7e
68#define MCTP_SERIAL_ESCAPE 0x7d
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080069
70struct mctp_serial_header {
Patrick Williamsa721c2d2022-12-04 14:30:26 -060071 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080072 uint8_t revision;
Patrick Williamsa721c2d2022-12-04 14:30:26 -060073 uint8_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080074};
75
76struct mctp_serial_trailer {
Patrick Williamsa721c2d2022-12-04 14:30:26 -060077 uint8_t fcs_msb;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080078 uint8_t fcs_lsb;
Patrick Williamsa721c2d2022-12-04 14:30:26 -060079 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080080};
81
Matt Johnstone79ac012024-12-10 18:17:52 +080082/*
83 * @fn: A function that will copy data from the buffer at src into the dst object
84 * @dst: An opaque object to pass as state to fn
85 * @src: A pointer to the buffer of data to copy to dst
86 * @len: The length of the data pointed to by src
87 * @return: 0 on succes, negative error code on failure
88 *
89 * Pre-condition: fn returns a write count or a negative error code
90 * Post-condition: All bytes written or an error has occurred
91 */
92static ssize_t mctp_write_all(mctp_serial_tx_fn fn, void *dst, uint8_t *src,
93 size_t len)
94{
95 uint8_t *__src = src;
96 ssize_t wrote;
97 while (len) {
98 wrote = fn(dst, __src, len);
99 if (wrote < 0) {
100 break;
101 }
102 __src += wrote;
103 len -= wrote;
104 }
105 return len ? wrote : 0;
106}
107
108static int mctp_serial_write(void *fildesp, void *buf, size_t nbyte)
109{
110 ssize_t wrote;
111 int fildes = *((int *)fildesp);
112
113 return ((wrote = write(fildes, buf, nbyte)) < 0) ? -errno : wrote;
114}
115
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800116static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800117{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800118 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800119 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800120 int i, j;
121
122 total_len = pkt->end - pkt->mctp_hdr_off;
123
124 p = (void *)mctp_pktbuf_hdr(pkt);
125
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800126 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800127 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800128 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800129 if (buf)
130 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800131 j++;
132 c ^= 0x20;
133 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800134 if (buf)
135 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800136 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800137
138 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800139}
140
141static int mctp_binding_serial_tx(struct mctp_binding *b,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600142 struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800143{
144 struct mctp_binding_serial *serial = binding_to_serial(b);
145 struct mctp_serial_header *hdr;
146 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800147 uint8_t *buf;
148 size_t len;
John Chungb3de3432024-04-19 11:47:01 +0800149 uint16_t fcs;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800150
151 /* the length field in the header excludes serial framing
152 * and escape sequences */
153 len = mctp_pktbuf_size(pkt);
154
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800155 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800156 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
157 hdr->revision = MCTP_SERIAL_REVISION;
158 hdr->len = len;
159
John Chungb3de3432024-04-19 11:47:01 +0800160 // Calculate fcs
161 fcs = crc_16_ccitt(FCS_INIT_16, (const uint8_t *)hdr + 1, 2);
162 fcs = crc_16_ccitt(fcs, (const uint8_t *)mctp_pktbuf_hdr(pkt), len);
163
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800164 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800165
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800166 len = mctp_serial_pkt_escape(pkt, NULL);
167 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
Andrew Jeffery0721f582022-09-29 12:12:39 +0930168 return -EMSGSIZE;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800169
170 mctp_serial_pkt_escape(pkt, buf);
171
172 buf += len;
173
174 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800175 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
John Chungb3de3432024-04-19 11:47:01 +0800176 tlr->fcs_msb = fcs >> 8;
177 tlr->fcs_lsb = fcs & 0xff;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800178
Jeremy Kerr597b3692019-09-03 16:24:48 +0800179 len += sizeof(*hdr) + sizeof(*tlr);
180
Andrew Jefferyad772b92020-01-10 16:04:18 +1030181 if (!serial->tx_fn)
Matt Johnstone79ac012024-12-10 18:17:52 +0800182 return mctp_write_all(mctp_serial_write, &serial->fd,
Andrew Jeffery89a28782022-09-29 11:46:31 +0930183 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800184
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600185 return mctp_write_all(serial->tx_fn, serial->tx_fn_data,
186 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800187}
188
189static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600190 bool valid)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800191{
192 struct mctp_pktbuf *pkt = serial->rx_pkt;
193 assert(pkt);
194
195 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800196 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800197
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800198 serial->rx_pkt = NULL;
199}
200
201static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600202 uint8_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800203{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800204 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800205}
206
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600207static void mctp_rx_consume_one(struct mctp_binding_serial *serial, uint8_t c)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800208{
209 struct mctp_pktbuf *pkt = serial->rx_pkt;
John Chungb3de3432024-04-19 11:47:01 +0800210 bool valid = false;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800211
212 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
213
214 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
215 serial->rx_state == STATE_WAIT_REVISION ||
216 serial->rx_state == STATE_WAIT_LEN));
217
218 switch (serial->rx_state) {
219 case STATE_WAIT_SYNC_START:
220 if (c != MCTP_SERIAL_FRAMING_FLAG) {
221 mctp_prdebug("lost sync, dropping packet");
222 if (pkt)
223 mctp_serial_finish_packet(serial, false);
224 } else {
225 serial->rx_state = STATE_WAIT_REVISION;
226 }
227 break;
228
229 case STATE_WAIT_REVISION:
230 if (c == MCTP_SERIAL_REVISION) {
231 serial->rx_state = STATE_WAIT_LEN;
John Chungb3de3432024-04-19 11:47:01 +0800232 serial->rx_fcs_calc = crc_16_ccitt_byte(FCS_INIT_16, c);
Nikhil Namjoshi1fe58992024-01-09 04:00:12 +0000233 } else if (c == MCTP_SERIAL_FRAMING_FLAG) {
234 /* Handle the case where there are bytes dropped in request,
235 * and the state machine is out of sync. The failed request's
236 * trailing footer i.e. 0x7e would be interpreted as next
237 * request's framing footer. So if we are in STATE_WAIT_REVISION
238 * and receive 0x7e byte, then contine to stay in
239 * STATE_WAIT_REVISION
240 */
241 mctp_prdebug(
242 "Received serial framing flag 0x%02x while waiting"
243 " for serial revision 0x%02x.",
244 c, MCTP_SERIAL_REVISION);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800245 } else {
246 mctp_prdebug("invalid revision 0x%02x", c);
247 serial->rx_state = STATE_WAIT_SYNC_START;
248 }
249 break;
250 case STATE_WAIT_LEN:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800251 if (c > serial->binding.pkt_size ||
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600252 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800253 mctp_prdebug("invalid size %d", c);
254 serial->rx_state = STATE_WAIT_SYNC_START;
255 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800256 mctp_serial_start_packet(serial, 0);
257 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800258 serial->rx_exp_len = c;
259 serial->rx_state = STATE_DATA;
John Chungb3de3432024-04-19 11:47:01 +0800260 serial->rx_fcs_calc =
261 crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800262 }
263 break;
264
265 case STATE_DATA:
266 if (c == MCTP_SERIAL_ESCAPE) {
267 serial->rx_state = STATE_DATA_ESCAPED;
268 } else {
269 mctp_pktbuf_push(pkt, &c, 1);
John Chungb3de3432024-04-19 11:47:01 +0800270 serial->rx_fcs_calc =
271 crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800272 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
273 serial->rx_state = STATE_WAIT_FCS1;
274 }
275 break;
276
277 case STATE_DATA_ESCAPED:
278 c ^= 0x20;
279 mctp_pktbuf_push(pkt, &c, 1);
John Chungb3de3432024-04-19 11:47:01 +0800280 serial->rx_fcs_calc = crc_16_ccitt_byte(serial->rx_fcs_calc, c);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800281 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
282 serial->rx_state = STATE_WAIT_FCS1;
283 else
284 serial->rx_state = STATE_DATA;
285 break;
286
287 case STATE_WAIT_FCS1:
288 serial->rx_fcs = c << 8;
289 serial->rx_state = STATE_WAIT_FCS2;
290 break;
291 case STATE_WAIT_FCS2:
292 serial->rx_fcs |= c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800293 serial->rx_state = STATE_WAIT_SYNC_END;
294 break;
295
296 case STATE_WAIT_SYNC_END:
John Chungb3de3432024-04-19 11:47:01 +0800297 if (serial->rx_fcs == serial->rx_fcs_calc) {
298 if (c == MCTP_SERIAL_FRAMING_FLAG) {
299 valid = true;
300 } else {
301 valid = false;
302 mctp_prdebug("missing end frame marker");
303 }
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800304 } else {
John Chungb3de3432024-04-19 11:47:01 +0800305 valid = false;
306 mctp_prdebug("invalid fcs : 0x%04x, expect 0x%04x",
307 serial->rx_fcs, serial->rx_fcs_calc);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800308 }
John Chungb3de3432024-04-19 11:47:01 +0800309
310 mctp_serial_finish_packet(serial, valid);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800311 serial->rx_state = STATE_WAIT_SYNC_START;
312 break;
313 }
314
315 mctp_prdebug(" -> state: %d", serial->rx_state);
316}
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600317static void mctp_rx_consume(struct mctp_binding_serial *serial, const void *buf,
318 size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800319{
320 size_t i;
321
322 for (i = 0; i < len; i++)
Matt Johnston3ef47782024-12-11 15:19:06 +0800323 mctp_rx_consume_one(serial, ((const uint8_t *)buf)[i]);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800324}
325
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800326#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800327int mctp_serial_read(struct mctp_binding_serial *serial)
328{
329 ssize_t len;
330
331 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
332 if (len == 0)
333 return -1;
334
335 if (len < 0) {
Matt Johnston3ef47782024-12-11 15:19:06 +0800336 mctp_prerr("can't read from serial device: %s",
337 strerror(errno));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800338 return -1;
339 }
340
341 mctp_rx_consume(serial, serial->rxbuf, len);
342
343 return 0;
344}
345
Andrew Jeffery1111c6a2022-07-25 20:44:39 +0930346int mctp_serial_init_pollfd(struct mctp_binding_serial *serial,
347 struct pollfd *pollfd)
348{
349 pollfd->fd = serial->fd;
350 pollfd->events = POLLIN;
351
352 return 0;
353}
354
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800355int mctp_serial_open_path(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600356 const char *device)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800357{
358 serial->fd = open(device, O_RDWR);
359 if (serial->fd < 0)
Matt Johnston3ef47782024-12-11 15:19:06 +0800360 mctp_prerr("can't open device %s: %s", device, strerror(errno));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800361
362 return 0;
363}
364
365void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
366{
367 serial->fd = fd;
368}
369#endif
370
Jeremy Kerr597b3692019-09-03 16:24:48 +0800371void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600372 mctp_serial_tx_fn fn, void *data)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800373{
374 serial->tx_fn = fn;
375 serial->tx_fn_data = data;
376}
377
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600378int mctp_serial_rx(struct mctp_binding_serial *serial, const void *buf,
379 size_t len)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800380{
381 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030382 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800383}
384
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800385static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800386{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800387 mctp_binding_set_tx_enabled(binding, true);
388 return 0;
389}
390
391struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
392{
393 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800394}
395
396struct mctp_binding_serial *mctp_serial_init(void)
397{
398 struct mctp_binding_serial *serial;
399
400 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800401 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800402 serial->fd = -1;
403 serial->rx_state = STATE_WAIT_SYNC_START;
404 serial->rx_pkt = NULL;
405 serial->binding.name = "serial";
406 serial->binding.version = 1;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030407 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Andrew Jeffery39da3d02021-03-12 16:51:26 +1030408 serial->binding.pkt_header = 0;
Konstantin Aladyshev2a2a0f62023-08-25 23:40:02 +0300409 serial->binding.pkt_trailer = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800410
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800411 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800412 serial->binding.tx = mctp_binding_serial_tx;
413
414 return serial;
415}
416
Andrew Jefferyf8b47492020-03-10 23:48:59 +1030417void mctp_serial_destroy(struct mctp_binding_serial *serial)
418{
419 __mctp_free(serial);
420}