blob: c90de544720267cb7d0ddbb2c94cfe7de80b3a87 [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"
41
42struct mctp_binding_serial {
43 struct mctp_binding binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080044 int fd;
45 unsigned long bus_id;
46
Jeremy Kerr597b3692019-09-03 16:24:48 +080047 mctp_serial_tx_fn tx_fn;
48 void *tx_fn_data;
49
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080050 /* receive buffer and state */
51 uint8_t rxbuf[1024];
52 struct mctp_pktbuf *rx_pkt;
53 uint8_t rx_exp_len;
54 uint16_t rx_fcs;
55 enum {
56 STATE_WAIT_SYNC_START,
57 STATE_WAIT_REVISION,
58 STATE_WAIT_LEN,
59 STATE_DATA,
60 STATE_DATA_ESCAPED,
61 STATE_WAIT_FCS1,
62 STATE_WAIT_FCS2,
63 STATE_WAIT_SYNC_END,
64 } rx_state;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080065
66 /* temporary transmit buffer */
67 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080068};
69
70#ifndef container_of
71#define container_of(ptr, type, member) \
Andrew Jeffery1e0af042020-01-10 15:58:28 +103072 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080073#endif
74
75#define binding_to_serial(b) \
76 container_of(b, struct mctp_binding_serial, binding)
77
78#define MCTP_SERIAL_REVISION 0x01
79#define MCTP_SERIAL_FRAMING_FLAG 0x7e
80#define MCTP_SERIAL_ESCAPE 0x7d
81
82struct mctp_serial_header {
83 uint8_t flag;
84 uint8_t revision;
85 uint8_t len;
86};
87
88struct mctp_serial_trailer {
89 uint8_t fcs_msb;
90 uint8_t fcs_lsb;
91 uint8_t flag;
92};
93
Jeremy Kerrc67605b2019-02-07 21:52:00 +080094static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080095{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080096 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080097 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080098 int i, j;
99
100 total_len = pkt->end - pkt->mctp_hdr_off;
101
102 p = (void *)mctp_pktbuf_hdr(pkt);
103
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800104 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800105 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800106 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800107 if (buf)
108 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800109 j++;
110 c ^= 0x20;
111 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800112 if (buf)
113 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800114 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800115
116 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800117}
118
119static int mctp_binding_serial_tx(struct mctp_binding *b,
120 struct mctp_pktbuf *pkt)
121{
122 struct mctp_binding_serial *serial = binding_to_serial(b);
123 struct mctp_serial_header *hdr;
124 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800125 uint8_t *buf;
126 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800127
128 /* the length field in the header excludes serial framing
129 * and escape sequences */
130 len = mctp_pktbuf_size(pkt);
131
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800132 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800133 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
134 hdr->revision = MCTP_SERIAL_REVISION;
135 hdr->len = len;
136
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800137 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800138
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800139 len = mctp_serial_pkt_escape(pkt, NULL);
140 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
141 return -1;
142
143 mctp_serial_pkt_escape(pkt, buf);
144
145 buf += len;
146
147 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800148 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800149 /* todo: trailer FCS */
150 tlr->fcs_msb = 0;
151 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800152
Jeremy Kerr597b3692019-09-03 16:24:48 +0800153 len += sizeof(*hdr) + sizeof(*tlr);
154
Andrew Jefferyad772b92020-01-10 16:04:18 +1030155 if (!serial->tx_fn)
156 return mctp_write_all(write, serial->fd, serial->txbuf, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800157
Andrew Jefferyad772b92020-01-10 16:04:18 +1030158 return mctp_write_all(serial->tx_fn, serial->tx_fn_data, serial->txbuf,
159 len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800160}
161
162static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
163 bool valid)
164{
165 struct mctp_pktbuf *pkt = serial->rx_pkt;
166 assert(pkt);
167
168 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800169 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800170
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800171 serial->rx_pkt = NULL;
172}
173
174static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
175 uint8_t len)
176{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800177 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800178}
179
180static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
181 uint8_t c)
182{
183 struct mctp_pktbuf *pkt = serial->rx_pkt;
184
185 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
186
187 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
188 serial->rx_state == STATE_WAIT_REVISION ||
189 serial->rx_state == STATE_WAIT_LEN));
190
191 switch (serial->rx_state) {
192 case STATE_WAIT_SYNC_START:
193 if (c != MCTP_SERIAL_FRAMING_FLAG) {
194 mctp_prdebug("lost sync, dropping packet");
195 if (pkt)
196 mctp_serial_finish_packet(serial, false);
197 } else {
198 serial->rx_state = STATE_WAIT_REVISION;
199 }
200 break;
201
202 case STATE_WAIT_REVISION:
203 if (c == MCTP_SERIAL_REVISION) {
204 serial->rx_state = STATE_WAIT_LEN;
205 } else {
206 mctp_prdebug("invalid revision 0x%02x", c);
207 serial->rx_state = STATE_WAIT_SYNC_START;
208 }
209 break;
210 case STATE_WAIT_LEN:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800211 if (c > serial->binding.pkt_size ||
212 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800213 mctp_prdebug("invalid size %d", c);
214 serial->rx_state = STATE_WAIT_SYNC_START;
215 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800216 mctp_serial_start_packet(serial, 0);
217 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800218 serial->rx_exp_len = c;
219 serial->rx_state = STATE_DATA;
220 }
221 break;
222
223 case STATE_DATA:
224 if (c == MCTP_SERIAL_ESCAPE) {
225 serial->rx_state = STATE_DATA_ESCAPED;
226 } else {
227 mctp_pktbuf_push(pkt, &c, 1);
228 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
229 serial->rx_state = STATE_WAIT_FCS1;
230 }
231 break;
232
233 case STATE_DATA_ESCAPED:
234 c ^= 0x20;
235 mctp_pktbuf_push(pkt, &c, 1);
236 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
237 serial->rx_state = STATE_WAIT_FCS1;
238 else
239 serial->rx_state = STATE_DATA;
240 break;
241
242 case STATE_WAIT_FCS1:
243 serial->rx_fcs = c << 8;
244 serial->rx_state = STATE_WAIT_FCS2;
245 break;
246 case STATE_WAIT_FCS2:
247 serial->rx_fcs |= c;
248 /* todo: check fcs */
249 serial->rx_state = STATE_WAIT_SYNC_END;
250 break;
251
252 case STATE_WAIT_SYNC_END:
253 if (c == MCTP_SERIAL_FRAMING_FLAG) {
254 mctp_serial_finish_packet(serial, true);
255 } else {
256 mctp_prdebug("missing end frame marker");
257 mctp_serial_finish_packet(serial, false);
258 }
259 serial->rx_state = STATE_WAIT_SYNC_START;
260 break;
261 }
262
263 mctp_prdebug(" -> state: %d", serial->rx_state);
264}
Jeremy Kerr597b3692019-09-03 16:24:48 +0800265static void mctp_rx_consume(struct mctp_binding_serial *serial,
266 const void *buf, size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800267{
268 size_t i;
269
270 for (i = 0; i < len; i++)
271 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
272}
273
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800274#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800275int mctp_serial_read(struct mctp_binding_serial *serial)
276{
277 ssize_t len;
278
279 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
280 if (len == 0)
281 return -1;
282
283 if (len < 0) {
284 mctp_prerr("can't read from serial device: %m");
285 return -1;
286 }
287
288 mctp_rx_consume(serial, serial->rxbuf, len);
289
290 return 0;
291}
292
293int mctp_serial_get_fd(struct mctp_binding_serial *serial)
294{
295 return serial->fd;
296}
297
298int mctp_serial_open_path(struct mctp_binding_serial *serial,
299 const char *device)
300{
301 serial->fd = open(device, O_RDWR);
302 if (serial->fd < 0)
303 mctp_prerr("can't open device %s: %m", device);
304
305 return 0;
306}
307
308void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
309{
310 serial->fd = fd;
311}
312#endif
313
Jeremy Kerr597b3692019-09-03 16:24:48 +0800314void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
315 mctp_serial_tx_fn fn, void *data)
316{
317 serial->tx_fn = fn;
318 serial->tx_fn_data = data;
319}
320
321int mctp_serial_rx(struct mctp_binding_serial *serial,
322 const void *buf, size_t len)
323{
324 mctp_rx_consume(serial, buf, len);
Andrew Jeffery00e67702020-01-24 12:25:50 +1030325 return 0;
Jeremy Kerr597b3692019-09-03 16:24:48 +0800326}
327
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800328static int mctp_serial_core_start(struct mctp_binding *binding)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800329{
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800330 mctp_binding_set_tx_enabled(binding, true);
331 return 0;
332}
333
334struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b)
335{
336 return &b->binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800337}
338
339struct mctp_binding_serial *mctp_serial_init(void)
340{
341 struct mctp_binding_serial *serial;
342
343 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800344 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800345 serial->fd = -1;
346 serial->rx_state = STATE_WAIT_SYNC_START;
347 serial->rx_pkt = NULL;
348 serial->binding.name = "serial";
349 serial->binding.version = 1;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030350 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800351 serial->binding.pkt_pad = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800352
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800353 serial->binding.start = mctp_serial_core_start;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800354 serial->binding.tx = mctp_binding_serial_tx;
355
356 return serial;
357}
358