blob: 007a377168a27e2932a3de3fee47afbcc94592f6 [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 Kerrc6f676d2019-12-19 09:24:06 +080013#include <unistd.h>
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080014#include <fcntl.h>
Jeremy Kerr597b3692019-09-03 16:24:48 +080015#else
16static const size_t write(int fd, void *buf, size_t len)
17{
18 return -1;
19}
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080020#endif
21
22#define pr_fmt(x) "serial: " x
23
Andrew Jefferyad772b92020-01-10 16:04:18 +103024/* 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
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080037#include "libmctp.h"
38#include "libmctp-alloc.h"
39#include "libmctp-log.h"
40#include "libmctp-serial.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010041#include "container_of.h"
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080042
43struct mctp_binding_serial {
44 struct mctp_binding binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080045 int fd;
46 unsigned long bus_id;
47
Jeremy Kerr597b3692019-09-03 16:24:48 +080048 mctp_serial_tx_fn tx_fn;
49 void *tx_fn_data;
50
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080051 /* 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080066
67 /* temporary transmit buffer */
68 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080069};
70
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080071#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
78struct mctp_serial_header {
79 uint8_t flag;
80 uint8_t revision;
81 uint8_t len;
82};
83
84struct mctp_serial_trailer {
85 uint8_t fcs_msb;
86 uint8_t fcs_lsb;
87 uint8_t flag;
88};
89
Jeremy Kerrc67605b2019-02-07 21:52:00 +080090static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080091{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080092 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080093 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080094 int i, j;
95
96 total_len = pkt->end - pkt->mctp_hdr_off;
97
98 p = (void *)mctp_pktbuf_hdr(pkt);
99
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800100 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800101 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800102 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800103 if (buf)
104 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800105 j++;
106 c ^= 0x20;
107 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800108 if (buf)
109 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800110 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800111
112 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800113}
114
115static 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;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800121 uint8_t *buf;
122 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800123
124 /* the length field in the header excludes serial framing
125 * and escape sequences */
126 len = mctp_pktbuf_size(pkt);
127
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800128 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800129 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
130 hdr->revision = MCTP_SERIAL_REVISION;
131 hdr->len = len;
132
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800133 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800134
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800135 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;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800144 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800145 /* todo: trailer FCS */
146 tlr->fcs_msb = 0;
147 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800148
Jeremy Kerr597b3692019-09-03 16:24:48 +0800149 len += sizeof(*hdr) + sizeof(*tlr);
150
Andrew Jefferyad772b92020-01-10 16:04:18 +1030151 if (!serial->tx_fn)
152 return mctp_write_all(write, serial->fd, serial->txbuf, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800153
Andrew Jefferyad772b92020-01-10 16:04:18 +1030154 return mctp_write_all(serial->tx_fn, serial->tx_fn_data, serial->txbuf,
155 len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800156}
157
158static 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)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800165 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800166
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800167 serial->rx_pkt = NULL;
168}
169
170static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
171 uint8_t len)
172{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800173 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800174}
175
176static 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:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800207 if (c > serial->binding.pkt_size ||
208 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800209 mctp_prdebug("invalid size %d", c);
210 serial->rx_state = STATE_WAIT_SYNC_START;
211 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800212 mctp_serial_start_packet(serial, 0);
213 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800214 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}
Jeremy Kerr597b3692019-09-03 16:24:48 +0800261static void mctp_rx_consume(struct mctp_binding_serial *serial,
262 const void *buf, size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800263{
264 size_t i;
265
266 for (i = 0; i < len; i++)
267 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
268}
269
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800270#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800271int 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
289int mctp_serial_get_fd(struct mctp_binding_serial *serial)
290{
291 return serial->fd;
292}
293
294int 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
304void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
305{
306 serial->fd = fd;
307}
308#endif
309
Jeremy Kerr597b3692019-09-03 16:24:48 +0800310void 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
317int mctp_serial_rx(struct mctp_binding_serial *serial,
318 const void *buf, size_t len)
319{
320 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030321 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800322}
323
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800324static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800325{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800326 mctp_binding_set_tx_enabled(binding, true);
327 return 0;
328}
329
330struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
331{
332 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800333}
334
335struct mctp_binding_serial *mctp_serial_init(void)
336{
337 struct mctp_binding_serial *serial;
338
339 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800340 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800341 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;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030346 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800347 serial->binding.pkt_pad = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800348
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800349 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800350 serial->binding.tx = mctp_binding_serial_tx;
351
352 return serial;
353}
354
Andrew Jefferyf8b47492020-03-10 23:48:59 +1030355void mctp_serial_destroy(struct mctp_binding_serial *serial)
356{
357 __mctp_free(serial);
358}