blob: 2d955cbe0028753b7e065e24d9d19dad0d43c492 [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
146 mctp_pktbuf_free(pkt);
147 serial->rx_pkt = NULL;
148}
149
150static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
151 uint8_t len)
152{
153 serial->rx_pkt = mctp_pktbuf_alloc(len);
154}
155
156static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
157 uint8_t c)
158{
159 struct mctp_pktbuf *pkt = serial->rx_pkt;
160
161 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
162
163 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
164 serial->rx_state == STATE_WAIT_REVISION ||
165 serial->rx_state == STATE_WAIT_LEN));
166
167 switch (serial->rx_state) {
168 case STATE_WAIT_SYNC_START:
169 if (c != MCTP_SERIAL_FRAMING_FLAG) {
170 mctp_prdebug("lost sync, dropping packet");
171 if (pkt)
172 mctp_serial_finish_packet(serial, false);
173 } else {
174 serial->rx_state = STATE_WAIT_REVISION;
175 }
176 break;
177
178 case STATE_WAIT_REVISION:
179 if (c == MCTP_SERIAL_REVISION) {
180 serial->rx_state = STATE_WAIT_LEN;
181 } else {
182 mctp_prdebug("invalid revision 0x%02x", c);
183 serial->rx_state = STATE_WAIT_SYNC_START;
184 }
185 break;
186 case STATE_WAIT_LEN:
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800187 if (c > (MCTP_MTU + sizeof(struct mctp_hdr))
188 || c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800189 mctp_prdebug("invalid size %d", c);
190 serial->rx_state = STATE_WAIT_SYNC_START;
191 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800192 mctp_serial_start_packet(serial, 0);
193 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800194 serial->rx_exp_len = c;
195 serial->rx_state = STATE_DATA;
196 }
197 break;
198
199 case STATE_DATA:
200 if (c == MCTP_SERIAL_ESCAPE) {
201 serial->rx_state = STATE_DATA_ESCAPED;
202 } else {
203 mctp_pktbuf_push(pkt, &c, 1);
204 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
205 serial->rx_state = STATE_WAIT_FCS1;
206 }
207 break;
208
209 case STATE_DATA_ESCAPED:
210 c ^= 0x20;
211 mctp_pktbuf_push(pkt, &c, 1);
212 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
213 serial->rx_state = STATE_WAIT_FCS1;
214 else
215 serial->rx_state = STATE_DATA;
216 break;
217
218 case STATE_WAIT_FCS1:
219 serial->rx_fcs = c << 8;
220 serial->rx_state = STATE_WAIT_FCS2;
221 break;
222 case STATE_WAIT_FCS2:
223 serial->rx_fcs |= c;
224 /* todo: check fcs */
225 serial->rx_state = STATE_WAIT_SYNC_END;
226 break;
227
228 case STATE_WAIT_SYNC_END:
229 if (c == MCTP_SERIAL_FRAMING_FLAG) {
230 mctp_serial_finish_packet(serial, true);
231 } else {
232 mctp_prdebug("missing end frame marker");
233 mctp_serial_finish_packet(serial, false);
234 }
235 serial->rx_state = STATE_WAIT_SYNC_START;
236 break;
237 }
238
239 mctp_prdebug(" -> state: %d", serial->rx_state);
240}
241static void __attribute__((used)) mctp_rx_consume(struct mctp_binding_serial *serial,
242 void *buf, size_t len)
243{
244 size_t i;
245
246 for (i = 0; i < len; i++)
247 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
248}
249
Jeremy Kerrc7e764a2019-05-28 16:49:03 +0800250#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800251int mctp_serial_read(struct mctp_binding_serial *serial)
252{
253 ssize_t len;
254
255 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
256 if (len == 0)
257 return -1;
258
259 if (len < 0) {
260 mctp_prerr("can't read from serial device: %m");
261 return -1;
262 }
263
264 mctp_rx_consume(serial, serial->rxbuf, len);
265
266 return 0;
267}
268
269int mctp_serial_get_fd(struct mctp_binding_serial *serial)
270{
271 return serial->fd;
272}
273
274int mctp_serial_open_path(struct mctp_binding_serial *serial,
275 const char *device)
276{
277 serial->fd = open(device, O_RDWR);
278 if (serial->fd < 0)
279 mctp_prerr("can't open device %s: %m", device);
280
281 return 0;
282}
283
284void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
285{
286 serial->fd = fd;
287}
288#endif
289
290void mctp_serial_register_bus(struct mctp_binding_serial *serial,
291 struct mctp *mctp, mctp_eid_t eid)
292{
293 assert(serial->fd >= 0);
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800294 mctp_register_bus(mctp, &serial->binding, eid);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800295 mctp_binding_set_tx_enabled(&serial->binding, true);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800296}
297
298struct mctp_binding_serial *mctp_serial_init(void)
299{
300 struct mctp_binding_serial *serial;
301
302 serial = __mctp_alloc(sizeof(*serial));
303 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;
308
309 serial->binding.tx = mctp_binding_serial_tx;
310
311 return serial;
312}
313