blob: 7c4e9e315c4c92baf358aaabcff020653c223bbc [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;
22 struct mctp *mctp;
23 int fd;
24 unsigned long bus_id;
25
26 /* receive buffer and state */
27 uint8_t rxbuf[1024];
28 struct mctp_pktbuf *rx_pkt;
29 uint8_t rx_exp_len;
30 uint16_t rx_fcs;
31 enum {
32 STATE_WAIT_SYNC_START,
33 STATE_WAIT_REVISION,
34 STATE_WAIT_LEN,
35 STATE_DATA,
36 STATE_DATA_ESCAPED,
37 STATE_WAIT_FCS1,
38 STATE_WAIT_FCS2,
39 STATE_WAIT_SYNC_END,
40 } rx_state;
41};
42
43#ifndef container_of
44#define container_of(ptr, type, member) \
45 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
46#endif
47
48#define binding_to_serial(b) \
49 container_of(b, struct mctp_binding_serial, binding)
50
51#define MCTP_SERIAL_REVISION 0x01
52#define MCTP_SERIAL_FRAMING_FLAG 0x7e
53#define MCTP_SERIAL_ESCAPE 0x7d
54
55struct mctp_serial_header {
56 uint8_t flag;
57 uint8_t revision;
58 uint8_t len;
59};
60
61struct mctp_serial_trailer {
62 uint8_t fcs_msb;
63 uint8_t fcs_lsb;
64 uint8_t flag;
65};
66
67static void mctp_serial_pkt_escape(struct mctp_pktbuf *pkt)
68{
69 uint8_t buf[MCTP_MTU + sizeof(struct mctp_hdr)], *p;
70 uint8_t total_len;
71 int i, j;
72
73 total_len = pkt->end - pkt->mctp_hdr_off;
74
75 p = (void *)mctp_pktbuf_hdr(pkt);
76
77 memcpy(buf, p, total_len);
78
79 for (i = 0, j = 0; i < total_len; i++, j++) {
80 uint8_t c = buf[i];
81 if (c == 0x7e || c == 0x7d) {
82 p[j] = 0x7d;
83 mctp_pktbuf_alloc_end(pkt, 1);
84 j++;
85 c ^= 0x20;
86 }
87 p[j] = c;
88 }
89}
90
91static int mctp_binding_serial_tx(struct mctp_binding *b,
92 struct mctp_pktbuf *pkt)
93{
94 struct mctp_binding_serial *serial = binding_to_serial(b);
95 struct mctp_serial_header *hdr;
96 struct mctp_serial_trailer *tlr;
97 uint8_t len;
98
99 /* the length field in the header excludes serial framing
100 * and escape sequences */
101 len = mctp_pktbuf_size(pkt);
102
103 hdr = mctp_pktbuf_alloc_start(pkt, 3);
104 hdr->flag = MCTP_SERIAL_FRAMING_FLAG;
105 hdr->revision = MCTP_SERIAL_REVISION;
106 hdr->len = len;
107
108 mctp_serial_pkt_escape(pkt);
109
110 tlr = mctp_pktbuf_alloc_end(pkt, 3);
111 /* todo: trailer FCS */
112 tlr->flag = MCTP_SERIAL_FRAMING_FLAG;
113
114 write(serial->fd, pkt->data + pkt->start, pkt->end - pkt->start);
115
116 return 0;
117}
118
119static void mctp_serial_finish_packet(struct mctp_binding_serial *serial,
120 bool valid)
121{
122 struct mctp_pktbuf *pkt = serial->rx_pkt;
123 assert(pkt);
124
125 if (valid)
126 mctp_bus_rx(serial->mctp, serial->bus_id, pkt);
127
128 mctp_pktbuf_free(pkt);
129 serial->rx_pkt = NULL;
130}
131
132static void mctp_serial_start_packet(struct mctp_binding_serial *serial,
133 uint8_t len)
134{
135 serial->rx_pkt = mctp_pktbuf_alloc(len);
136}
137
138static void mctp_rx_consume_one(struct mctp_binding_serial *serial,
139 uint8_t c)
140{
141 struct mctp_pktbuf *pkt = serial->rx_pkt;
142
143 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c);
144
145 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START ||
146 serial->rx_state == STATE_WAIT_REVISION ||
147 serial->rx_state == STATE_WAIT_LEN));
148
149 switch (serial->rx_state) {
150 case STATE_WAIT_SYNC_START:
151 if (c != MCTP_SERIAL_FRAMING_FLAG) {
152 mctp_prdebug("lost sync, dropping packet");
153 if (pkt)
154 mctp_serial_finish_packet(serial, false);
155 } else {
156 serial->rx_state = STATE_WAIT_REVISION;
157 }
158 break;
159
160 case STATE_WAIT_REVISION:
161 if (c == MCTP_SERIAL_REVISION) {
162 serial->rx_state = STATE_WAIT_LEN;
163 } else {
164 mctp_prdebug("invalid revision 0x%02x", c);
165 serial->rx_state = STATE_WAIT_SYNC_START;
166 }
167 break;
168 case STATE_WAIT_LEN:
169 if (c > MCTP_MTU || c < sizeof(struct mctp_hdr)) {
170 mctp_prdebug("invalid size %d", c);
171 serial->rx_state = STATE_WAIT_SYNC_START;
172 } else {
173 uint8_t *p;
174
175 mctp_serial_start_packet(serial, 0);
176 pkt = serial->rx_pkt;
177 p = mctp_pktbuf_alloc_start(pkt, 3);
178 p[0] = MCTP_SERIAL_FRAMING_FLAG;
179 p[1] = MCTP_SERIAL_REVISION;
180 p[2] = c;
181 serial->rx_exp_len = c;
182 serial->rx_state = STATE_DATA;
183 }
184 break;
185
186 case STATE_DATA:
187 if (c == MCTP_SERIAL_ESCAPE) {
188 serial->rx_state = STATE_DATA_ESCAPED;
189 } else {
190 mctp_pktbuf_push(pkt, &c, 1);
191 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
192 serial->rx_state = STATE_WAIT_FCS1;
193 }
194 break;
195
196 case STATE_DATA_ESCAPED:
197 c ^= 0x20;
198 mctp_pktbuf_push(pkt, &c, 1);
199 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len)
200 serial->rx_state = STATE_WAIT_FCS1;
201 else
202 serial->rx_state = STATE_DATA;
203 break;
204
205 case STATE_WAIT_FCS1:
206 serial->rx_fcs = c << 8;
207 serial->rx_state = STATE_WAIT_FCS2;
208 break;
209 case STATE_WAIT_FCS2:
210 serial->rx_fcs |= c;
211 /* todo: check fcs */
212 serial->rx_state = STATE_WAIT_SYNC_END;
213 break;
214
215 case STATE_WAIT_SYNC_END:
216 if (c == MCTP_SERIAL_FRAMING_FLAG) {
217 mctp_serial_finish_packet(serial, true);
218 } else {
219 mctp_prdebug("missing end frame marker");
220 mctp_serial_finish_packet(serial, false);
221 }
222 serial->rx_state = STATE_WAIT_SYNC_START;
223 break;
224 }
225
226 mctp_prdebug(" -> state: %d", serial->rx_state);
227}
228static void __attribute__((used)) mctp_rx_consume(struct mctp_binding_serial *serial,
229 void *buf, size_t len)
230{
231 size_t i;
232
233 for (i = 0; i < len; i++)
234 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i));
235}
236
237#ifdef MCTP_FILEIO
238int mctp_serial_read(struct mctp_binding_serial *serial)
239{
240 ssize_t len;
241
242 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf));
243 if (len == 0)
244 return -1;
245
246 if (len < 0) {
247 mctp_prerr("can't read from serial device: %m");
248 return -1;
249 }
250
251 mctp_rx_consume(serial, serial->rxbuf, len);
252
253 return 0;
254}
255
256int mctp_serial_get_fd(struct mctp_binding_serial *serial)
257{
258 return serial->fd;
259}
260
261int mctp_serial_open_path(struct mctp_binding_serial *serial,
262 const char *device)
263{
264 serial->fd = open(device, O_RDWR);
265 if (serial->fd < 0)
266 mctp_prerr("can't open device %s: %m", device);
267
268 return 0;
269}
270
271void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd)
272{
273 serial->fd = fd;
274}
275#endif
276
277void mctp_serial_register_bus(struct mctp_binding_serial *serial,
278 struct mctp *mctp, mctp_eid_t eid)
279{
280 assert(serial->fd >= 0);
281 serial->mctp = mctp;
282 serial->bus_id = mctp_register_bus(mctp, &serial->binding, eid);
283}
284
285struct mctp_binding_serial *mctp_serial_init(void)
286{
287 struct mctp_binding_serial *serial;
288
289 serial = __mctp_alloc(sizeof(*serial));
290 serial->fd = -1;
291 serial->rx_state = STATE_WAIT_SYNC_START;
292 serial->rx_pkt = NULL;
293 serial->binding.name = "serial";
294 serial->binding.version = 1;
295
296 serial->binding.tx = mctp_binding_serial_tx;
297
298 return serial;
299}
300