1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 3 #include <assert.h> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #ifdef HAVE_CONFIG_H 9 #include "config.h" 10 #endif 11 12 #ifdef MCTP_HAVE_FILEIO 13 #include <unistd.h> 14 #include <fcntl.h> 15 #else 16 static const size_t write(int fd, void *buf, size_t len) 17 { 18 return -1; 19 } 20 #endif 21 22 #define pr_fmt(x) "serial: " x 23 24 /* Post-condition: All bytes written or an error has occurred */ 25 #define mctp_write_all(fn, dst, src, len) \ 26 ({ \ 27 ssize_t wrote; \ 28 while (len) { \ 29 wrote = fn(dst, src, len); \ 30 if (wrote < 0) \ 31 break; \ 32 len -= wrote; \ 33 } \ 34 len ? -1 : 0; \ 35 }) 36 37 #include "libmctp.h" 38 #include "libmctp-alloc.h" 39 #include "libmctp-log.h" 40 #include "libmctp-serial.h" 41 #include "container_of.h" 42 43 struct mctp_binding_serial { 44 struct mctp_binding binding; 45 int fd; 46 unsigned long bus_id; 47 48 mctp_serial_tx_fn tx_fn; 49 void *tx_fn_data; 50 51 /* receive buffer and state */ 52 uint8_t rxbuf[1024]; 53 struct mctp_pktbuf *rx_pkt; 54 uint8_t rx_exp_len; 55 uint16_t rx_fcs; 56 enum { 57 STATE_WAIT_SYNC_START, 58 STATE_WAIT_REVISION, 59 STATE_WAIT_LEN, 60 STATE_DATA, 61 STATE_DATA_ESCAPED, 62 STATE_WAIT_FCS1, 63 STATE_WAIT_FCS2, 64 STATE_WAIT_SYNC_END, 65 } rx_state; 66 67 /* temporary transmit buffer */ 68 uint8_t txbuf[256]; 69 }; 70 71 #define binding_to_serial(b) \ 72 container_of(b, struct mctp_binding_serial, binding) 73 74 #define MCTP_SERIAL_REVISION 0x01 75 #define MCTP_SERIAL_FRAMING_FLAG 0x7e 76 #define MCTP_SERIAL_ESCAPE 0x7d 77 78 struct mctp_serial_header { 79 uint8_t flag; 80 uint8_t revision; 81 uint8_t len; 82 }; 83 84 struct mctp_serial_trailer { 85 uint8_t fcs_msb; 86 uint8_t fcs_lsb; 87 uint8_t flag; 88 }; 89 90 static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf) 91 { 92 uint8_t total_len; 93 uint8_t *p; 94 int i, j; 95 96 total_len = pkt->end - pkt->mctp_hdr_off; 97 98 p = (void *)mctp_pktbuf_hdr(pkt); 99 100 for (i = 0, j = 0; i < total_len; i++, j++) { 101 uint8_t c = p[i]; 102 if (c == 0x7e || c == 0x7d) { 103 if (buf) 104 buf[j] = 0x7d; 105 j++; 106 c ^= 0x20; 107 } 108 if (buf) 109 buf[j] = c; 110 } 111 112 return j; 113 } 114 115 static int mctp_binding_serial_tx(struct mctp_binding *b, 116 struct mctp_pktbuf *pkt) 117 { 118 struct mctp_binding_serial *serial = binding_to_serial(b); 119 struct mctp_serial_header *hdr; 120 struct mctp_serial_trailer *tlr; 121 uint8_t *buf; 122 size_t len; 123 124 /* the length field in the header excludes serial framing 125 * and escape sequences */ 126 len = mctp_pktbuf_size(pkt); 127 128 hdr = (void *)serial->txbuf; 129 hdr->flag = MCTP_SERIAL_FRAMING_FLAG; 130 hdr->revision = MCTP_SERIAL_REVISION; 131 hdr->len = len; 132 133 buf = (void *)(hdr + 1); 134 135 len = mctp_serial_pkt_escape(pkt, NULL); 136 if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf)) 137 return -1; 138 139 mctp_serial_pkt_escape(pkt, buf); 140 141 buf += len; 142 143 tlr = (void *)buf; 144 tlr->flag = MCTP_SERIAL_FRAMING_FLAG; 145 /* todo: trailer FCS */ 146 tlr->fcs_msb = 0; 147 tlr->fcs_lsb = 0; 148 149 len += sizeof(*hdr) + sizeof(*tlr); 150 151 if (!serial->tx_fn) 152 return mctp_write_all(write, serial->fd, serial->txbuf, len); 153 154 return mctp_write_all(serial->tx_fn, serial->tx_fn_data, serial->txbuf, 155 len); 156 } 157 158 static void mctp_serial_finish_packet(struct mctp_binding_serial *serial, 159 bool valid) 160 { 161 struct mctp_pktbuf *pkt = serial->rx_pkt; 162 assert(pkt); 163 164 if (valid) 165 mctp_bus_rx(&serial->binding, pkt); 166 167 serial->rx_pkt = NULL; 168 } 169 170 static void mctp_serial_start_packet(struct mctp_binding_serial *serial, 171 uint8_t len) 172 { 173 serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len); 174 } 175 176 static void mctp_rx_consume_one(struct mctp_binding_serial *serial, 177 uint8_t c) 178 { 179 struct mctp_pktbuf *pkt = serial->rx_pkt; 180 181 mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c); 182 183 assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START || 184 serial->rx_state == STATE_WAIT_REVISION || 185 serial->rx_state == STATE_WAIT_LEN)); 186 187 switch (serial->rx_state) { 188 case STATE_WAIT_SYNC_START: 189 if (c != MCTP_SERIAL_FRAMING_FLAG) { 190 mctp_prdebug("lost sync, dropping packet"); 191 if (pkt) 192 mctp_serial_finish_packet(serial, false); 193 } else { 194 serial->rx_state = STATE_WAIT_REVISION; 195 } 196 break; 197 198 case STATE_WAIT_REVISION: 199 if (c == MCTP_SERIAL_REVISION) { 200 serial->rx_state = STATE_WAIT_LEN; 201 } else { 202 mctp_prdebug("invalid revision 0x%02x", c); 203 serial->rx_state = STATE_WAIT_SYNC_START; 204 } 205 break; 206 case STATE_WAIT_LEN: 207 if (c > serial->binding.pkt_size || 208 c < sizeof(struct mctp_hdr)) { 209 mctp_prdebug("invalid size %d", c); 210 serial->rx_state = STATE_WAIT_SYNC_START; 211 } else { 212 mctp_serial_start_packet(serial, 0); 213 pkt = serial->rx_pkt; 214 serial->rx_exp_len = c; 215 serial->rx_state = STATE_DATA; 216 } 217 break; 218 219 case STATE_DATA: 220 if (c == MCTP_SERIAL_ESCAPE) { 221 serial->rx_state = STATE_DATA_ESCAPED; 222 } else { 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 } 227 break; 228 229 case STATE_DATA_ESCAPED: 230 c ^= 0x20; 231 mctp_pktbuf_push(pkt, &c, 1); 232 if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len) 233 serial->rx_state = STATE_WAIT_FCS1; 234 else 235 serial->rx_state = STATE_DATA; 236 break; 237 238 case STATE_WAIT_FCS1: 239 serial->rx_fcs = c << 8; 240 serial->rx_state = STATE_WAIT_FCS2; 241 break; 242 case STATE_WAIT_FCS2: 243 serial->rx_fcs |= c; 244 /* todo: check fcs */ 245 serial->rx_state = STATE_WAIT_SYNC_END; 246 break; 247 248 case STATE_WAIT_SYNC_END: 249 if (c == MCTP_SERIAL_FRAMING_FLAG) { 250 mctp_serial_finish_packet(serial, true); 251 } else { 252 mctp_prdebug("missing end frame marker"); 253 mctp_serial_finish_packet(serial, false); 254 } 255 serial->rx_state = STATE_WAIT_SYNC_START; 256 break; 257 } 258 259 mctp_prdebug(" -> state: %d", serial->rx_state); 260 } 261 static void mctp_rx_consume(struct mctp_binding_serial *serial, 262 const void *buf, size_t len) 263 { 264 size_t i; 265 266 for (i = 0; i < len; i++) 267 mctp_rx_consume_one(serial, *(uint8_t *)(buf + i)); 268 } 269 270 #ifdef MCTP_HAVE_FILEIO 271 int mctp_serial_read(struct mctp_binding_serial *serial) 272 { 273 ssize_t len; 274 275 len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf)); 276 if (len == 0) 277 return -1; 278 279 if (len < 0) { 280 mctp_prerr("can't read from serial device: %m"); 281 return -1; 282 } 283 284 mctp_rx_consume(serial, serial->rxbuf, len); 285 286 return 0; 287 } 288 289 int mctp_serial_get_fd(struct mctp_binding_serial *serial) 290 { 291 return serial->fd; 292 } 293 294 int mctp_serial_open_path(struct mctp_binding_serial *serial, 295 const char *device) 296 { 297 serial->fd = open(device, O_RDWR); 298 if (serial->fd < 0) 299 mctp_prerr("can't open device %s: %m", device); 300 301 return 0; 302 } 303 304 void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd) 305 { 306 serial->fd = fd; 307 } 308 #endif 309 310 void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial, 311 mctp_serial_tx_fn fn, void *data) 312 { 313 serial->tx_fn = fn; 314 serial->tx_fn_data = data; 315 } 316 317 int mctp_serial_rx(struct mctp_binding_serial *serial, 318 const void *buf, size_t len) 319 { 320 mctp_rx_consume(serial, buf, len); 321 return 0; 322 } 323 324 static int mctp_serial_core_start(struct mctp_binding *binding) 325 { 326 mctp_binding_set_tx_enabled(binding, true); 327 return 0; 328 } 329 330 struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b) 331 { 332 return &b->binding; 333 } 334 335 struct mctp_binding_serial *mctp_serial_init(void) 336 { 337 struct mctp_binding_serial *serial; 338 339 serial = __mctp_alloc(sizeof(*serial)); 340 memset(serial, 0, sizeof(*serial)); 341 serial->fd = -1; 342 serial->rx_state = STATE_WAIT_SYNC_START; 343 serial->rx_pkt = NULL; 344 serial->binding.name = "serial"; 345 serial->binding.version = 1; 346 serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); 347 serial->binding.pkt_pad = 0; 348 349 serial->binding.start = mctp_serial_core_start; 350 serial->binding.tx = mctp_binding_serial_tx; 351 352 return serial; 353 } 354 355 void mctp_serial_destroy(struct mctp_binding_serial *serial) 356 { 357 __mctp_free(serial); 358 } 359