blob: f673a52a7fc5d7b865582312b4e8df673de1d9b9 [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>
7#include <unistd.h>
8
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>
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
24#include "libmctp.h"
25#include "libmctp-alloc.h"
26#include "libmctp-log.h"
27#include "libmctp-serial.h"
28
29struct mctp_binding_serial {
30 struct mctp_binding binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080031 int fd;
32 unsigned long bus_id;
33
Jeremy Kerr597b3692019-09-03 16:24:48 +080034 mctp_serial_tx_fn tx_fn;
35 void *tx_fn_data;
36
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080037 /* receive buffer and state */
38 uint8_t rxbuf[1024];
39 struct mctp_pktbuf *rx_pkt;
40 uint8_t rx_exp_len;
41 uint16_t rx_fcs;
42 enum {
43 STATE_WAIT_SYNC_START,
44 STATE_WAIT_REVISION,
45 STATE_WAIT_LEN,
46 STATE_DATA,
47 STATE_DATA_ESCAPED,
48 STATE_WAIT_FCS1,
49 STATE_WAIT_FCS2,
50 STATE_WAIT_SYNC_END,
51 } rx_state;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080052
53 /* temporary transmit buffer */
54 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080055};
56
57#ifndef container_of
58#define container_of(ptr, type, member) \
59 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
60#endif
61
62#define binding_to_serial(b) \
63 container_of(b, struct mctp_binding_serial, binding)
64
65#define MCTP_SERIAL_REVISION 0x01
66#define MCTP_SERIAL_FRAMING_FLAG 0x7e
67#define MCTP_SERIAL_ESCAPE 0x7d
68
69struct mctp_serial_header {
70 uint8_t flag;
71 uint8_t revision;
72 uint8_t len;
73};
74
75struct mctp_serial_trailer {
76 uint8_t fcs_msb;
77 uint8_t fcs_lsb;
78 uint8_t flag;
79};
80
Jeremy Kerrc67605b2019-02-07 21:52:00 +080081static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080082{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080083 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080084 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080085 int i, j;
86
87 total_len = pkt->end - pkt->mctp_hdr_off;
88
89 p = (void *)mctp_pktbuf_hdr(pkt);
90
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080091 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080092 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080093 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080094 if (buf)
95 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080096 j++;
97 c ^= 0x20;
98 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +080099 if (buf)
100 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800101 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800102
103 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800104}
105
106static int mctp_binding_serial_tx(struct mctp_binding *b,
107 struct mctp_pktbuf *pkt)
108{
109 struct mctp_binding_serial *serial = binding_to_serial(b);
110 struct mctp_serial_header *hdr;
111 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800112 uint8_t *buf;
113 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800114
115 /* the length field in the header excludes serial framing
116 * and escape sequences */
117 len = mctp_pktbuf_size(pkt);
118
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800119 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800120 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
121 hdr->revision = MCTP_SERIAL_REVISION;
122 hdr->len = len;
123
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800124 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800125
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800126 len = mctp_serial_pkt_escape(pkt, NULL);
127 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
128 return -1;
129
130 mctp_serial_pkt_escape(pkt, buf);
131
132 buf += len;
133
134 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800135 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800136 /* todo: trailer FCS */
137 tlr->fcs_msb = 0;
138 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800139
Jeremy Kerr597b3692019-09-03 16:24:48 +0800140 len += sizeof(*hdr) + sizeof(*tlr);
141
142 if (serial->tx_fn)
143 serial->tx_fn(serial->tx_fn_data, serial->txbuf, len);
144 else
145 write(serial->fd, serial->txbuf, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800146
147 return 0;
148}
149
150static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
151 bool valid)
152{
153 struct mctp_pktbuf *pkt = serial->rx_pkt;
154 assert(pkt);
155
156 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800157 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800158
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800159 serial->rx_pkt = NULL;
160}
161
162static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
163 uint8_t len)
164{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800165 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800166}
167
168static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
169 uint8_t c)
170{
171 struct mctp_pktbuf *pkt = serial->rx_pkt;
172
173 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
174
175 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
176 serial->rx_state == STATE_WAIT_REVISION ||
177 serial->rx_state == STATE_WAIT_LEN));
178
179 switch (serial->rx_state) {
180 case STATE_WAIT_SYNC_START:
181 if (c != MCTP_SERIAL_FRAMING_FLAG) {
182 mctp_prdebug("lost sync, dropping packet");
183 if (pkt)
184 mctp_serial_finish_packet(serial, false);
185 } else {
186 serial->rx_state = STATE_WAIT_REVISION;
187 }
188 break;
189
190 case STATE_WAIT_REVISION:
191 if (c == MCTP_SERIAL_REVISION) {
192 serial->rx_state = STATE_WAIT_LEN;
193 } else {
194 mctp_prdebug("invalid revision 0x%02x", c);
195 serial->rx_state = STATE_WAIT_SYNC_START;
196 }
197 break;
198 case STATE_WAIT_LEN:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800199 if (c > serial->binding.pkt_size ||
200 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800201 mctp_prdebug("invalid size %d", c);
202 serial->rx_state = STATE_WAIT_SYNC_START;
203 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800204 mctp_serial_start_packet(serial, 0);
205 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800206 serial->rx_exp_len = c;
207 serial->rx_state = STATE_DATA;
208 }
209 break;
210
211 case STATE_DATA:
212 if (c == MCTP_SERIAL_ESCAPE) {
213 serial->rx_state = STATE_DATA_ESCAPED;
214 } else {
215 mctp_pktbuf_push(pkt, &c, 1);
216 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
217 serial->rx_state = STATE_WAIT_FCS1;
218 }
219 break;
220
221 case STATE_DATA_ESCAPED:
222 c ^= 0x20;
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 else
227 serial->rx_state = STATE_DATA;
228 break;
229
230 case STATE_WAIT_FCS1:
231 serial->rx_fcs = c << 8;
232 serial->rx_state = STATE_WAIT_FCS2;
233 break;
234 case STATE_WAIT_FCS2:
235 serial->rx_fcs |= c;
236 /* todo: check fcs */
237 serial->rx_state = STATE_WAIT_SYNC_END;
238 break;
239
240 case STATE_WAIT_SYNC_END:
241 if (c == MCTP_SERIAL_FRAMING_FLAG) {
242 mctp_serial_finish_packet(serial, true);
243 } else {
244 mctp_prdebug("missing end frame marker");
245 mctp_serial_finish_packet(serial, false);
246 }
247 serial->rx_state = STATE_WAIT_SYNC_START;
248 break;
249 }
250
251 mctp_prdebug(" -> state: %d", serial->rx_state);
252}
Jeremy Kerr597b3692019-09-03 16:24:48 +0800253static void mctp_rx_consume(struct mctp_binding_serial *serial,
254 const void *buf, size_t len)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800255{
256 size_t i;
257
258 for (i = 0; i < len; i++)
259 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
260}
261
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800262#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800263int mctp_serial_read(struct mctp_binding_serial *serial)
264{
265 ssize_t len;
266
267 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
268 if (len == 0)
269 return -1;
270
271 if (len < 0) {
272 mctp_prerr("can't read from serial device: %m");
273 return -1;
274 }
275
276 mctp_rx_consume(serial, serial->rxbuf, len);
277
278 return 0;
279}
280
281int mctp_serial_get_fd(struct mctp_binding_serial *serial)
282{
283 return serial->fd;
284}
285
286int mctp_serial_open_path(struct mctp_binding_serial *serial,
287 const char *device)
288{
289 serial->fd = open(device, O_RDWR);
290 if (serial->fd < 0)
291 mctp_prerr("can't open device %s: %m", device);
292
293 return 0;
294}
295
296void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
297{
298 serial->fd = fd;
299}
300#endif
301
Jeremy Kerr597b3692019-09-03 16:24:48 +0800302void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial,
303 mctp_serial_tx_fn fn, void *data)
304{
305 serial->tx_fn = fn;
306 serial->tx_fn_data = data;
307}
308
309int mctp_serial_rx(struct mctp_binding_serial *serial,
310 const void *buf, size_t len)
311{
312 mctp_rx_consume(serial, buf, len);
313}
314
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800315void mctp_serial_register_bus(struct mctp_binding_serial *serial,
316 struct mctp *mctp, mctp_eid_t eid)
317{
318 assert(serial->fd >= 0);
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800319 mctp_register_bus(mctp, &serial->binding, eid);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800320 mctp_binding_set_tx_enabled(&serial->binding, true);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800321}
322
323struct mctp_binding_serial *mctp_serial_init(void)
324{
325 struct mctp_binding_serial *serial;
326
327 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800328 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800329 serial->fd = -1;
330 serial->rx_state = STATE_WAIT_SYNC_START;
331 serial->rx_pkt = NULL;
332 serial->binding.name = "serial";
333 serial->binding.version = 1;
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800334 serial->binding.pkt_size = MCTP_BMTU;
335 serial->binding.pkt_pad = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800336
337 serial->binding.tx = mctp_binding_serial_tx;
338
339 return serial;
340}
341