mirror of
https://github.com/torvalds/linux.git
synced 2026-09-26 10:02:02 +02:00
net: mctp: usblib: Move RX transfer processing to a new mctp-usblib
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> Link: https://patch.msgid.link/20260724-dev-mctp-usb-1-1-v5-3-e66bbba0dbdc@codeconstruct.com.au Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
05b1a3a5ee
commit
b8564b19c0
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,93 +160,28 @@ 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:
|
||||
mctp_usblib_rx_complete(netdev, &mctp_usb->rx,
|
||||
urb->actual_length);
|
||||
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_usb_rx_queue(mctp_usb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
|
|
@ -286,6 +222,8 @@ static int mctp_usb_stop(struct net_device *dev)
|
|||
usb_kill_urb(mctp_usb->rx_urb);
|
||||
usb_kill_urb(mctp_usb->tx_urb);
|
||||
|
||||
mctp_usblib_rx_cancel(&mctp_usb->rx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -341,6 +279,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 +302,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 +312,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);
|
||||
|
|
|
|||
179
drivers/net/mctp/mctp-usblib.c
Normal file
179
drivers/net/mctp/mctp-usblib.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// 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/module.h>
|
||||
#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 = len;
|
||||
|
||||
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);
|
||||
u64_stats_update_end_irqrestore(&dstats->syncp, flags);
|
||||
|
||||
skb->protocol = htons(ETH_P_MCTP);
|
||||
skb_reset_network_header(skb);
|
||||
cb = __mctp_cb(skb);
|
||||
cb->halen = 0;
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void mctp_usblib_rx_stats_single_drop(struct net_device *dev)
|
||||
{
|
||||
struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
|
||||
unsigned long flags;
|
||||
|
||||
flags = u64_stats_update_begin_irqsave(&dstats->syncp);
|
||||
u64_stats_inc(&dstats->rx_drops);
|
||||
u64_stats_update_end_irqrestore(&dstats->syncp, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
} else {
|
||||
mctp_usblib_rx_stats_single_drop(netdev);
|
||||
}
|
||||
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");
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user