mctp-serial.c (11357B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Management Component Transport Protocol (MCTP) - serial transport 4 * binding. This driver is an implementation of the DMTF specificiation 5 * "DSP0253 - Management Component Transport Protocol (MCTP) Serial Transport 6 * Binding", available at: 7 * 8 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0253_1.0.0.pdf 9 * 10 * This driver provides DSP0253-type MCTP-over-serial transport using a Linux 11 * tty device, by setting the N_MCTP line discipline on the tty. 12 * 13 * Copyright (c) 2021 Code Construct 14 */ 15 16#include <linux/idr.h> 17#include <linux/if_arp.h> 18#include <linux/module.h> 19#include <linux/skbuff.h> 20#include <linux/tty.h> 21#include <linux/workqueue.h> 22#include <linux/crc-ccitt.h> 23 24#include <linux/mctp.h> 25#include <net/mctp.h> 26#include <net/pkt_sched.h> 27 28#define MCTP_SERIAL_MTU 68 /* base mtu (64) + mctp header */ 29#define MCTP_SERIAL_FRAME_MTU (MCTP_SERIAL_MTU + 6) /* + serial framing */ 30 31#define MCTP_SERIAL_VERSION 0x1 /* DSP0253 defines a single version: 1 */ 32 33#define BUFSIZE MCTP_SERIAL_FRAME_MTU 34 35#define BYTE_FRAME 0x7e 36#define BYTE_ESC 0x7d 37 38static DEFINE_IDA(mctp_serial_ida); 39 40enum mctp_serial_state { 41 STATE_IDLE, 42 STATE_START, 43 STATE_HEADER, 44 STATE_DATA, 45 STATE_ESCAPE, 46 STATE_TRAILER, 47 STATE_DONE, 48 STATE_ERR, 49}; 50 51struct mctp_serial { 52 struct net_device *netdev; 53 struct tty_struct *tty; 54 55 int idx; 56 57 /* protects our rx & tx state machines; held during both paths */ 58 spinlock_t lock; 59 60 struct work_struct tx_work; 61 enum mctp_serial_state txstate, rxstate; 62 u16 txfcs, rxfcs, rxfcs_rcvd; 63 unsigned int txlen, rxlen; 64 unsigned int txpos, rxpos; 65 unsigned char txbuf[BUFSIZE], 66 rxbuf[BUFSIZE]; 67}; 68 69static bool needs_escape(unsigned char c) 70{ 71 return c == BYTE_ESC || c == BYTE_FRAME; 72} 73 74static int next_chunk_len(struct mctp_serial *dev) 75{ 76 int i; 77 78 /* either we have no bytes to send ... */ 79 if (dev->txpos == dev->txlen) 80 return 0; 81 82 /* ... or the next byte to send is an escaped byte; requiring a 83 * single-byte chunk... 84 */ 85 if (needs_escape(dev->txbuf[dev->txpos])) 86 return 1; 87 88 /* ... or we have one or more bytes up to the next escape - this chunk 89 * will be those non-escaped bytes, and does not include the escaped 90 * byte. 91 */ 92 for (i = 1; i + dev->txpos + 1 < dev->txlen; i++) { 93 if (needs_escape(dev->txbuf[dev->txpos + i + 1])) 94 break; 95 } 96 97 return i; 98} 99 100static int write_chunk(struct mctp_serial *dev, unsigned char *buf, int len) 101{ 102 return dev->tty->ops->write(dev->tty, buf, len); 103} 104 105static void mctp_serial_tx_work(struct work_struct *work) 106{ 107 struct mctp_serial *dev = container_of(work, struct mctp_serial, 108 tx_work); 109 unsigned char c, buf[3]; 110 unsigned long flags; 111 int len, txlen; 112 113 spin_lock_irqsave(&dev->lock, flags); 114 115 /* txstate represents the next thing to send */ 116 switch (dev->txstate) { 117 case STATE_START: 118 dev->txpos = 0; 119 fallthrough; 120 case STATE_HEADER: 121 buf[0] = BYTE_FRAME; 122 buf[1] = MCTP_SERIAL_VERSION; 123 buf[2] = dev->txlen; 124 125 if (!dev->txpos) 126 dev->txfcs = crc_ccitt(0, buf + 1, 2); 127 128 txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos); 129 if (txlen <= 0) { 130 dev->txstate = STATE_ERR; 131 } else { 132 dev->txpos += txlen; 133 if (dev->txpos == 3) { 134 dev->txstate = STATE_DATA; 135 dev->txpos = 0; 136 } 137 } 138 break; 139 140 case STATE_ESCAPE: 141 buf[0] = dev->txbuf[dev->txpos] & ~0x20; 142 txlen = write_chunk(dev, buf, 1); 143 if (txlen <= 0) { 144 dev->txstate = STATE_ERR; 145 } else { 146 dev->txpos += txlen; 147 if (dev->txpos == dev->txlen) { 148 dev->txstate = STATE_TRAILER; 149 dev->txpos = 0; 150 } 151 } 152 153 break; 154 155 case STATE_DATA: 156 len = next_chunk_len(dev); 157 if (len) { 158 c = dev->txbuf[dev->txpos]; 159 if (len == 1 && needs_escape(c)) { 160 buf[0] = BYTE_ESC; 161 buf[1] = c & ~0x20; 162 dev->txfcs = crc_ccitt_byte(dev->txfcs, c); 163 txlen = write_chunk(dev, buf, 2); 164 if (txlen == 2) 165 dev->txpos++; 166 else if (txlen == 1) 167 dev->txstate = STATE_ESCAPE; 168 else 169 dev->txstate = STATE_ERR; 170 } else { 171 txlen = write_chunk(dev, 172 dev->txbuf + dev->txpos, 173 len); 174 if (txlen <= 0) { 175 dev->txstate = STATE_ERR; 176 } else { 177 dev->txfcs = crc_ccitt(dev->txfcs, 178 dev->txbuf + 179 dev->txpos, 180 txlen); 181 dev->txpos += txlen; 182 } 183 } 184 if (dev->txstate == STATE_DATA && 185 dev->txpos == dev->txlen) { 186 dev->txstate = STATE_TRAILER; 187 dev->txpos = 0; 188 } 189 break; 190 } 191 dev->txstate = STATE_TRAILER; 192 dev->txpos = 0; 193 fallthrough; 194 195 case STATE_TRAILER: 196 buf[0] = dev->txfcs >> 8; 197 buf[1] = dev->txfcs & 0xff; 198 buf[2] = BYTE_FRAME; 199 txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos); 200 if (txlen <= 0) { 201 dev->txstate = STATE_ERR; 202 } else { 203 dev->txpos += txlen; 204 if (dev->txpos == 3) { 205 dev->txstate = STATE_DONE; 206 dev->txpos = 0; 207 } 208 } 209 break; 210 default: 211 netdev_err_once(dev->netdev, "invalid tx state %d\n", 212 dev->txstate); 213 } 214 215 if (dev->txstate == STATE_DONE) { 216 dev->netdev->stats.tx_packets++; 217 dev->netdev->stats.tx_bytes += dev->txlen; 218 dev->txlen = 0; 219 dev->txpos = 0; 220 clear_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags); 221 dev->txstate = STATE_IDLE; 222 spin_unlock_irqrestore(&dev->lock, flags); 223 224 netif_wake_queue(dev->netdev); 225 } else { 226 spin_unlock_irqrestore(&dev->lock, flags); 227 } 228} 229 230static netdev_tx_t mctp_serial_tx(struct sk_buff *skb, struct net_device *ndev) 231{ 232 struct mctp_serial *dev = netdev_priv(ndev); 233 unsigned long flags; 234 235 WARN_ON(dev->txstate != STATE_IDLE); 236 237 if (skb->len > MCTP_SERIAL_MTU) { 238 dev->netdev->stats.tx_dropped++; 239 goto out; 240 } 241 242 spin_lock_irqsave(&dev->lock, flags); 243 netif_stop_queue(dev->netdev); 244 skb_copy_bits(skb, 0, dev->txbuf, skb->len); 245 dev->txpos = 0; 246 dev->txlen = skb->len; 247 dev->txstate = STATE_START; 248 spin_unlock_irqrestore(&dev->lock, flags); 249 250 set_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags); 251 schedule_work(&dev->tx_work); 252 253out: 254 kfree_skb(skb); 255 return NETDEV_TX_OK; 256} 257 258static void mctp_serial_tty_write_wakeup(struct tty_struct *tty) 259{ 260 struct mctp_serial *dev = tty->disc_data; 261 262 schedule_work(&dev->tx_work); 263} 264 265static void mctp_serial_rx(struct mctp_serial *dev) 266{ 267 struct mctp_skb_cb *cb; 268 struct sk_buff *skb; 269 270 if (dev->rxfcs != dev->rxfcs_rcvd) { 271 dev->netdev->stats.rx_dropped++; 272 dev->netdev->stats.rx_crc_errors++; 273 return; 274 } 275 276 skb = netdev_alloc_skb(dev->netdev, dev->rxlen); 277 if (!skb) { 278 dev->netdev->stats.rx_dropped++; 279 return; 280 } 281 282 skb->protocol = htons(ETH_P_MCTP); 283 skb_put_data(skb, dev->rxbuf, dev->rxlen); 284 skb_reset_network_header(skb); 285 286 cb = __mctp_cb(skb); 287 cb->halen = 0; 288 289 netif_rx(skb); 290 dev->netdev->stats.rx_packets++; 291 dev->netdev->stats.rx_bytes += dev->rxlen; 292} 293 294static void mctp_serial_push_header(struct mctp_serial *dev, unsigned char c) 295{ 296 switch (dev->rxpos) { 297 case 0: 298 if (c == BYTE_FRAME) 299 dev->rxpos++; 300 else 301 dev->rxstate = STATE_ERR; 302 break; 303 case 1: 304 if (c == MCTP_SERIAL_VERSION) { 305 dev->rxpos++; 306 dev->rxfcs = crc_ccitt_byte(0, c); 307 } else { 308 dev->rxstate = STATE_ERR; 309 } 310 break; 311 case 2: 312 if (c > MCTP_SERIAL_FRAME_MTU) { 313 dev->rxstate = STATE_ERR; 314 } else { 315 dev->rxlen = c; 316 dev->rxpos = 0; 317 dev->rxstate = STATE_DATA; 318 dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); 319 } 320 break; 321 } 322} 323 324static void mctp_serial_push_trailer(struct mctp_serial *dev, unsigned char c) 325{ 326 switch (dev->rxpos) { 327 case 0: 328 dev->rxfcs_rcvd = c << 8; 329 dev->rxpos++; 330 break; 331 case 1: 332 dev->rxfcs_rcvd |= c; 333 dev->rxpos++; 334 break; 335 case 2: 336 if (c != BYTE_FRAME) { 337 dev->rxstate = STATE_ERR; 338 } else { 339 mctp_serial_rx(dev); 340 dev->rxlen = 0; 341 dev->rxpos = 0; 342 dev->rxstate = STATE_IDLE; 343 } 344 break; 345 } 346} 347 348static void mctp_serial_push(struct mctp_serial *dev, unsigned char c) 349{ 350 switch (dev->rxstate) { 351 case STATE_IDLE: 352 dev->rxstate = STATE_HEADER; 353 fallthrough; 354 case STATE_HEADER: 355 mctp_serial_push_header(dev, c); 356 break; 357 358 case STATE_ESCAPE: 359 c |= 0x20; 360 fallthrough; 361 case STATE_DATA: 362 if (dev->rxstate != STATE_ESCAPE && c == BYTE_ESC) { 363 dev->rxstate = STATE_ESCAPE; 364 } else { 365 dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); 366 dev->rxbuf[dev->rxpos] = c; 367 dev->rxpos++; 368 dev->rxstate = STATE_DATA; 369 if (dev->rxpos == dev->rxlen) { 370 dev->rxpos = 0; 371 dev->rxstate = STATE_TRAILER; 372 } 373 } 374 break; 375 376 case STATE_TRAILER: 377 mctp_serial_push_trailer(dev, c); 378 break; 379 380 case STATE_ERR: 381 if (c == BYTE_FRAME) 382 dev->rxstate = STATE_IDLE; 383 break; 384 385 default: 386 netdev_err_once(dev->netdev, "invalid rx state %d\n", 387 dev->rxstate); 388 } 389} 390 391static void mctp_serial_tty_receive_buf(struct tty_struct *tty, 392 const unsigned char *c, 393 const char *f, int len) 394{ 395 struct mctp_serial *dev = tty->disc_data; 396 int i; 397 398 if (!netif_running(dev->netdev)) 399 return; 400 401 /* we don't (currently) use the flag bytes, just data. */ 402 for (i = 0; i < len; i++) 403 mctp_serial_push(dev, c[i]); 404} 405 406static void mctp_serial_uninit(struct net_device *ndev) 407{ 408 struct mctp_serial *dev = netdev_priv(ndev); 409 410 cancel_work_sync(&dev->tx_work); 411} 412 413static const struct net_device_ops mctp_serial_netdev_ops = { 414 .ndo_start_xmit = mctp_serial_tx, 415 .ndo_uninit = mctp_serial_uninit, 416}; 417 418static void mctp_serial_setup(struct net_device *ndev) 419{ 420 ndev->type = ARPHRD_MCTP; 421 422 /* we limit at the fixed MTU, which is also the MCTP-standard 423 * baseline MTU, so is also our minimum 424 */ 425 ndev->mtu = MCTP_SERIAL_MTU; 426 ndev->max_mtu = MCTP_SERIAL_MTU; 427 ndev->min_mtu = MCTP_SERIAL_MTU; 428 429 ndev->hard_header_len = 0; 430 ndev->addr_len = 0; 431 ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 432 ndev->flags = IFF_NOARP; 433 ndev->netdev_ops = &mctp_serial_netdev_ops; 434 ndev->needs_free_netdev = true; 435} 436 437static int mctp_serial_open(struct tty_struct *tty) 438{ 439 struct mctp_serial *dev; 440 struct net_device *ndev; 441 char name[32]; 442 int idx, rc; 443 444 if (!capable(CAP_NET_ADMIN)) 445 return -EPERM; 446 447 if (!tty->ops->write) 448 return -EOPNOTSUPP; 449 450 idx = ida_alloc(&mctp_serial_ida, GFP_KERNEL); 451 if (idx < 0) 452 return idx; 453 454 snprintf(name, sizeof(name), "mctpserial%d", idx); 455 ndev = alloc_netdev(sizeof(*dev), name, NET_NAME_ENUM, 456 mctp_serial_setup); 457 if (!ndev) { 458 rc = -ENOMEM; 459 goto free_ida; 460 } 461 462 dev = netdev_priv(ndev); 463 dev->idx = idx; 464 dev->tty = tty; 465 dev->netdev = ndev; 466 dev->txstate = STATE_IDLE; 467 dev->rxstate = STATE_IDLE; 468 spin_lock_init(&dev->lock); 469 INIT_WORK(&dev->tx_work, mctp_serial_tx_work); 470 471 rc = register_netdev(ndev); 472 if (rc) 473 goto free_netdev; 474 475 tty->receive_room = 64 * 1024; 476 tty->disc_data = dev; 477 478 return 0; 479 480free_netdev: 481 free_netdev(ndev); 482 483free_ida: 484 ida_free(&mctp_serial_ida, idx); 485 return rc; 486} 487 488static void mctp_serial_close(struct tty_struct *tty) 489{ 490 struct mctp_serial *dev = tty->disc_data; 491 int idx = dev->idx; 492 493 unregister_netdev(dev->netdev); 494 ida_free(&mctp_serial_ida, idx); 495} 496 497static struct tty_ldisc_ops mctp_ldisc = { 498 .owner = THIS_MODULE, 499 .num = N_MCTP, 500 .name = "mctp", 501 .open = mctp_serial_open, 502 .close = mctp_serial_close, 503 .receive_buf = mctp_serial_tty_receive_buf, 504 .write_wakeup = mctp_serial_tty_write_wakeup, 505}; 506 507static int __init mctp_serial_init(void) 508{ 509 return tty_register_ldisc(&mctp_ldisc); 510} 511 512static void __exit mctp_serial_exit(void) 513{ 514 tty_unregister_ldisc(&mctp_ldisc); 515} 516 517module_init(mctp_serial_init); 518module_exit(mctp_serial_exit); 519 520MODULE_LICENSE("GPL v2"); 521MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 522MODULE_DESCRIPTION("MCTP Serial transport");