blob: 22517a762b9896cca790c08e29db21355d760475 [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
Jeremy Kerrc7e764a2019-05-28 16:49:03 +08009#ifdef HAVE_CONFIG_H
10#include "config.h"
11#endif
12
13#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080014#include <fcntl.h>
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093015#include <poll.h>
16#include <unistd.h>
Jeremy Kerr597b3692019-09-03 16:24:48 +080017#else
18static const size_t write(int fd, void *buf, size_t len)
19{
20 return -1;
21}
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080022#endif
23
24#define pr_fmt(x) "serial: " x
25
Andrew Jeffery89a28782022-09-29 11:46:31 +093026/*
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
50static 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}
Andrew Jefferyad772b92020-01-10 16:04:18 +103056
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080057#include "libmctp.h"
58#include "libmctp-alloc.h"
59#include "libmctp-log.h"
60#include "libmctp-serial.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010061#include "container_of.h"
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080062
63struct mctp_binding_serial {
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093064 struct mctp_binding binding;
65 int fd;
66 unsigned long bus_id;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080067
Patrick Williamsa721c2d2022-12-04 14:30:26 -060068 mctp_serial_tx_fn tx_fn;
69 void *tx_fn_data;
Jeremy Kerr597b3692019-09-03 16:24:48 +080070
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080071 /* receive buffer and state */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060072 uint8_t rxbuf[1024];
73 struct mctp_pktbuf *rx_pkt;
74 uint8_t rx_exp_len;
75 uint16_t rx_fcs;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080076 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080086
87 /* temporary transmit buffer */
Patrick Williamsa721c2d2022-12-04 14:30:26 -060088 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080089};
90
Patrick Williamsa721c2d2022-12-04 14:30:26 -060091#define binding_to_serial(b) \
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080092 container_of(b, struct mctp_binding_serial, binding)
93
Patrick Williamsa721c2d2022-12-04 14:30:26 -060094#define MCTP_SERIAL_REVISION 0x01
95#define MCTP_SERIAL_FRAMING_FLAG 0x7e
96#define MCTP_SERIAL_ESCAPE 0x7d
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080097
98struct mctp_serial_header {
Patrick Williamsa721c2d2022-12-04 14:30:26 -060099 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800100 uint8_t revision;
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600101 uint8_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800102};
103
104struct mctp_serial_trailer {
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600105 uint8_t fcs_msb;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800106 uint8_t fcs_lsb;
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600107 uint8_t flag;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800108};
109
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800110static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800111{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800112 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800113 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800114 int i, j;
115
116 total_len = pkt->end - pkt->mctp_hdr_off;
117
118 p = (void *)mctp_pktbuf_hdr(pkt);
119
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800120 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800121 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800122 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800123 if (buf)
124 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800125 j++;
126 c ^= 0x20;
127 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800128 if (buf)
129 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800130 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800131
132 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800133}
134
135static int mctp_binding_serial_tx(struct mctp_binding *b,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600136 struct mctp_pktbuf *pkt)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800137{
138 struct mctp_binding_serial *serial = binding_to_serial(b);
139 struct mctp_serial_header *hdr;
140 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800141 uint8_t *buf;
142 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800143
144 /* the length field in the header excludes serial framing
145 * and escape sequences */
146 len = mctp_pktbuf_size(pkt);
147
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800148 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800149 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
150 hdr->revision = MCTP_SERIAL_REVISION;
151 hdr->len = len;
152
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800153 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800154
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800155 len = mctp_serial_pkt_escape(pkt, NULL);
156 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
Andrew Jeffery0721f582022-09-29 12:12:39 +0930157 return -EMSGSIZE;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800158
159 mctp_serial_pkt_escape(pkt, buf);
160
161 buf += len;
162
163 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800164 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800165 /* todo: trailer FCS */
166 tlr->fcs_msb = 0;
167 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800168
Jeremy Kerr597b3692019-09-03 16:24:48 +0800169 len += sizeof(*hdr) + sizeof(*tlr);
170
Andrew Jefferyad772b92020-01-10 16:04:18 +1030171 if (!serial->tx_fn)
Andrew Jeffery89a28782022-09-29 11:46:31 +0930172 return mctp_write_all(mctp_serial_write, serial->fd,
173 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800174
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600175 return mctp_write_all(serial->tx_fn, serial->tx_fn_data,
176 &serial->txbuf[0], len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800177}
178
179static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600180 bool valid)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800181{
182 struct mctp_pktbuf *pkt = serial->rx_pkt;
183 assert(pkt);
184
185 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800186 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800187
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800188 serial->rx_pkt = NULL;
189}
190
191static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600192 uint8_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800193{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800194 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800195}
196
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600197static void mctp_rx_consume_one(struct mctp_binding_serial *serial, uint8_t c)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800198{
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 {
222 mctp_prdebug("invalid revision 0x%02x", c);
223 serial->rx_state = STATE_WAIT_SYNC_START;
224 }
225 break;
226 case STATE_WAIT_LEN:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800227 if (c > serial->binding.pkt_size ||
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600228 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800229 mctp_prdebug("invalid size %d", c);
230 serial->rx_state = STATE_WAIT_SYNC_START;
231 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800232 mctp_serial_start_packet(serial, 0);
233 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800234 serial->rx_exp_len = c;
235 serial->rx_state = STATE_DATA;
236 }
237 break;
238
239 case STATE_DATA:
240 if (c == MCTP_SERIAL_ESCAPE) {
241 serial->rx_state = STATE_DATA_ESCAPED;
242 } else {
243 mctp_pktbuf_push(pkt, &c, 1);
244 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
245 serial->rx_state = STATE_WAIT_FCS1;
246 }
247 break;
248
249 case STATE_DATA_ESCAPED:
250 c ^= 0x20;
251 mctp_pktbuf_push(pkt, &c, 1);
252 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
253 serial->rx_state = STATE_WAIT_FCS1;
254 else
255 serial->rx_state = STATE_DATA;
256 break;
257
258 case STATE_WAIT_FCS1:
259 serial->rx_fcs = c << 8;
260 serial->rx_state = STATE_WAIT_FCS2;
261 break;
262 case STATE_WAIT_FCS2:
263 serial->rx_fcs |= c;
264 /* todo: check fcs */
265 serial->rx_state = STATE_WAIT_SYNC_END;
266 break;
267
268 case STATE_WAIT_SYNC_END:
269 if (c == MCTP_SERIAL_FRAMING_FLAG) {
270 mctp_serial_finish_packet(serial, true);
271 } else {
272 mctp_prdebug("missing end frame marker");
273 mctp_serial_finish_packet(serial, false);
274 }
275 serial->rx_state = STATE_WAIT_SYNC_START;
276 break;
277 }
278
279 mctp_prdebug(" -> state: %d", serial->rx_state);
280}
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600281static void mctp_rx_consume(struct mctp_binding_serial *serial, const void *buf,
282 size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800283{
284 size_t i;
285
286 for (i = 0; i < len; i++)
287 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
288}
289
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800290#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800291int mctp_serial_read(struct mctp_binding_serial *serial)
292{
293 ssize_t len;
294
295 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
296 if (len == 0)
297 return -1;
298
299 if (len < 0) {
300 mctp_prerr("can't read from serial device: %m");
301 return -1;
302 }
303
304 mctp_rx_consume(serial, serial->rxbuf, len);
305
306 return 0;
307}
308
Andrew Jeffery1111c6a2022-07-25 20:44:39 +0930309int mctp_serial_init_pollfd(struct mctp_binding_serial *serial,
310 struct pollfd *pollfd)
311{
312 pollfd->fd = serial->fd;
313 pollfd->events = POLLIN;
314
315 return 0;
316}
317
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800318int mctp_serial_open_path(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600319 const char *device)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800320{
321 serial->fd = open(device, O_RDWR);
322 if (serial->fd < 0)
323 mctp_prerr("can't open device %s: %m", device);
324
325 return 0;
326}
327
328void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
329{
330 serial->fd = fd;
331}
332#endif
333
Jeremy Kerr597b3692019-09-03 16:24:48 +0800334void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600335 mctp_serial_tx_fn fn, void *data)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800336{
337 serial->tx_fn = fn;
338 serial->tx_fn_data = data;
339}
340
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600341int mctp_serial_rx(struct mctp_binding_serial *serial, const void *buf,
342 size_t len)
Jeremy Kerr597b3692019-09-03 16:24:48 +0800343{
344 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030345 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800346}
347
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800348static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800349{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800350 mctp_binding_set_tx_enabled(binding, true);
351 return 0;
352}
353
354struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
355{
356 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800357}
358
359struct mctp_binding_serial *mctp_serial_init(void)
360{
361 struct mctp_binding_serial *serial;
362
363 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800364 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800365 serial->fd = -1;
366 serial->rx_state = STATE_WAIT_SYNC_START;
367 serial->rx_pkt = NULL;
368 serial->binding.name = "serial";
369 serial->binding.version = 1;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030370 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Andrew Jeffery39da3d02021-03-12 16:51:26 +1030371 serial->binding.pkt_header = 0;
372 serial->binding.pkt_header = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800373
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800374 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800375 serial->binding.tx = mctp_binding_serial_tx;
376
377 return serial;
378}
379
Andrew Jefferyf8b47492020-03-10 23:48:59 +1030380void mctp_serial_destroy(struct mctp_binding_serial *serial)
381{
382 __mctp_free(serial);
383}