blob: b12fcfbb7fb386f814cc77f466f3163d38b9d7ad [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>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <string.h>
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08007
Jeremy Kerrc7e764a2019-05-28 16:49:03 +08008#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080013#include <fcntl.h>
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093014#include <poll.h>
15#include <unistd.h>
Jeremy Kerr597b3692019-09-03 16:24:48 +080016#else
17static const size_t write(int fd, void *buf, size_t len)
18{
19 return -1;
20}
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080021#endif
22
23#define pr_fmt(x) "serial: " x
24
Andrew Jefferyad772b92020-01-10 16:04:18 +103025/* 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
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080038#include "libmctp.h"
39#include "libmctp-alloc.h"
40#include "libmctp-log.h"
41#include "libmctp-serial.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010042#include "container_of.h"
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080043
44struct mctp_binding_serial {
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093045 struct mctp_binding binding;
46 int fd;
47 unsigned long bus_id;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080048
Jeremy Kerr597b3692019-09-03 16:24:48 +080049 mctp_serial_tx_fn tx_fn;
50 void *tx_fn_data;
51
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080052 /* 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080067
68 /* temporary transmit buffer */
69 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080070};
71
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080072#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
79struct mctp_serial_header {
80 uint8_t flag;
81 uint8_t revision;
82 uint8_t len;
83};
84
85struct mctp_serial_trailer {
86 uint8_t fcs_msb;
87 uint8_t fcs_lsb;
88 uint8_t flag;
89};
90
Jeremy Kerrc67605b2019-02-07 21:52:00 +080091static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080092{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080093 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080094 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080095 int i, j;
96
97 total_len = pkt->end - pkt->mctp_hdr_off;
98
99 p = (void *)mctp_pktbuf_hdr(pkt);
100
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800101 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800102 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800103 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800104 if (buf)
105 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800106 j++;
107 c ^= 0x20;
108 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800109 if (buf)
110 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800111 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800112
113 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800114}
115
116static 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800122 uint8_t *buf;
123 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800124
125 /* the length field in the header excludes serial framing
126 * and escape sequences */
127 len = mctp_pktbuf_size(pkt);
128
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800129 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800130 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
131 hdr->revision = MCTP_SERIAL_REVISION;
132 hdr->len = len;
133
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800134 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800135
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800136 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;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800145 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800146 /* todo: trailer FCS */
147 tlr->fcs_msb = 0;
148 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800149
Jeremy Kerr597b3692019-09-03 16:24:48 +0800150 len += sizeof(*hdr) + sizeof(*tlr);
151
Andrew Jefferyad772b92020-01-10 16:04:18 +1030152 if (!serial->tx_fn)
153 return mctp_write_all(write, serial->fd, serial->txbuf, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800154
Andrew Jefferyad772b92020-01-10 16:04:18 +1030155 return mctp_write_all(serial->tx_fn, serial->tx_fn_data, serial->txbuf,
156 len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800157}
158
159static 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)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800166 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800167
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800168 serial->rx_pkt = NULL;
169}
170
171static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
172 uint8_t len)
173{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800174 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800175}
176
177static 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:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800208 if (c > serial->binding.pkt_size ||
209 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800210 mctp_prdebug("invalid size %d", c);
211 serial->rx_state = STATE_WAIT_SYNC_START;
212 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800213 mctp_serial_start_packet(serial, 0);
214 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800215 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}
Jeremy Kerr597b3692019-09-03 16:24:48 +0800262static void mctp_rx_consume(struct mctp_binding_serial *serial,
263 const void *buf, size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800264{
265 size_t i;
266
267 for (i = 0; i < len; i++)
268 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
269}
270
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800271#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800272int 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
290int mctp_serial_get_fd(struct mctp_binding_serial *serial)
291{
292 return serial->fd;
293}
294
Andrew Jeffery1111c6a2022-07-25 20:44:39 +0930295int mctp_serial_init_pollfd(struct mctp_binding_serial *serial,
296 struct pollfd *pollfd)
297{
298 pollfd->fd = serial->fd;
299 pollfd->events = POLLIN;
300
301 return 0;
302}
303
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800304int mctp_serial_open_path(struct mctp_binding_serial *serial,
305 const char *device)
306{
307 serial->fd = open(device, O_RDWR);
308 if (serial->fd < 0)
309 mctp_prerr("can't open device %s: %m", device);
310
311 return 0;
312}
313
314void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
315{
316 serial->fd = fd;
317}
318#endif
319
Jeremy Kerr597b3692019-09-03 16:24:48 +0800320void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
321 mctp_serial_tx_fn fn, void *data)
322{
323 serial->tx_fn = fn;
324 serial->tx_fn_data = data;
325}
326
327int mctp_serial_rx(struct mctp_binding_serial *serial,
328 const void *buf, size_t len)
329{
330 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030331 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800332}
333
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800334static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800335{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800336 mctp_binding_set_tx_enabled(binding, true);
337 return 0;
338}
339
340struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
341{
342 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800343}
344
345struct mctp_binding_serial *mctp_serial_init(void)
346{
347 struct mctp_binding_serial *serial;
348
349 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800350 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800351 serial->fd = -1;
352 serial->rx_state = STATE_WAIT_SYNC_START;
353 serial->rx_pkt = NULL;
354 serial->binding.name = "serial";
355 serial->binding.version = 1;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030356 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Andrew Jeffery39da3d02021-03-12 16:51:26 +1030357 serial->binding.pkt_header = 0;
358 serial->binding.pkt_header = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800359
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800360 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800361 serial->binding.tx = mctp_binding_serial_tx;
362
363 return serial;
364}
365
Andrew Jefferyf8b47492020-03-10 23:48:59 +1030366void mctp_serial_destroy(struct mctp_binding_serial *serial)
367{
368 __mctp_free(serial);
369}