Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: mctp: Add MCTP USB transport driver

Add an implementation for DMTF DSP0283, which defines a MCTP-over-USB
transport. As per that spec, we're restricted to full speed mode,
requiring 512-byte transfers.

Each MCTP-over-USB interface is a peer-to-peer link to a single MCTP
endpoint, so no physical addressing is required (of course, that MCTP
endpoint may then bridge to further MCTP endpoints). Consequently,
interfaces will report with no lladdr data:

# mctp link
dev lo index 1 address 00:00:00:00:00:00 net 1 mtu 65536 up
dev mctpusb0 index 6 address none net 1 mtu 68 up

This is a simple initial implementation, with single rx & tx urbs, and
no multi-packet tx transfers - although we do accept multi-packet rx
from the device.

Includes suggested fixes from Santosh Puranik <spuranik@nvidia.com>.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Cc: Santosh Puranik <spuranik@nvidia.com>
Link: https://patch.msgid.link/20250221-dev-mctp-usb-v3-2-3353030fe9cc@codeconstruct.com.au
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Jeremy Kerr and committed by
Jakub Kicinski
0791c032 dcc35baa

+396
+10
drivers/net/mctp/Kconfig
··· 47 47 A MCTP protocol network device is created for each I3C bus 48 48 having a "mctp-controller" devicetree property. 49 49 50 + config MCTP_TRANSPORT_USB 51 + tristate "MCTP USB transport" 52 + depends on USB 53 + help 54 + Provides a driver to access MCTP devices over USB transport, 55 + defined by DMTF specification DSP0283. 56 + 57 + MCTP-over-USB interfaces are peer-to-peer, so each interface 58 + represents a physical connection to one remote MCTP endpoint. 59 + 50 60 endmenu 51 61 52 62 endif
+1
drivers/net/mctp/Makefile
··· 1 1 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o 2 2 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o 3 3 obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o 4 + obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o
+385
drivers/net/mctp/mctp-usb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * mctp-usb.c - MCTP-over-USB (DMTF DSP0283) transport binding driver. 4 + * 5 + * DSP0283 is available at: 6 + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf 7 + * 8 + * Copyright (C) 2024-2025 Code Construct Pty Ltd 9 + */ 10 + 11 + #include <linux/module.h> 12 + #include <linux/netdevice.h> 13 + #include <linux/usb.h> 14 + #include <linux/usb/mctp-usb.h> 15 + 16 + #include <net/mctp.h> 17 + #include <net/mctpdevice.h> 18 + #include <net/pkt_sched.h> 19 + 20 + #include <uapi/linux/if_arp.h> 21 + 22 + struct mctp_usb { 23 + struct usb_device *usbdev; 24 + struct usb_interface *intf; 25 + bool stopped; 26 + 27 + struct net_device *netdev; 28 + 29 + u8 ep_in; 30 + u8 ep_out; 31 + 32 + struct urb *tx_urb; 33 + struct urb *rx_urb; 34 + 35 + struct delayed_work rx_retry_work; 36 + }; 37 + 38 + static void mctp_usb_out_complete(struct urb *urb) 39 + { 40 + struct sk_buff *skb = urb->context; 41 + struct net_device *netdev = skb->dev; 42 + int status; 43 + 44 + status = urb->status; 45 + 46 + switch (status) { 47 + case -ENOENT: 48 + case -ECONNRESET: 49 + case -ESHUTDOWN: 50 + case -EPROTO: 51 + dev_dstats_tx_dropped(netdev); 52 + break; 53 + case 0: 54 + dev_dstats_tx_add(netdev, skb->len); 55 + netif_wake_queue(netdev); 56 + consume_skb(skb); 57 + return; 58 + default: 59 + netdev_dbg(netdev, "unexpected tx urb status: %d\n", status); 60 + dev_dstats_tx_dropped(netdev); 61 + } 62 + 63 + kfree_skb(skb); 64 + } 65 + 66 + static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb, 67 + struct net_device *dev) 68 + { 69 + struct mctp_usb *mctp_usb = netdev_priv(dev); 70 + struct mctp_usb_hdr *hdr; 71 + unsigned int plen; 72 + struct urb *urb; 73 + int rc; 74 + 75 + plen = skb->len; 76 + 77 + if (plen + sizeof(*hdr) > MCTP_USB_XFER_SIZE) 78 + goto err_drop; 79 + 80 + rc = skb_cow_head(skb, sizeof(*hdr)); 81 + if (rc) 82 + goto err_drop; 83 + 84 + hdr = skb_push(skb, sizeof(*hdr)); 85 + if (!hdr) 86 + goto err_drop; 87 + 88 + hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID); 89 + hdr->rsvd = 0; 90 + hdr->len = plen + sizeof(*hdr); 91 + 92 + urb = mctp_usb->tx_urb; 93 + 94 + usb_fill_bulk_urb(urb, mctp_usb->usbdev, 95 + usb_sndbulkpipe(mctp_usb->usbdev, mctp_usb->ep_out), 96 + skb->data, skb->len, 97 + mctp_usb_out_complete, skb); 98 + 99 + rc = usb_submit_urb(urb, GFP_ATOMIC); 100 + if (rc) 101 + goto err_drop; 102 + else 103 + netif_stop_queue(dev); 104 + 105 + return NETDEV_TX_OK; 106 + 107 + err_drop: 108 + dev_dstats_tx_dropped(dev); 109 + kfree_skb(skb); 110 + return NETDEV_TX_OK; 111 + } 112 + 113 + static void mctp_usb_in_complete(struct urb *urb); 114 + 115 + /* If we fail to queue an in urb atomically (either due to skb allocation or 116 + * urb submission), we will schedule a rx queue in nonatomic context 117 + * after a delay, specified in jiffies 118 + */ 119 + static const unsigned long RX_RETRY_DELAY = HZ / 4; 120 + 121 + static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) 122 + { 123 + struct sk_buff *skb; 124 + int rc; 125 + 126 + skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_XFER_SIZE, gfp); 127 + if (!skb) { 128 + rc = -ENOMEM; 129 + goto err_retry; 130 + } 131 + 132 + usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev, 133 + usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in), 134 + skb->data, MCTP_USB_XFER_SIZE, 135 + mctp_usb_in_complete, skb); 136 + 137 + rc = usb_submit_urb(mctp_usb->rx_urb, gfp); 138 + if (rc) { 139 + netdev_dbg(mctp_usb->netdev, "rx urb submit failure: %d\n", rc); 140 + kfree_skb(skb); 141 + if (rc == -ENOMEM) 142 + goto err_retry; 143 + } 144 + 145 + return rc; 146 + 147 + err_retry: 148 + schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); 149 + return rc; 150 + } 151 + 152 + static void mctp_usb_in_complete(struct urb *urb) 153 + { 154 + struct sk_buff *skb = urb->context; 155 + struct net_device *netdev = skb->dev; 156 + struct mctp_usb *mctp_usb = netdev_priv(netdev); 157 + struct mctp_skb_cb *cb; 158 + unsigned int len; 159 + int status; 160 + 161 + status = urb->status; 162 + 163 + switch (status) { 164 + case -ENOENT: 165 + case -ECONNRESET: 166 + case -ESHUTDOWN: 167 + case -EPROTO: 168 + kfree_skb(skb); 169 + return; 170 + case 0: 171 + break; 172 + default: 173 + netdev_dbg(netdev, "unexpected rx urb status: %d\n", status); 174 + kfree_skb(skb); 175 + return; 176 + } 177 + 178 + len = urb->actual_length; 179 + __skb_put(skb, len); 180 + 181 + while (skb) { 182 + struct sk_buff *skb2 = NULL; 183 + struct mctp_usb_hdr *hdr; 184 + u8 pkt_len; /* length of MCTP packet, no USB header */ 185 + 186 + hdr = skb_pull_data(skb, sizeof(*hdr)); 187 + if (!hdr) 188 + break; 189 + 190 + if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) { 191 + netdev_dbg(netdev, "rx: invalid id %04x\n", 192 + be16_to_cpu(hdr->id)); 193 + break; 194 + } 195 + 196 + if (hdr->len < 197 + sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) { 198 + netdev_dbg(netdev, "rx: short packet (hdr) %d\n", 199 + hdr->len); 200 + break; 201 + } 202 + 203 + /* we know we have at least sizeof(struct mctp_usb_hdr) here */ 204 + pkt_len = hdr->len - sizeof(struct mctp_usb_hdr); 205 + if (pkt_len > skb->len) { 206 + netdev_dbg(netdev, 207 + "rx: short packet (xfer) %d, actual %d\n", 208 + hdr->len, skb->len); 209 + break; 210 + } 211 + 212 + if (pkt_len < skb->len) { 213 + /* more packets may follow - clone to a new 214 + * skb to use on the next iteration 215 + */ 216 + skb2 = skb_clone(skb, GFP_ATOMIC); 217 + if (skb2) { 218 + if (!skb_pull(skb2, pkt_len)) { 219 + kfree_skb(skb2); 220 + skb2 = NULL; 221 + } 222 + } 223 + skb_trim(skb, pkt_len); 224 + } 225 + 226 + dev_dstats_rx_add(netdev, skb->len); 227 + 228 + skb->protocol = htons(ETH_P_MCTP); 229 + skb_reset_network_header(skb); 230 + cb = __mctp_cb(skb); 231 + cb->halen = 0; 232 + netif_rx(skb); 233 + 234 + skb = skb2; 235 + } 236 + 237 + if (skb) 238 + kfree_skb(skb); 239 + 240 + mctp_usb_rx_queue(mctp_usb, GFP_ATOMIC); 241 + } 242 + 243 + static void mctp_usb_rx_retry_work(struct work_struct *work) 244 + { 245 + struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, 246 + rx_retry_work.work); 247 + 248 + if (READ_ONCE(mctp_usb->stopped)) 249 + return; 250 + 251 + mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); 252 + } 253 + 254 + static int mctp_usb_open(struct net_device *dev) 255 + { 256 + struct mctp_usb *mctp_usb = netdev_priv(dev); 257 + 258 + WRITE_ONCE(mctp_usb->stopped, false); 259 + 260 + return mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); 261 + } 262 + 263 + static int mctp_usb_stop(struct net_device *dev) 264 + { 265 + struct mctp_usb *mctp_usb = netdev_priv(dev); 266 + 267 + netif_stop_queue(dev); 268 + 269 + /* prevent RX submission retry */ 270 + WRITE_ONCE(mctp_usb->stopped, true); 271 + 272 + usb_kill_urb(mctp_usb->rx_urb); 273 + usb_kill_urb(mctp_usb->tx_urb); 274 + 275 + cancel_delayed_work_sync(&mctp_usb->rx_retry_work); 276 + 277 + return 0; 278 + } 279 + 280 + static const struct net_device_ops mctp_usb_netdev_ops = { 281 + .ndo_start_xmit = mctp_usb_start_xmit, 282 + .ndo_open = mctp_usb_open, 283 + .ndo_stop = mctp_usb_stop, 284 + }; 285 + 286 + static void mctp_usb_netdev_setup(struct net_device *dev) 287 + { 288 + dev->type = ARPHRD_MCTP; 289 + 290 + dev->mtu = MCTP_USB_MTU_MIN; 291 + dev->min_mtu = MCTP_USB_MTU_MIN; 292 + dev->max_mtu = MCTP_USB_MTU_MAX; 293 + 294 + dev->hard_header_len = sizeof(struct mctp_usb_hdr); 295 + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 296 + dev->flags = IFF_NOARP; 297 + dev->netdev_ops = &mctp_usb_netdev_ops; 298 + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; 299 + } 300 + 301 + static int mctp_usb_probe(struct usb_interface *intf, 302 + const struct usb_device_id *id) 303 + { 304 + struct usb_endpoint_descriptor *ep_in, *ep_out; 305 + struct usb_host_interface *iface_desc; 306 + struct net_device *netdev; 307 + struct mctp_usb *dev; 308 + int rc; 309 + 310 + /* only one alternate */ 311 + iface_desc = intf->cur_altsetting; 312 + 313 + rc = usb_find_common_endpoints(iface_desc, &ep_in, &ep_out, NULL, NULL); 314 + if (rc) { 315 + dev_err(&intf->dev, "invalid endpoints on device?\n"); 316 + return rc; 317 + } 318 + 319 + netdev = alloc_netdev(sizeof(*dev), "mctpusb%d", NET_NAME_ENUM, 320 + mctp_usb_netdev_setup); 321 + if (!netdev) 322 + return -ENOMEM; 323 + 324 + SET_NETDEV_DEV(netdev, &intf->dev); 325 + dev = netdev_priv(netdev); 326 + dev->netdev = netdev; 327 + dev->usbdev = usb_get_dev(interface_to_usbdev(intf)); 328 + dev->intf = intf; 329 + usb_set_intfdata(intf, dev); 330 + 331 + dev->ep_in = ep_in->bEndpointAddress; 332 + dev->ep_out = ep_out->bEndpointAddress; 333 + 334 + dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); 335 + dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 336 + if (!dev->tx_urb || !dev->rx_urb) { 337 + rc = -ENOMEM; 338 + goto err_free_urbs; 339 + } 340 + 341 + INIT_DELAYED_WORK(&dev->rx_retry_work, mctp_usb_rx_retry_work); 342 + 343 + rc = mctp_register_netdev(netdev, NULL, MCTP_PHYS_BINDING_USB); 344 + if (rc) 345 + goto err_free_urbs; 346 + 347 + return 0; 348 + 349 + err_free_urbs: 350 + usb_free_urb(dev->tx_urb); 351 + usb_free_urb(dev->rx_urb); 352 + free_netdev(netdev); 353 + return rc; 354 + } 355 + 356 + static void mctp_usb_disconnect(struct usb_interface *intf) 357 + { 358 + struct mctp_usb *dev = usb_get_intfdata(intf); 359 + 360 + mctp_unregister_netdev(dev->netdev); 361 + usb_free_urb(dev->tx_urb); 362 + usb_free_urb(dev->rx_urb); 363 + usb_put_dev(dev->usbdev); 364 + free_netdev(dev->netdev); 365 + } 366 + 367 + static const struct usb_device_id mctp_usb_devices[] = { 368 + { USB_INTERFACE_INFO(USB_CLASS_MCTP, 0x0, 0x1) }, 369 + { 0 }, 370 + }; 371 + 372 + MODULE_DEVICE_TABLE(usb, mctp_usb_devices); 373 + 374 + static struct usb_driver mctp_usb_driver = { 375 + .name = "mctp-usb", 376 + .id_table = mctp_usb_devices, 377 + .probe = mctp_usb_probe, 378 + .disconnect = mctp_usb_disconnect, 379 + }; 380 + 381 + module_usb_driver(mctp_usb_driver) 382 + 383 + MODULE_LICENSE("GPL"); 384 + MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 385 + MODULE_DESCRIPTION("MCTP USB transport");