From: Jeremy Kerr <jk@codeconstruct.com.au>
To: Matt Johnston <matt@codeconstruct.com.au>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: netdev@vger.kernel.org, linux-usb@vger.kernel.org
Subject: [PATCH net-next 03/12] net: mctp: usblib: Move RX transfer processing to a new mctp-usblib
Date: Tue, 30 Jun 2026 11:21:24 +0800 [thread overview]
Message-ID: <20260630-dev-mctp-usb-1-1-v1-3-86a311fc67b7@codeconstruct.com.au> (raw)
In-Reply-To: <20260630-dev-mctp-usb-1-1-v1-0-86a311fc67b7@codeconstruct.com.au>
The processing of USB receive transfers is common to both sides of a
MCTP over USB transport. In order to support a future gadget driver,
move the current host-side driver into a new common file, mctp-usblib.
This currently handles the submit-complete-packetise process of the
receive path of the USB transport. We'll add transmit handling in an
upcoming change.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
drivers/net/mctp/Kconfig | 11 +++
drivers/net/mctp/Makefile | 1 +
drivers/net/mctp/mctp-usb.c | 100 +++++-------------------
drivers/net/mctp/mctp-usblib.c | 167 +++++++++++++++++++++++++++++++++++++++++
include/linux/usb/mctp-usb.h | 26 +++++++
5 files changed, 225 insertions(+), 80 deletions(-)
diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index cf325ab0b1ef..a564a792801d 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -47,9 +47,20 @@ config MCTP_TRANSPORT_I3C
A MCTP protocol network device is created for each I3C bus
having a "mctp-controller" devicetree property.
+config MCTP_TRANSPORT_USBLIB
+ tristate "MCTP over USB common library"
+ depends on USB
+ help
+ Common protocol handling functions for MCTP-over-USB transport
+ implementations, suitable for use in either host- or gadget-side
+ transport driver
+
+ This will be automatically enabled by the transport driver.
+
config MCTP_TRANSPORT_USB
tristate "MCTP USB transport"
depends on USB
+ select MCTP_TRANSPORT_USBLIB
help
Provides a driver to access MCTP devices over USB transport,
defined by DMTF specification DSP0283.
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index c36006849a1e..c870b62d3f1c 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o
+obj-$(CONFIG_MCTP_TRANSPORT_USBLIB) += mctp-usblib.o
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index c6e36b63e87a..531b7c994afb 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -28,6 +28,8 @@ struct mctp_usb {
u8 ep_in;
u8 ep_out;
+ struct mctp_usblib_rx rx;
+
struct urb *tx_urb;
struct urb *rx_urb;
@@ -125,24 +127,23 @@ static const unsigned long RX_RETRY_DELAY = HZ / 4;
static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
{
unsigned long flags;
- struct sk_buff *skb;
+ size_t len;
+ void *buf;
int rc;
- skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_1_0_XFER_SIZE, gfp);
- if (!skb) {
- rc = -ENOMEM;
+ rc = mctp_usblib_rx_prepare(mctp_usb->netdev, &mctp_usb->rx,
+ &buf, &len, gfp);
+ if (rc)
goto err_retry;
- }
usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev,
usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in),
- skb->data, MCTP_USB_1_0_XFER_SIZE,
- mctp_usb_in_complete, skb);
+ buf, len, mctp_usb_in_complete, mctp_usb);
rc = usb_submit_urb(mctp_usb->rx_urb, gfp);
if (rc) {
netdev_dbg(mctp_usb->netdev, "rx urb submit failure: %d\n", rc);
- kfree_skb(skb);
+ mctp_usblib_rx_cancel(&mctp_usb->rx);
if (rc == -ENOMEM)
goto err_retry;
}
@@ -159,92 +160,27 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
static void mctp_usb_in_complete(struct urb *urb)
{
- struct sk_buff *skb = urb->context;
- struct net_device *netdev = skb->dev;
- struct mctp_usb *mctp_usb = netdev_priv(netdev);
- struct mctp_skb_cb *cb;
- unsigned int len;
+ struct mctp_usb *mctp_usb = urb->context;
+ struct net_device *netdev = mctp_usb->netdev;
int status;
status = urb->status;
switch (status) {
+ default:
+ netdev_dbg(netdev, "unexpected rx urb status: %d\n", status);
+ fallthrough;
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
case -EPROTO:
- kfree_skb(skb);
+ mctp_usblib_rx_cancel(&mctp_usb->rx);
return;
case 0:
break;
- default:
- netdev_dbg(netdev, "unexpected rx urb status: %d\n", status);
- kfree_skb(skb);
- return;
}
- len = urb->actual_length;
- __skb_put(skb, len);
-
- while (skb) {
- struct sk_buff *skb2 = NULL;
- struct mctp_usb_hdr *hdr;
- u8 pkt_len; /* length of MCTP packet, no USB header */
-
- skb_reset_mac_header(skb);
- hdr = skb_pull_data(skb, sizeof(*hdr));
- if (!hdr)
- break;
-
- if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) {
- netdev_dbg(netdev, "rx: invalid id %04x\n",
- be16_to_cpu(hdr->id));
- break;
- }
-
- if (hdr->len <
- sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) {
- netdev_dbg(netdev, "rx: short packet (hdr) %d\n",
- hdr->len);
- break;
- }
-
- /* we know we have at least sizeof(struct mctp_usb_hdr) here */
- pkt_len = hdr->len - sizeof(struct mctp_usb_hdr);
- if (pkt_len > skb->len) {
- netdev_dbg(netdev,
- "rx: short packet (xfer) %d, actual %d\n",
- hdr->len, skb->len);
- break;
- }
-
- if (pkt_len < skb->len) {
- /* more packets may follow - clone to a new
- * skb to use on the next iteration
- */
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
- if (!skb_pull(skb2, pkt_len)) {
- kfree_skb(skb2);
- skb2 = NULL;
- }
- }
- skb_trim(skb, pkt_len);
- }
-
- dev_dstats_rx_add(netdev, skb->len);
-
- skb->protocol = htons(ETH_P_MCTP);
- skb_reset_network_header(skb);
- cb = __mctp_cb(skb);
- cb->halen = 0;
- netif_rx(skb);
-
- skb = skb2;
- }
-
- if (skb)
- kfree_skb(skb);
+ mctp_usblib_rx_complete(netdev, &mctp_usb->rx, urb->actual_length);
mctp_usb_rx_queue(mctp_usb, GFP_ATOMIC);
}
@@ -341,6 +277,8 @@ static int mctp_usb_probe(struct usb_interface *intf,
spin_lock_init(&dev->rx_lock);
usb_set_intfdata(intf, dev);
+ mctp_usblib_rx_init(&dev->rx);
+
dev->ep_in = ep_in->bEndpointAddress;
dev->ep_out = ep_out->bEndpointAddress;
@@ -362,6 +300,7 @@ static int mctp_usb_probe(struct usb_interface *intf,
err_free_urbs:
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->rx_urb);
+ mctp_usblib_rx_fini(&dev->rx);
free_netdev(netdev);
return rc;
}
@@ -371,6 +310,7 @@ static void mctp_usb_disconnect(struct usb_interface *intf)
struct mctp_usb *dev = usb_get_intfdata(intf);
mctp_unregister_netdev(dev->netdev);
+ mctp_usblib_rx_fini(&dev->rx);
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->rx_urb);
free_netdev(dev->netdev);
diff --git a/drivers/net/mctp/mctp-usblib.c b/drivers/net/mctp/mctp-usblib.c
new file mode 100644
index 000000000000..9b86eb4310ce
--- /dev/null
+++ b/drivers/net/mctp/mctp-usblib.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mctp-usblib.c - MCTP-over-USB (DMTF DSP0283) transport helper library
+ *
+ * DSP0283 is available at:
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf
+ *
+ * Copyright (C) 2024-2026 Code Construct Pty Ltd
+ */
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/usb/mctp-usb.h>
+#include <net/mctp.h>
+
+void mctp_usblib_rx_init(struct mctp_usblib_rx *rx)
+{
+ memset(rx, 0, sizeof(*rx));
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_init);
+
+void mctp_usblib_rx_fini(struct mctp_usblib_rx *rx)
+{
+ kfree_skb(rx->skb);
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_fini);
+
+/*
+ * Prepare a transfer buffer for future completion; *bufp and *lenp will
+ * be populated on success.
+ */
+int mctp_usblib_rx_prepare(struct net_device *netdev,
+ struct mctp_usblib_rx *rx,
+ void **bufp, size_t *lenp, gfp_t gfp)
+{
+ const unsigned int len = MCTP_USB_1_0_XFER_SIZE;
+ struct sk_buff *skb;
+
+ skb = __netdev_alloc_skb(netdev, len, gfp);
+ if (!skb)
+ return -ENOMEM;
+
+ rx->skb = skb;
+
+ *bufp = skb_tail_pointer(skb);
+ *lenp = skb_tailroom(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_prepare);
+
+static void mctp_usblib_rx(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct pcpu_dstats *dstats = this_cpu_ptr(netdev->dstats);
+ struct mctp_skb_cb *cb;
+ unsigned long flags;
+
+ /* we're called from an URB completion handler, and cannot assume local
+ * irqs are always disabled
+ */
+ flags = u64_stats_update_begin_irqsave(&dstats->syncp);
+ u64_stats_inc(&dstats->rx_packets);
+ u64_stats_add(&dstats->rx_bytes, skb->len + sizeof(struct mctp_usb_hdr));
+ u64_stats_update_end_irqrestore(&dstats->syncp, flags);
+
+ skb_reset_mac_header(skb);
+ skb->protocol = htons(ETH_P_MCTP);
+ skb_reset_network_header(skb);
+ cb = __mctp_cb(skb);
+ cb->halen = 0;
+ netif_rx(skb);
+}
+
+/*
+ * Receive a USB completion of @len bytes of incoming data. We will then split
+ * this into packets and netif_rx() each. Intended to be called in atomic
+ * contexts - ie., URB completion.
+ *
+ * Assumes @netdev uses dstats.
+ */
+int mctp_usblib_rx_complete(struct net_device *netdev,
+ struct mctp_usblib_rx *rx, size_t len)
+{
+ struct sk_buff *skb = rx->skb;
+ int rc = 0;
+
+ __skb_put(skb, len);
+
+ while (skb) {
+ struct sk_buff *skb2 = NULL;
+ struct mctp_usb_hdr *hdr;
+ /* length of MCTP packet, no USB header */
+ u8 pkt_len;
+
+ skb_reset_mac_header(skb);
+ hdr = skb_pull_data(skb, sizeof(*hdr));
+ if (!hdr) {
+ rc = -ENOMSG;
+ break;
+ }
+
+ if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) {
+ netdev_dbg(netdev, "rx: invalid id %04x\n",
+ be16_to_cpu(hdr->id));
+ rc = -EPROTO;
+ break;
+ }
+
+ if (hdr->len <
+ sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) {
+ netdev_dbg(netdev, "rx: short packet (hdr) %d\n",
+ hdr->len);
+ rc = -EPROTO;
+ break;
+ }
+
+ /* we know we have at least sizeof(struct mctp_usb_hdr) here */
+ pkt_len = hdr->len - sizeof(struct mctp_usb_hdr);
+ if (pkt_len > skb->len) {
+ rc = -EPROTO;
+ netdev_dbg(netdev,
+ "rx: short packet (xfer) %d, actual %d\n",
+ hdr->len, skb->len);
+ break;
+ }
+
+ if (pkt_len < skb->len) {
+ /* more packets may follow - clone to a new
+ * skb to use on the next iteration
+ */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ if (!skb_pull(skb2, pkt_len)) {
+ dev_kfree_skb_any(skb2);
+ skb2 = NULL;
+ }
+ }
+ skb_trim(skb, pkt_len);
+ }
+
+ mctp_usblib_rx(netdev, skb);
+ skb = skb2;
+ }
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ rx->skb = NULL;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_complete);
+
+/*
+ * Cancel a rx context; subsequent prepare/complete calls will not be a
+ * continuation of any data already received.
+ */
+void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx)
+{
+ dev_kfree_skb_any(rx->skb);
+ rx->skb = NULL;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_cancel);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
+MODULE_DESCRIPTION("MCTP USB transport library");
diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h
index 2bece8afd1c7..595e6af16dd0 100644
--- a/include/linux/usb/mctp-usb.h
+++ b/include/linux/usb/mctp-usb.h
@@ -13,6 +13,8 @@
#ifndef __LINUX_USB_MCTP_USB_H
#define __LINUX_USB_MCTP_USB_H
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
#include <linux/types.h>
struct mctp_usb_hdr {
@@ -29,4 +31,28 @@ struct mctp_usb_hdr {
#define MCTP_USB_1_0_MTU_MAX (MCTP_USB_1_0_PKTLEN_MAX - sizeof(struct mctp_usb_hdr))
#define MCTP_USB_DMTF_ID 0x1ab4
+/* mctp-usblib */
+
+/*
+ * RX handle: drivers will typically create one on init, which persists for
+ * the life of the driver. The same handle is used for progressive
+ * prepare -> complete operations (for each incoming USB transfer), which
+ * result in netif_rx()-ing the MCTP packets received
+ */
+struct mctp_usblib_rx {
+ struct sk_buff *skb;
+};
+
+void mctp_usblib_rx_init(struct mctp_usblib_rx *rx);
+void mctp_usblib_rx_fini(struct mctp_usblib_rx *rx);
+
+int mctp_usblib_rx_prepare(struct net_device *netdev,
+ struct mctp_usblib_rx *rx,
+ void **bufp, size_t *lenp, gfp_t gfp);
+
+int mctp_usblib_rx_complete(struct net_device *netdev,
+ struct mctp_usblib_rx *rx, size_t len);
+
+void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx);
+
#endif /* __LINUX_USB_MCTP_USB_H */
--
2.47.3
next prev parent reply other threads:[~2026-06-30 3:32 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 3:21 [PATCH net-next 00/12] net: mctp: usb: Add support for MCTP-over-USB v1.1 Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 01/12] net: mctp: usb: Include version indicator in max packet size defines Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 02/12] net: mctp: usb: Use packet-length max for maximum packet-size check Jeremy Kerr
2026-06-30 3:21 ` Jeremy Kerr [this message]
2026-06-30 3:21 ` [PATCH net-next 04/12] net: mctp: usblib: Move TX transfer processing to mctp-usblib Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 05/12] net: mctp: usb: Allow for multiple urb submissions from a packet tx Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 06/12] net: mctp: usblib: Add support for multi-packet transmit Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 07/12] net: mctp: usb: Accommodate DSP0283 v1.1 header format Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 08/12] net: mctp: usblib: Implement receive-side packet spanning Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 09/12] net: mctp: usblib: Implement transmit-side " Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 10/12] net: mctp: usblib: Add initial kunit tests Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 11/12] net: mctp: usb: enable v1.1 packet spanning Jeremy Kerr
2026-06-30 3:21 ` [PATCH net-next 12/12] net: mctp: usb: Allow multiple urbs in flight Jeremy Kerr
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260630-dev-mctp-usb-1-1-v1-3-86a311fc67b7@codeconstruct.com.au \
--to=jk@codeconstruct.com.au \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=kuba@kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=matt@codeconstruct.com.au \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox