blob: 081538f37341105ba544fa7e9c2e27009608e30d [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>
15#endif
16
17#define pr_fmt(x) "serial: " x
18
19#include "libmctp.h"
20#include "libmctp-alloc.h"
21#include "libmctp-log.h"
22#include "libmctp-serial.h"
23
24struct mctp_binding_serial {
25 struct mctp_binding binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080026 int fd;
27 unsigned long bus_id;
28
29 /* receive buffer and state */
30 uint8_t rxbuf[1024];
31 struct mctp_pktbuf *rx_pkt;
32 uint8_t rx_exp_len;
33 uint16_t rx_fcs;
34 enum {
35 STATE_WAIT_SYNC_START,
36 STATE_WAIT_REVISION,
37 STATE_WAIT_LEN,
38 STATE_DATA,
39 STATE_DATA_ESCAPED,
40 STATE_WAIT_FCS1,
41 STATE_WAIT_FCS2,
42 STATE_WAIT_SYNC_END,
43 } rx_state;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080044
45 /* temporary transmit buffer */
46 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080047};
48
49#ifndef container_of
50#define container_of(ptr, type, member) \
51 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
52#endif
53
54#define binding_to_serial(b) \
55 container_of(b, struct mctp_binding_serial, binding)
56
57#define MCTP_SERIAL_REVISION 0x01
58#define MCTP_SERIAL_FRAMING_FLAG 0x7e
59#define MCTP_SERIAL_ESCAPE 0x7d
60
61struct mctp_serial_header {
62 uint8_t flag;
63 uint8_t revision;
64 uint8_t len;
65};
66
67struct mctp_serial_trailer {
68 uint8_t fcs_msb;
69 uint8_t fcs_lsb;
70 uint8_t flag;
71};
72
Jeremy Kerrc67605b2019-02-07 21:52:00 +080073static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080074{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080075 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080076 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080077 int i, j;
78
79 total_len = pkt->end - pkt->mctp_hdr_off;
80
81 p = (void *)mctp_pktbuf_hdr(pkt);
82
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080083 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080084 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080085 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080086 if (buf)
87 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080088 j++;
89 c ^= 0x20;
90 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +080091 if (buf)
92 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080093 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +080094
95 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080096}
97
98static int mctp_binding_serial_tx(struct mctp_binding *b,
99 struct mctp_pktbuf *pkt)
100{
101 struct mctp_binding_serial *serial = binding_to_serial(b);
102 struct mctp_serial_header *hdr;
103 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800104 uint8_t *buf;
105 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800106
107 /* the length field in the header excludes serial framing
108 * and escape sequences */
109 len = mctp_pktbuf_size(pkt);
110
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800111 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800112 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
113 hdr->revision = MCTP_SERIAL_REVISION;
114 hdr->len = len;
115
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800116 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800117
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800118 len = mctp_serial_pkt_escape(pkt, NULL);
119 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
120 return -1;
121
122 mctp_serial_pkt_escape(pkt, buf);
123
124 buf += len;
125
126 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800127 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800128 /* todo: trailer FCS */
129 tlr->fcs_msb = 0;
130 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800131
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800132 write(serial->fd, serial->txbuf, sizeof(*hdr) + len + sizeof(*tlr));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800133
134 return 0;
135}
136
137static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
138 bool valid)
139{
140 struct mctp_pktbuf *pkt = serial->rx_pkt;
141 assert(pkt);
142
143 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800144 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800145
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800146 serial->rx_pkt = NULL;
147}
148
149static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
150 uint8_t len)
151{
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800152 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800153}
154
155static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
156 uint8_t c)
157{
158 struct mctp_pktbuf *pkt = serial->rx_pkt;
159
160 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
161
162 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
163 serial->rx_state == STATE_WAIT_REVISION ||
164 serial->rx_state == STATE_WAIT_LEN));
165
166 switch (serial->rx_state) {
167 case STATE_WAIT_SYNC_START:
168 if (c != MCTP_SERIAL_FRAMING_FLAG) {
169 mctp_prdebug("lost sync, dropping packet");
170 if (pkt)
171 mctp_serial_finish_packet(serial, false);
172 } else {
173 serial->rx_state = STATE_WAIT_REVISION;
174 }
175 break;
176
177 case STATE_WAIT_REVISION:
178 if (c == MCTP_SERIAL_REVISION) {
179 serial->rx_state = STATE_WAIT_LEN;
180 } else {
181 mctp_prdebug("invalid revision 0x%02x", c);
182 serial->rx_state = STATE_WAIT_SYNC_START;
183 }
184 break;
185 case STATE_WAIT_LEN:
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800186 if (c > serial->binding.pkt_size ||
187 c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800188 mctp_prdebug("invalid size %d", c);
189 serial->rx_state = STATE_WAIT_SYNC_START;
190 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800191 mctp_serial_start_packet(serial, 0);
192 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800193 serial->rx_exp_len = c;
194 serial->rx_state = STATE_DATA;
195 }
196 break;
197
198 case STATE_DATA:
199 if (c == MCTP_SERIAL_ESCAPE) {
200 serial->rx_state = STATE_DATA_ESCAPED;
201 } else {
202 mctp_pktbuf_push(pkt, &c, 1);
203 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
204 serial->rx_state = STATE_WAIT_FCS1;
205 }
206 break;
207
208 case STATE_DATA_ESCAPED:
209 c ^= 0x20;
210 mctp_pktbuf_push(pkt, &c, 1);
211 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
212 serial->rx_state = STATE_WAIT_FCS1;
213 else
214 serial->rx_state = STATE_DATA;
215 break;
216
217 case STATE_WAIT_FCS1:
218 serial->rx_fcs = c << 8;
219 serial->rx_state = STATE_WAIT_FCS2;
220 break;
221 case STATE_WAIT_FCS2:
222 serial->rx_fcs |= c;
223 /* todo: check fcs */
224 serial->rx_state = STATE_WAIT_SYNC_END;
225 break;
226
227 case STATE_WAIT_SYNC_END:
228 if (c == MCTP_SERIAL_FRAMING_FLAG) {
229 mctp_serial_finish_packet(serial, true);
230 } else {
231 mctp_prdebug("missing end frame marker");
232 mctp_serial_finish_packet(serial, false);
233 }
234 serial->rx_state = STATE_WAIT_SYNC_START;
235 break;
236 }
237
238 mctp_prdebug(" -> state: %d", serial->rx_state);
239}
240static void __attribute__((used)) mctp_rx_consume(struct mctp_binding_serial *serial,
241 void *buf, size_t len)
242{
243 size_t i;
244
245 for (i = 0; i < len; i++)
246 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
247}
248
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800249#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800250int mctp_serial_read(struct mctp_binding_serial *serial)
251{
252 ssize_t len;
253
254 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
255 if (len == 0)
256 return -1;
257
258 if (len < 0) {
259 mctp_prerr("can't read from serial device: %m");
260 return -1;
261 }
262
263 mctp_rx_consume(serial, serial->rxbuf, len);
264
265 return 0;
266}
267
268int mctp_serial_get_fd(struct mctp_binding_serial *serial)
269{
270 return serial->fd;
271}
272
273int mctp_serial_open_path(struct mctp_binding_serial *serial,
274 const char *device)
275{
276 serial->fd = open(device, O_RDWR);
277 if (serial->fd < 0)
278 mctp_prerr("can't open device %s: %m", device);
279
280 return 0;
281}
282
283void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
284{
285 serial->fd = fd;
286}
287#endif
288
289void mctp_serial_register_bus(struct mctp_binding_serial *serial,
290 struct mctp *mctp, mctp_eid_t eid)
291{
292 assert(serial->fd >= 0);
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800293 mctp_register_bus(mctp, &serial->binding, eid);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800294 mctp_binding_set_tx_enabled(&serial->binding, true);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800295}
296
297struct mctp_binding_serial *mctp_serial_init(void)
298{
299 struct mctp_binding_serial *serial;
300
301 serial = __mctp_alloc(sizeof(*serial));
Jeremy Kerr0bead572019-09-02 16:45:33 +0800302 memset(serial, 0, sizeof(*serial));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800303 serial->fd = -1;
304 serial->rx_state = STATE_WAIT_SYNC_START;
305 serial->rx_pkt = NULL;
306 serial->binding.name = "serial";
307 serial->binding.version = 1;
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800308 serial->binding.pkt_size = MCTP_BMTU;
309 serial->binding.pkt_pad = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800310
311 serial->binding.tx = mctp_binding_serial_tx;
312
313 return serial;
314}
315