162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Management Component Transport Protocol (MCTP) - serial transport 462306a36Sopenharmony_ci * binding. This driver is an implementation of the DMTF specificiation 562306a36Sopenharmony_ci * "DSP0253 - Management Component Transport Protocol (MCTP) Serial Transport 662306a36Sopenharmony_ci * Binding", available at: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * https://www.dmtf.org/sites/default/files/standards/documents/DSP0253_1.0.0.pdf 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This driver provides DSP0253-type MCTP-over-serial transport using a Linux 1162306a36Sopenharmony_ci * tty device, by setting the N_MCTP line discipline on the tty. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Copyright (c) 2021 Code Construct 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/idr.h> 1762306a36Sopenharmony_ci#include <linux/if_arp.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/skbuff.h> 2062306a36Sopenharmony_ci#include <linux/tty.h> 2162306a36Sopenharmony_ci#include <linux/workqueue.h> 2262306a36Sopenharmony_ci#include <linux/crc-ccitt.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/mctp.h> 2562306a36Sopenharmony_ci#include <net/mctp.h> 2662306a36Sopenharmony_ci#include <net/pkt_sched.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MCTP_SERIAL_MTU 68 /* base mtu (64) + mctp header */ 2962306a36Sopenharmony_ci#define MCTP_SERIAL_FRAME_MTU (MCTP_SERIAL_MTU + 6) /* + serial framing */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MCTP_SERIAL_VERSION 0x1 /* DSP0253 defines a single version: 1 */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define BUFSIZE MCTP_SERIAL_FRAME_MTU 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define BYTE_FRAME 0x7e 3662306a36Sopenharmony_ci#define BYTE_ESC 0x7d 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define FCS_INIT 0xffff 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic DEFINE_IDA(mctp_serial_ida); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cienum mctp_serial_state { 4362306a36Sopenharmony_ci STATE_IDLE, 4462306a36Sopenharmony_ci STATE_START, 4562306a36Sopenharmony_ci STATE_HEADER, 4662306a36Sopenharmony_ci STATE_DATA, 4762306a36Sopenharmony_ci STATE_ESCAPE, 4862306a36Sopenharmony_ci STATE_TRAILER, 4962306a36Sopenharmony_ci STATE_DONE, 5062306a36Sopenharmony_ci STATE_ERR, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct mctp_serial { 5462306a36Sopenharmony_ci struct net_device *netdev; 5562306a36Sopenharmony_ci struct tty_struct *tty; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci int idx; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* protects our rx & tx state machines; held during both paths */ 6062306a36Sopenharmony_ci spinlock_t lock; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci struct work_struct tx_work; 6362306a36Sopenharmony_ci enum mctp_serial_state txstate, rxstate; 6462306a36Sopenharmony_ci u16 txfcs, rxfcs, rxfcs_rcvd; 6562306a36Sopenharmony_ci unsigned int txlen, rxlen; 6662306a36Sopenharmony_ci unsigned int txpos, rxpos; 6762306a36Sopenharmony_ci unsigned char txbuf[BUFSIZE], 6862306a36Sopenharmony_ci rxbuf[BUFSIZE]; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic bool needs_escape(unsigned char c) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return c == BYTE_ESC || c == BYTE_FRAME; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int next_chunk_len(struct mctp_serial *dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* either we have no bytes to send ... */ 8162306a36Sopenharmony_ci if (dev->txpos == dev->txlen) 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* ... or the next byte to send is an escaped byte; requiring a 8562306a36Sopenharmony_ci * single-byte chunk... 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (needs_escape(dev->txbuf[dev->txpos])) 8862306a36Sopenharmony_ci return 1; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* ... or we have one or more bytes up to the next escape - this chunk 9162306a36Sopenharmony_ci * will be those non-escaped bytes, and does not include the escaped 9262306a36Sopenharmony_ci * byte. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci for (i = 1; i + dev->txpos + 1 < dev->txlen; i++) { 9562306a36Sopenharmony_ci if (needs_escape(dev->txbuf[dev->txpos + i + 1])) 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return i; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int write_chunk(struct mctp_serial *dev, unsigned char *buf, int len) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci return dev->tty->ops->write(dev->tty, buf, len); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void mctp_serial_tx_work(struct work_struct *work) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct mctp_serial *dev = container_of(work, struct mctp_serial, 11062306a36Sopenharmony_ci tx_work); 11162306a36Sopenharmony_ci unsigned char c, buf[3]; 11262306a36Sopenharmony_ci unsigned long flags; 11362306a36Sopenharmony_ci int len, txlen; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* txstate represents the next thing to send */ 11862306a36Sopenharmony_ci switch (dev->txstate) { 11962306a36Sopenharmony_ci case STATE_START: 12062306a36Sopenharmony_ci dev->txpos = 0; 12162306a36Sopenharmony_ci fallthrough; 12262306a36Sopenharmony_ci case STATE_HEADER: 12362306a36Sopenharmony_ci buf[0] = BYTE_FRAME; 12462306a36Sopenharmony_ci buf[1] = MCTP_SERIAL_VERSION; 12562306a36Sopenharmony_ci buf[2] = dev->txlen; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!dev->txpos) 12862306a36Sopenharmony_ci dev->txfcs = crc_ccitt(FCS_INIT, buf + 1, 2); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos); 13162306a36Sopenharmony_ci if (txlen <= 0) { 13262306a36Sopenharmony_ci dev->txstate = STATE_ERR; 13362306a36Sopenharmony_ci } else { 13462306a36Sopenharmony_ci dev->txpos += txlen; 13562306a36Sopenharmony_ci if (dev->txpos == 3) { 13662306a36Sopenharmony_ci dev->txstate = STATE_DATA; 13762306a36Sopenharmony_ci dev->txpos = 0; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci case STATE_ESCAPE: 14362306a36Sopenharmony_ci buf[0] = dev->txbuf[dev->txpos] & ~0x20; 14462306a36Sopenharmony_ci txlen = write_chunk(dev, buf, 1); 14562306a36Sopenharmony_ci if (txlen <= 0) { 14662306a36Sopenharmony_ci dev->txstate = STATE_ERR; 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci dev->txpos += txlen; 14962306a36Sopenharmony_ci if (dev->txpos == dev->txlen) { 15062306a36Sopenharmony_ci dev->txstate = STATE_TRAILER; 15162306a36Sopenharmony_ci dev->txpos = 0; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci case STATE_DATA: 15862306a36Sopenharmony_ci len = next_chunk_len(dev); 15962306a36Sopenharmony_ci if (len) { 16062306a36Sopenharmony_ci c = dev->txbuf[dev->txpos]; 16162306a36Sopenharmony_ci if (len == 1 && needs_escape(c)) { 16262306a36Sopenharmony_ci buf[0] = BYTE_ESC; 16362306a36Sopenharmony_ci buf[1] = c & ~0x20; 16462306a36Sopenharmony_ci dev->txfcs = crc_ccitt_byte(dev->txfcs, c); 16562306a36Sopenharmony_ci txlen = write_chunk(dev, buf, 2); 16662306a36Sopenharmony_ci if (txlen == 2) 16762306a36Sopenharmony_ci dev->txpos++; 16862306a36Sopenharmony_ci else if (txlen == 1) 16962306a36Sopenharmony_ci dev->txstate = STATE_ESCAPE; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci dev->txstate = STATE_ERR; 17262306a36Sopenharmony_ci } else { 17362306a36Sopenharmony_ci txlen = write_chunk(dev, 17462306a36Sopenharmony_ci dev->txbuf + dev->txpos, 17562306a36Sopenharmony_ci len); 17662306a36Sopenharmony_ci if (txlen <= 0) { 17762306a36Sopenharmony_ci dev->txstate = STATE_ERR; 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci dev->txfcs = crc_ccitt(dev->txfcs, 18062306a36Sopenharmony_ci dev->txbuf + 18162306a36Sopenharmony_ci dev->txpos, 18262306a36Sopenharmony_ci txlen); 18362306a36Sopenharmony_ci dev->txpos += txlen; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci if (dev->txstate == STATE_DATA && 18762306a36Sopenharmony_ci dev->txpos == dev->txlen) { 18862306a36Sopenharmony_ci dev->txstate = STATE_TRAILER; 18962306a36Sopenharmony_ci dev->txpos = 0; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci dev->txstate = STATE_TRAILER; 19462306a36Sopenharmony_ci dev->txpos = 0; 19562306a36Sopenharmony_ci fallthrough; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci case STATE_TRAILER: 19862306a36Sopenharmony_ci buf[0] = dev->txfcs >> 8; 19962306a36Sopenharmony_ci buf[1] = dev->txfcs & 0xff; 20062306a36Sopenharmony_ci buf[2] = BYTE_FRAME; 20162306a36Sopenharmony_ci txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos); 20262306a36Sopenharmony_ci if (txlen <= 0) { 20362306a36Sopenharmony_ci dev->txstate = STATE_ERR; 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci dev->txpos += txlen; 20662306a36Sopenharmony_ci if (dev->txpos == 3) { 20762306a36Sopenharmony_ci dev->txstate = STATE_DONE; 20862306a36Sopenharmony_ci dev->txpos = 0; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci default: 21362306a36Sopenharmony_ci netdev_err_once(dev->netdev, "invalid tx state %d\n", 21462306a36Sopenharmony_ci dev->txstate); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (dev->txstate == STATE_DONE) { 21862306a36Sopenharmony_ci dev->netdev->stats.tx_packets++; 21962306a36Sopenharmony_ci dev->netdev->stats.tx_bytes += dev->txlen; 22062306a36Sopenharmony_ci dev->txlen = 0; 22162306a36Sopenharmony_ci dev->txpos = 0; 22262306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags); 22362306a36Sopenharmony_ci dev->txstate = STATE_IDLE; 22462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci netif_wake_queue(dev->netdev); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic netdev_tx_t mctp_serial_tx(struct sk_buff *skb, struct net_device *ndev) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct mctp_serial *dev = netdev_priv(ndev); 23562306a36Sopenharmony_ci unsigned long flags; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci WARN_ON(dev->txstate != STATE_IDLE); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (skb->len > MCTP_SERIAL_MTU) { 24062306a36Sopenharmony_ci dev->netdev->stats.tx_dropped++; 24162306a36Sopenharmony_ci goto out; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 24562306a36Sopenharmony_ci netif_stop_queue(dev->netdev); 24662306a36Sopenharmony_ci skb_copy_bits(skb, 0, dev->txbuf, skb->len); 24762306a36Sopenharmony_ci dev->txpos = 0; 24862306a36Sopenharmony_ci dev->txlen = skb->len; 24962306a36Sopenharmony_ci dev->txstate = STATE_START; 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags); 25362306a36Sopenharmony_ci schedule_work(&dev->tx_work); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciout: 25662306a36Sopenharmony_ci kfree_skb(skb); 25762306a36Sopenharmony_ci return NETDEV_TX_OK; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void mctp_serial_tty_write_wakeup(struct tty_struct *tty) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct mctp_serial *dev = tty->disc_data; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci schedule_work(&dev->tx_work); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void mctp_serial_rx(struct mctp_serial *dev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct mctp_skb_cb *cb; 27062306a36Sopenharmony_ci struct sk_buff *skb; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (dev->rxfcs != dev->rxfcs_rcvd) { 27362306a36Sopenharmony_ci dev->netdev->stats.rx_dropped++; 27462306a36Sopenharmony_ci dev->netdev->stats.rx_crc_errors++; 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci skb = netdev_alloc_skb(dev->netdev, dev->rxlen); 27962306a36Sopenharmony_ci if (!skb) { 28062306a36Sopenharmony_ci dev->netdev->stats.rx_dropped++; 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MCTP); 28562306a36Sopenharmony_ci skb_put_data(skb, dev->rxbuf, dev->rxlen); 28662306a36Sopenharmony_ci skb_reset_network_header(skb); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci cb = __mctp_cb(skb); 28962306a36Sopenharmony_ci cb->halen = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci netif_rx(skb); 29262306a36Sopenharmony_ci dev->netdev->stats.rx_packets++; 29362306a36Sopenharmony_ci dev->netdev->stats.rx_bytes += dev->rxlen; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void mctp_serial_push_header(struct mctp_serial *dev, unsigned char c) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci switch (dev->rxpos) { 29962306a36Sopenharmony_ci case 0: 30062306a36Sopenharmony_ci if (c == BYTE_FRAME) 30162306a36Sopenharmony_ci dev->rxpos++; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci dev->rxstate = STATE_ERR; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case 1: 30662306a36Sopenharmony_ci if (c == MCTP_SERIAL_VERSION) { 30762306a36Sopenharmony_ci dev->rxpos++; 30862306a36Sopenharmony_ci dev->rxfcs = crc_ccitt_byte(FCS_INIT, c); 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci dev->rxstate = STATE_ERR; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 2: 31462306a36Sopenharmony_ci if (c > MCTP_SERIAL_FRAME_MTU) { 31562306a36Sopenharmony_ci dev->rxstate = STATE_ERR; 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci dev->rxlen = c; 31862306a36Sopenharmony_ci dev->rxpos = 0; 31962306a36Sopenharmony_ci dev->rxstate = STATE_DATA; 32062306a36Sopenharmony_ci dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void mctp_serial_push_trailer(struct mctp_serial *dev, unsigned char c) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci switch (dev->rxpos) { 32962306a36Sopenharmony_ci case 0: 33062306a36Sopenharmony_ci dev->rxfcs_rcvd = c << 8; 33162306a36Sopenharmony_ci dev->rxpos++; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case 1: 33462306a36Sopenharmony_ci dev->rxfcs_rcvd |= c; 33562306a36Sopenharmony_ci dev->rxpos++; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case 2: 33862306a36Sopenharmony_ci if (c != BYTE_FRAME) { 33962306a36Sopenharmony_ci dev->rxstate = STATE_ERR; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci mctp_serial_rx(dev); 34262306a36Sopenharmony_ci dev->rxlen = 0; 34362306a36Sopenharmony_ci dev->rxpos = 0; 34462306a36Sopenharmony_ci dev->rxstate = STATE_IDLE; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void mctp_serial_push(struct mctp_serial *dev, unsigned char c) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci switch (dev->rxstate) { 35362306a36Sopenharmony_ci case STATE_IDLE: 35462306a36Sopenharmony_ci dev->rxstate = STATE_HEADER; 35562306a36Sopenharmony_ci fallthrough; 35662306a36Sopenharmony_ci case STATE_HEADER: 35762306a36Sopenharmony_ci mctp_serial_push_header(dev, c); 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci case STATE_ESCAPE: 36162306a36Sopenharmony_ci c |= 0x20; 36262306a36Sopenharmony_ci fallthrough; 36362306a36Sopenharmony_ci case STATE_DATA: 36462306a36Sopenharmony_ci if (dev->rxstate != STATE_ESCAPE && c == BYTE_ESC) { 36562306a36Sopenharmony_ci dev->rxstate = STATE_ESCAPE; 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); 36862306a36Sopenharmony_ci dev->rxbuf[dev->rxpos] = c; 36962306a36Sopenharmony_ci dev->rxpos++; 37062306a36Sopenharmony_ci dev->rxstate = STATE_DATA; 37162306a36Sopenharmony_ci if (dev->rxpos == dev->rxlen) { 37262306a36Sopenharmony_ci dev->rxpos = 0; 37362306a36Sopenharmony_ci dev->rxstate = STATE_TRAILER; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci case STATE_TRAILER: 37962306a36Sopenharmony_ci mctp_serial_push_trailer(dev, c); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci case STATE_ERR: 38362306a36Sopenharmony_ci if (c == BYTE_FRAME) 38462306a36Sopenharmony_ci dev->rxstate = STATE_IDLE; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci default: 38862306a36Sopenharmony_ci netdev_err_once(dev->netdev, "invalid rx state %d\n", 38962306a36Sopenharmony_ci dev->rxstate); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void mctp_serial_tty_receive_buf(struct tty_struct *tty, const u8 *c, 39462306a36Sopenharmony_ci const u8 *f, size_t len) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct mctp_serial *dev = tty->disc_data; 39762306a36Sopenharmony_ci int i; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!netif_running(dev->netdev)) 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* we don't (currently) use the flag bytes, just data. */ 40362306a36Sopenharmony_ci for (i = 0; i < len; i++) 40462306a36Sopenharmony_ci mctp_serial_push(dev, c[i]); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void mctp_serial_uninit(struct net_device *ndev) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct mctp_serial *dev = netdev_priv(ndev); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci cancel_work_sync(&dev->tx_work); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic const struct net_device_ops mctp_serial_netdev_ops = { 41562306a36Sopenharmony_ci .ndo_start_xmit = mctp_serial_tx, 41662306a36Sopenharmony_ci .ndo_uninit = mctp_serial_uninit, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void mctp_serial_setup(struct net_device *ndev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci ndev->type = ARPHRD_MCTP; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* we limit at the fixed MTU, which is also the MCTP-standard 42462306a36Sopenharmony_ci * baseline MTU, so is also our minimum 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci ndev->mtu = MCTP_SERIAL_MTU; 42762306a36Sopenharmony_ci ndev->max_mtu = MCTP_SERIAL_MTU; 42862306a36Sopenharmony_ci ndev->min_mtu = MCTP_SERIAL_MTU; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ndev->hard_header_len = 0; 43162306a36Sopenharmony_ci ndev->addr_len = 0; 43262306a36Sopenharmony_ci ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 43362306a36Sopenharmony_ci ndev->flags = IFF_NOARP; 43462306a36Sopenharmony_ci ndev->netdev_ops = &mctp_serial_netdev_ops; 43562306a36Sopenharmony_ci ndev->needs_free_netdev = true; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int mctp_serial_open(struct tty_struct *tty) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct mctp_serial *dev; 44162306a36Sopenharmony_ci struct net_device *ndev; 44262306a36Sopenharmony_ci char name[32]; 44362306a36Sopenharmony_ci int idx, rc; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 44662306a36Sopenharmony_ci return -EPERM; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!tty->ops->write) 44962306a36Sopenharmony_ci return -EOPNOTSUPP; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci idx = ida_alloc(&mctp_serial_ida, GFP_KERNEL); 45262306a36Sopenharmony_ci if (idx < 0) 45362306a36Sopenharmony_ci return idx; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci snprintf(name, sizeof(name), "mctpserial%d", idx); 45662306a36Sopenharmony_ci ndev = alloc_netdev(sizeof(*dev), name, NET_NAME_ENUM, 45762306a36Sopenharmony_ci mctp_serial_setup); 45862306a36Sopenharmony_ci if (!ndev) { 45962306a36Sopenharmony_ci rc = -ENOMEM; 46062306a36Sopenharmony_ci goto free_ida; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dev = netdev_priv(ndev); 46462306a36Sopenharmony_ci dev->idx = idx; 46562306a36Sopenharmony_ci dev->tty = tty; 46662306a36Sopenharmony_ci dev->netdev = ndev; 46762306a36Sopenharmony_ci dev->txstate = STATE_IDLE; 46862306a36Sopenharmony_ci dev->rxstate = STATE_IDLE; 46962306a36Sopenharmony_ci spin_lock_init(&dev->lock); 47062306a36Sopenharmony_ci INIT_WORK(&dev->tx_work, mctp_serial_tx_work); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci rc = register_netdev(ndev); 47362306a36Sopenharmony_ci if (rc) 47462306a36Sopenharmony_ci goto free_netdev; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci tty->receive_room = 64 * 1024; 47762306a36Sopenharmony_ci tty->disc_data = dev; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cifree_netdev: 48262306a36Sopenharmony_ci free_netdev(ndev); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cifree_ida: 48562306a36Sopenharmony_ci ida_free(&mctp_serial_ida, idx); 48662306a36Sopenharmony_ci return rc; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void mctp_serial_close(struct tty_struct *tty) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct mctp_serial *dev = tty->disc_data; 49262306a36Sopenharmony_ci int idx = dev->idx; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci unregister_netdev(dev->netdev); 49562306a36Sopenharmony_ci ida_free(&mctp_serial_ida, idx); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic struct tty_ldisc_ops mctp_ldisc = { 49962306a36Sopenharmony_ci .owner = THIS_MODULE, 50062306a36Sopenharmony_ci .num = N_MCTP, 50162306a36Sopenharmony_ci .name = "mctp", 50262306a36Sopenharmony_ci .open = mctp_serial_open, 50362306a36Sopenharmony_ci .close = mctp_serial_close, 50462306a36Sopenharmony_ci .receive_buf = mctp_serial_tty_receive_buf, 50562306a36Sopenharmony_ci .write_wakeup = mctp_serial_tty_write_wakeup, 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int __init mctp_serial_init(void) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci return tty_register_ldisc(&mctp_ldisc); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void __exit mctp_serial_exit(void) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci tty_unregister_ldisc(&mctp_ldisc); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cimodule_init(mctp_serial_init); 51962306a36Sopenharmony_cimodule_exit(mctp_serial_exit); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 52262306a36Sopenharmony_ciMODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 52362306a36Sopenharmony_ciMODULE_DESCRIPTION("MCTP Serial transport"); 524