blob: 22c1b4be92625ea93f78656b159b82009da0e97f [file] [log] [blame]
Jeremy Kerr4cdc2002019-02-07 16:49:12 +08001/* SPDX-License-Identifier: Apache-2.0 */
2
3#include <assert.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8
9#ifdef MCTP_FILEIO
10#include <fcntl.h>
11#endif
12
13#define pr_fmt(x) "serial: " x
14
15#include "libmctp.h"
16#include "libmctp-alloc.h"
17#include "libmctp-log.h"
18#include "libmctp-serial.h"
19
20struct mctp_binding_serial {
21 struct mctp_binding binding;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080022 int fd;
23 unsigned long bus_id;
24
25 /* receive buffer and state */
26 uint8_t rxbuf[1024];
27 struct mctp_pktbuf *rx_pkt;
28 uint8_t rx_exp_len;
29 uint16_t rx_fcs;
30 enum {
31 STATE_WAIT_SYNC_START,
32 STATE_WAIT_REVISION,
33 STATE_WAIT_LEN,
34 STATE_DATA,
35 STATE_DATA_ESCAPED,
36 STATE_WAIT_FCS1,
37 STATE_WAIT_FCS2,
38 STATE_WAIT_SYNC_END,
39 } rx_state;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080040
41 /* temporary transmit buffer */
42 uint8_t txbuf[256];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080043};
44
45#ifndef container_of
46#define container_of(ptr, type, member) \
47 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
48#endif
49
50#define binding_to_serial(b) \
51 container_of(b, struct mctp_binding_serial, binding)
52
53#define MCTP_SERIAL_REVISION 0x01
54#define MCTP_SERIAL_FRAMING_FLAG 0x7e
55#define MCTP_SERIAL_ESCAPE 0x7d
56
57struct mctp_serial_header {
58 uint8_t flag;
59 uint8_t revision;
60 uint8_t len;
61};
62
63struct mctp_serial_trailer {
64 uint8_t fcs_msb;
65 uint8_t fcs_lsb;
66 uint8_t flag;
67};
68
Jeremy Kerrc67605b2019-02-07 21:52:00 +080069static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf)
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080070{
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080071 uint8_t total_len;
Jeremy Kerrc67605b2019-02-07 21:52:00 +080072 uint8_t *p;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080073 int i, j;
74
75 total_len = pkt->end - pkt->mctp_hdr_off;
76
77 p = (void *)mctp_pktbuf_hdr(pkt);
78
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080079 for (i = 0, j = 0; i < total_len; i++, j++) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080080 uint8_t c = p[i];
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080081 if (c == 0x7e || c == 0x7d) {
Jeremy Kerrc67605b2019-02-07 21:52:00 +080082 if (buf)
83 buf[j] = 0x7d;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080084 j++;
85 c ^= 0x20;
86 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +080087 if (buf)
88 buf[j] = c;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080089 }
Jeremy Kerrc67605b2019-02-07 21:52:00 +080090
91 return j;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +080092}
93
94static int mctp_binding_serial_tx(struct mctp_binding *b,
95 struct mctp_pktbuf *pkt)
96{
97 struct mctp_binding_serial *serial = binding_to_serial(b);
98 struct mctp_serial_header *hdr;
99 struct mctp_serial_trailer *tlr;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800100 uint8_t *buf;
101 size_t len;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800102
103 /* the length field in the header excludes serial framing
104 * and escape sequences */
105 len = mctp_pktbuf_size(pkt);
106
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800107 hdr = (void *)serial->txbuf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800108 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
109 hdr->revision = MCTP_SERIAL_REVISION;
110 hdr->len = len;
111
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800112 buf = (void *)(hdr + 1);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800113
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800114 len = mctp_serial_pkt_escape(pkt, NULL);
115 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf))
116 return -1;
117
118 mctp_serial_pkt_escape(pkt, buf);
119
120 buf += len;
121
122 tlr = (void *)buf;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800123 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800124 /* todo: trailer FCS */
125 tlr->fcs_msb = 0;
126 tlr->fcs_lsb = 0;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800127
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800128 write(serial->fd, serial->txbuf, sizeof(*hdr) + len + sizeof(*tlr));
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800129
130 return 0;
131}
132
133static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
134 bool valid)
135{
136 struct mctp_pktbuf *pkt = serial->rx_pkt;
137 assert(pkt);
138
139 if (valid)
Jeremy Kerr0a00dca2019-03-01 08:01:35 +0800140 mctp_bus_rx(&serial->binding, pkt);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800141
142 mctp_pktbuf_free(pkt);
143 serial->rx_pkt = NULL;
144}
145
146static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
147 uint8_t len)
148{
149 serial->rx_pkt = mctp_pktbuf_alloc(len);
150}
151
152static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
153 uint8_t c)
154{
155 struct mctp_pktbuf *pkt = serial->rx_pkt;
156
157 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
158
159 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
160 serial->rx_state == STATE_WAIT_REVISION ||
161 serial->rx_state == STATE_WAIT_LEN));
162
163 switch (serial->rx_state) {
164 case STATE_WAIT_SYNC_START:
165 if (c != MCTP_SERIAL_FRAMING_FLAG) {
166 mctp_prdebug("lost sync, dropping packet");
167 if (pkt)
168 mctp_serial_finish_packet(serial, false);
169 } else {
170 serial->rx_state = STATE_WAIT_REVISION;
171 }
172 break;
173
174 case STATE_WAIT_REVISION:
175 if (c == MCTP_SERIAL_REVISION) {
176 serial->rx_state = STATE_WAIT_LEN;
177 } else {
178 mctp_prdebug("invalid revision 0x%02x", c);
179 serial->rx_state = STATE_WAIT_SYNC_START;
180 }
181 break;
182 case STATE_WAIT_LEN:
Jeremy Kerrc67605b2019-02-07 21:52:00 +0800183 if (c > (MCTP_MTU + sizeof(struct mctp_hdr))
184 || c < sizeof(struct mctp_hdr)) {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800185 mctp_prdebug("invalid size %d", c);
186 serial->rx_state = STATE_WAIT_SYNC_START;
187 } else {
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800188 mctp_serial_start_packet(serial, 0);
189 pkt = serial->rx_pkt;
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800190 serial->rx_exp_len = c;
191 serial->rx_state = STATE_DATA;
192 }
193 break;
194
195 case STATE_DATA:
196 if (c == MCTP_SERIAL_ESCAPE) {
197 serial->rx_state = STATE_DATA_ESCAPED;
198 } else {
199 mctp_pktbuf_push(pkt, &c, 1);
200 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
201 serial->rx_state = STATE_WAIT_FCS1;
202 }
203 break;
204
205 case STATE_DATA_ESCAPED:
206 c ^= 0x20;
207 mctp_pktbuf_push(pkt, &c, 1);
208 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
209 serial->rx_state = STATE_WAIT_FCS1;
210 else
211 serial->rx_state = STATE_DATA;
212 break;
213
214 case STATE_WAIT_FCS1:
215 serial->rx_fcs = c << 8;
216 serial->rx_state = STATE_WAIT_FCS2;
217 break;
218 case STATE_WAIT_FCS2:
219 serial->rx_fcs |= c;
220 /* todo: check fcs */
221 serial->rx_state = STATE_WAIT_SYNC_END;
222 break;
223
224 case STATE_WAIT_SYNC_END:
225 if (c == MCTP_SERIAL_FRAMING_FLAG) {
226 mctp_serial_finish_packet(serial, true);
227 } else {
228 mctp_prdebug("missing end frame marker");
229 mctp_serial_finish_packet(serial, false);
230 }
231 serial->rx_state = STATE_WAIT_SYNC_START;
232 break;
233 }
234
235 mctp_prdebug(" -> state: %d", serial->rx_state);
236}
237static void __attribute__((used)) mctp_rx_consume(struct mctp_binding_serial *serial,
238 void *buf, size_t len)
239{
240 size_t i;
241
242 for (i = 0; i < len; i++)
243 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
244}
245
246#ifdef MCTP_FILEIO
247int mctp_serial_read(struct mctp_binding_serial *serial)
248{
249 ssize_t len;
250
251 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
252 if (len == 0)
253 return -1;
254
255 if (len < 0) {
256 mctp_prerr("can't read from serial device: %m");
257 return -1;
258 }
259
260 mctp_rx_consume(serial, serial->rxbuf, len);
261
262 return 0;
263}
264
265int mctp_serial_get_fd(struct mctp_binding_serial *serial)
266{
267 return serial->fd;
268}
269
270int mctp_serial_open_path(struct mctp_binding_serial *serial,
271 const char *device)
272{
273 serial->fd = open(device, O_RDWR);
274 if (serial->fd < 0)
275 mctp_prerr("can't open device %s: %m", device);
276
277 return 0;
278}
279
280void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
281{
282 serial->fd = fd;
283}
284#endif
285
286void mctp_serial_register_bus(struct mctp_binding_serial *serial,
287 struct mctp *mctp, mctp_eid_t eid)
288{
289 assert(serial->fd >= 0);
Jeremy Kerr7520cec2019-03-01 07:13:18 +0800290 mctp_register_bus(mctp, &serial->binding, eid);
Jeremy Kerr1cd31182019-02-27 18:01:00 +0800291 mctp_binding_set_tx_enabled(&serial->binding, true);
Jeremy Kerr4cdc2002019-02-07 16:49:12 +0800292}
293
294struct mctp_binding_serial *mctp_serial_init(void)
295{
296 struct mctp_binding_serial *serial;
297
298 serial = __mctp_alloc(sizeof(*serial));
299 serial->fd = -1;
300 serial->rx_state = STATE_WAIT_SYNC_START;
301 serial->rx_pkt = NULL;
302 serial->binding.name = "serial";
303 serial->binding.version = 1;
304
305 serial->binding.tx = mctp_binding_serial_tx;
306
307 return serial;
308}
309