Commit 8ad228b1 authored by David S. Miller's avatar David S. Miller

Merge tag 'linux-can-next-for-6.6-20230728' of...

Merge tag 'linux-can-next-for-6.6-20230728' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next

linux-can-next-for-6.6-20230728

Marc Kleine-Budde says:

====================
Hello netdev-team,

this is a pull request of 21 patches for net-next/master.

The 1st patch is by Gerhard Uttenthaler, which adds Gerhard as the
maintainer ems_pci driver.

Peter Seiderer's patch removes a unused function from the peak_usb
driver.

The next 4 patches are by John Watts and add support for the sun4i_can
driver on the Allwinner D1.

Rob Herring's patch corrects the DT includes in various CAN drivers.

Followed by 14 patches from me concerning the gs_usb driver. The first
11 are various cleanups consisting of coding style improvements, error
path printout cleanups, and removal of unneeded
usb_kill_anchored_urbs(). The last 3 convert the driver to use NAPI to
avoid out-of-order reception of CAN frames.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 91fcb99c 52be626c
...@@ -21,6 +21,7 @@ properties: ...@@ -21,6 +21,7 @@ properties:
- const: allwinner,sun4i-a10-can - const: allwinner,sun4i-a10-can
- const: allwinner,sun4i-a10-can - const: allwinner,sun4i-a10-can
- const: allwinner,sun8i-r40-can - const: allwinner,sun8i-r40-can
- const: allwinner,sun20i-d1-can
reg: reg:
maxItems: 1 maxItems: 1
...@@ -37,8 +38,9 @@ properties: ...@@ -37,8 +38,9 @@ properties:
if: if:
properties: properties:
compatible: compatible:
contains: enum:
const: allwinner,sun8i-r40-can - allwinner,sun8i-r40-can
- allwinner,sun20i-d1-can
then: then:
required: required:
......
...@@ -7606,6 +7606,13 @@ L: linux-mmc@vger.kernel.org ...@@ -7606,6 +7606,13 @@ L: linux-mmc@vger.kernel.org
S: Supported S: Supported
F: drivers/mmc/host/cqhci* F: drivers/mmc/host/cqhci*
EMS CPC-PCI CAN DRIVER
M: Gerhard Uttenthaler <uttenthaler@ems-wuensche.com>
M: support@ems-wuensche.com
L: linux-can@vger.kernel.org
S: Maintained
F: drivers/net/can/sja1000/ems_pci.c
EMULEX 10Gbps iSCSI - OneConnect DRIVER EMULEX 10Gbps iSCSI - OneConnect DRIVER
M: Ketan Mukadam <ketan.mukadam@broadcom.com> M: Ketan Mukadam <ketan.mukadam@broadcom.com>
L: linux-scsi@vger.kernel.org L: linux-scsi@vger.kernel.org
......
...@@ -131,6 +131,18 @@ uart3_pb_pins: uart3-pb-pins { ...@@ -131,6 +131,18 @@ uart3_pb_pins: uart3-pb-pins {
pins = "PB6", "PB7"; pins = "PB6", "PB7";
function = "uart3"; function = "uart3";
}; };
/omit-if-no-ref/
can0_pins: can0-pins {
pins = "PB2", "PB3";
function = "can0";
};
/omit-if-no-ref/
can1_pins: can1-pins {
pins = "PB4", "PB5";
function = "can1";
};
}; };
ccu: clock-controller@2001000 { ccu: clock-controller@2001000 {
...@@ -879,5 +891,23 @@ rtc: rtc@7090000 { ...@@ -879,5 +891,23 @@ rtc: rtc@7090000 {
clock-names = "bus", "hosc", "ahb"; clock-names = "bus", "hosc", "ahb";
#clock-cells = <1>; #clock-cells = <1>;
}; };
can0: can@2504000 {
compatible = "allwinner,sun20i-d1-can";
reg = <0x02504000 0x400>;
interrupts = <SOC_PERIPHERAL_IRQ(21) IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_CAN0>;
resets = <&ccu RST_BUS_CAN0>;
status = "disabled";
};
can1: can@2504400 {
compatible = "allwinner,sun20i-d1-can";
reg = <0x02504400 0x400>;
interrupts = <SOC_PERIPHERAL_IRQ(22) IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_CAN1>;
resets = <&ccu RST_BUS_CAN1>;
status = "disabled";
};
}; };
}; };
...@@ -190,10 +190,10 @@ config CAN_SLCAN ...@@ -190,10 +190,10 @@ config CAN_SLCAN
config CAN_SUN4I config CAN_SUN4I
tristate "Allwinner A10 CAN controller" tristate "Allwinner A10 CAN controller"
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST depends on MACH_SUN4I || MACH_SUN7I || RISCV || COMPILE_TEST
help help
Say Y here if you want to use CAN controller found on Allwinner Say Y here if you want to use CAN controller found on Allwinner
A10/A20 SoCs. A10/A20/D1 SoCs.
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called sun4i_can. be called sun4i_can.
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
......
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2014 Protonic Holland, /* Copyright (c) 2014 Protonic Holland,
* David Jander * David Jander
* Copyright (C) 2014-2021 Pengutronix, * Copyright (C) 2014-2021, 2023 Pengutronix,
* Marc Kleine-Budde <kernel@pengutronix.de> * Marc Kleine-Budde <kernel@pengutronix.de>
*/ */
...@@ -240,9 +240,10 @@ int can_rx_offload_queue_timestamp(struct can_rx_offload *offload, ...@@ -240,9 +240,10 @@ int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
} }
EXPORT_SYMBOL_GPL(can_rx_offload_queue_timestamp); EXPORT_SYMBOL_GPL(can_rx_offload_queue_timestamp);
unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, unsigned int
unsigned int idx, u32 timestamp, can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
unsigned int *frame_len_ptr) unsigned int idx, u32 timestamp,
unsigned int *frame_len_ptr)
{ {
struct net_device *dev = offload->dev; struct net_device *dev = offload->dev;
struct net_device_stats *stats = &dev->stats; struct net_device_stats *stats = &dev->stats;
...@@ -262,7 +263,7 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, ...@@ -262,7 +263,7 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
return len; return len;
} }
EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb); EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb_queue_timestamp);
int can_rx_offload_queue_tail(struct can_rx_offload *offload, int can_rx_offload_queue_tail(struct can_rx_offload *offload,
struct sk_buff *skb) struct sk_buff *skb)
...@@ -279,6 +280,31 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload, ...@@ -279,6 +280,31 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload,
} }
EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail); EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
unsigned int
can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
unsigned int idx,
unsigned int *frame_len_ptr)
{
struct net_device *dev = offload->dev;
struct net_device_stats *stats = &dev->stats;
struct sk_buff *skb;
unsigned int len;
int err;
skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
if (!skb)
return 0;
err = can_rx_offload_queue_tail(offload, skb);
if (err) {
stats->rx_errors++;
stats->tx_fifo_errors++;
}
return len;
}
EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb_queue_tail);
void can_rx_offload_irq_finish(struct can_rx_offload *offload) void can_rx_offload_irq_finish(struct can_rx_offload *offload)
{ {
unsigned long flags; unsigned long flags;
......
...@@ -1097,8 +1097,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) ...@@ -1097,8 +1097,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
handled = IRQ_HANDLED; handled = IRQ_HANDLED;
stats->tx_bytes += stats->tx_bytes +=
can_rx_offload_get_echo_skb(&priv->offload, 0, can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload, 0,
reg_ctrl << 16, NULL); reg_ctrl << 16, NULL);
stats->tx_packets++; stats->tx_packets++;
/* after sending a RTR frame MB is in RX mode */ /* after sending a RTR frame MB is in RX mode */
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -1017,10 +1016,10 @@ static void m_can_tx_update_stats(struct m_can_classdev *cdev, ...@@ -1017,10 +1016,10 @@ static void m_can_tx_update_stats(struct m_can_classdev *cdev,
if (cdev->is_peripheral) if (cdev->is_peripheral)
stats->tx_bytes += stats->tx_bytes +=
can_rx_offload_get_echo_skb(&cdev->offload, can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload,
msg_mark, msg_mark,
timestamp, timestamp,
NULL); NULL);
else else
stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL);
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include "sja1000.h" #include "sja1000.h"
......
...@@ -111,9 +111,9 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, ...@@ -111,9 +111,9 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
if (skb) if (skb)
mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts); mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts);
stats->tx_bytes += stats->tx_bytes +=
can_rx_offload_get_echo_skb(&priv->offload, can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
tef_tail, hw_tef_obj->ts, tef_tail, hw_tef_obj->ts,
frame_len_ptr); frame_len_ptr);
stats->tx_packets++; stats->tx_packets++;
priv->tef->tail++; priv->tef->tail++;
......
...@@ -59,7 +59,6 @@ ...@@ -59,7 +59,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
...@@ -91,6 +90,8 @@ ...@@ -91,6 +90,8 @@
#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */ #define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */
#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */ #define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */
#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */ #define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */
#define SUN4I_REG_ACPC_ADDR_D1 0x0028 /* CAN Acceptance Code 0 on the D1 */
#define SUN4I_REG_ACPM_ADDR_D1 0x002C /* CAN Acceptance Mask 0 on the D1 */
#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */ #define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */
#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */ #define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */
...@@ -205,9 +206,11 @@ ...@@ -205,9 +206,11 @@
* struct sun4ican_quirks - Differences between SoC variants. * struct sun4ican_quirks - Differences between SoC variants.
* *
* @has_reset: SoC needs reset deasserted. * @has_reset: SoC needs reset deasserted.
* @acp_offset: Offset of ACPC and ACPM registers
*/ */
struct sun4ican_quirks { struct sun4ican_quirks {
bool has_reset; bool has_reset;
int acp_offset;
}; };
struct sun4ican_priv { struct sun4ican_priv {
...@@ -216,6 +219,7 @@ struct sun4ican_priv { ...@@ -216,6 +219,7 @@ struct sun4ican_priv {
struct clk *clk; struct clk *clk;
struct reset_control *reset; struct reset_control *reset;
spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */
int acp_offset;
}; };
static const struct can_bittiming_const sun4ican_bittiming_const = { static const struct can_bittiming_const sun4ican_bittiming_const = {
...@@ -338,8 +342,8 @@ static int sun4i_can_start(struct net_device *dev) ...@@ -338,8 +342,8 @@ static int sun4i_can_start(struct net_device *dev)
} }
/* set filters - we accept all */ /* set filters - we accept all */
writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR); writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR + priv->acp_offset);
writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR); writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR + priv->acp_offset);
/* clear error counters and error code capture */ /* clear error counters and error code capture */
writel(0, priv->base + SUN4I_REG_ERRC_ADDR); writel(0, priv->base + SUN4I_REG_ERRC_ADDR);
...@@ -768,10 +772,17 @@ static const struct ethtool_ops sun4ican_ethtool_ops = { ...@@ -768,10 +772,17 @@ static const struct ethtool_ops sun4ican_ethtool_ops = {
static const struct sun4ican_quirks sun4ican_quirks_a10 = { static const struct sun4ican_quirks sun4ican_quirks_a10 = {
.has_reset = false, .has_reset = false,
.acp_offset = 0,
}; };
static const struct sun4ican_quirks sun4ican_quirks_r40 = { static const struct sun4ican_quirks sun4ican_quirks_r40 = {
.has_reset = true, .has_reset = true,
.acp_offset = 0,
};
static const struct sun4ican_quirks sun4ican_quirks_d1 = {
.has_reset = true,
.acp_offset = (SUN4I_REG_ACPC_ADDR_D1 - SUN4I_REG_ACPC_ADDR),
}; };
static const struct of_device_id sun4ican_of_match[] = { static const struct of_device_id sun4ican_of_match[] = {
...@@ -784,6 +795,9 @@ static const struct of_device_id sun4ican_of_match[] = { ...@@ -784,6 +795,9 @@ static const struct of_device_id sun4ican_of_match[] = {
}, { }, {
.compatible = "allwinner,sun8i-r40-can", .compatible = "allwinner,sun8i-r40-can",
.data = &sun4ican_quirks_r40 .data = &sun4ican_quirks_r40
}, {
.compatible = "allwinner,sun20i-d1-can",
.data = &sun4ican_quirks_d1
}, { }, {
/* sentinel */ /* sentinel */
}, },
...@@ -870,6 +884,7 @@ static int sun4ican_probe(struct platform_device *pdev) ...@@ -870,6 +884,7 @@ static int sun4ican_probe(struct platform_device *pdev)
priv->base = addr; priv->base = addr;
priv->clk = clk; priv->clk = clk;
priv->reset = reset; priv->reset = reset;
priv->acp_offset = quirks->acp_offset;
spin_lock_init(&priv->cmdreg_lock); spin_lock_init(&priv->cmdreg_lock);
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
...@@ -907,4 +922,4 @@ module_platform_driver(sun4i_can_driver); ...@@ -907,4 +922,4 @@ module_platform_driver(sun4i_can_driver);
MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>"); MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>");
MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>"); MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20)"); MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20/D1)");
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
...@@ -748,8 +747,8 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) ...@@ -748,8 +747,8 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
spin_unlock_irqrestore(&priv->mbx_lock, flags); spin_unlock_irqrestore(&priv->mbx_lock, flags);
stamp = hecc_read_stamp(priv, mbxno); stamp = hecc_read_stamp(priv, mbxno);
stats->tx_bytes += stats->tx_bytes +=
can_rx_offload_get_echo_skb(&priv->offload, can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
mbxno, stamp, NULL); mbxno, stamp, NULL);
stats->tx_packets++; stats->tx_packets++;
--priv->tx_tail; --priv->tx_tail;
} }
......
...@@ -52,6 +52,7 @@ config CAN_F81604 ...@@ -52,6 +52,7 @@ config CAN_F81604
config CAN_GS_USB config CAN_GS_USB
tristate "Geschwister Schneider UG and candleLight compatible interfaces" tristate "Geschwister Schneider UG and candleLight compatible interfaces"
select CAN_RX_OFFLOAD
help help
This driver supports the Geschwister Schneider and This driver supports the Geschwister Schneider and
bytewerk.org candleLight compatible bytewerk.org candleLight compatible
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright (C) 2013-2016 Geschwister Schneider Technologie-, * Copyright (C) 2013-2016 Geschwister Schneider Technologie-,
* Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt).
* Copyright (C) 2016 Hubert Denkmair * Copyright (C) 2016 Hubert Denkmair
* Copyright (c) 2023 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
* *
* Many thanks to all socketcan devs! * Many thanks to all socketcan devs!
*/ */
...@@ -24,6 +25,7 @@ ...@@ -24,6 +25,7 @@
#include <linux/can.h> #include <linux/can.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
#include <linux/can/error.h> #include <linux/can/error.h>
#include <linux/can/rx-offload.h>
/* Device specific constants */ /* Device specific constants */
#define USB_GS_USB_1_VENDOR_ID 0x1d50 #define USB_GS_USB_1_VENDOR_ID 0x1d50
...@@ -282,6 +284,8 @@ struct gs_host_frame { ...@@ -282,6 +284,8 @@ struct gs_host_frame {
#define GS_MAX_TX_URBS 10 #define GS_MAX_TX_URBS 10
/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ /* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */
#define GS_MAX_RX_URBS 30 #define GS_MAX_RX_URBS 30
#define GS_NAPI_WEIGHT 32
/* Maximum number of interfaces the driver supports per device. /* Maximum number of interfaces the driver supports per device.
* Current hardware only supports 3 interfaces. The future may vary. * Current hardware only supports 3 interfaces. The future may vary.
*/ */
...@@ -295,6 +299,7 @@ struct gs_tx_context { ...@@ -295,6 +299,7 @@ struct gs_tx_context {
struct gs_can { struct gs_can {
struct can_priv can; /* must be the first member */ struct can_priv can; /* must be the first member */
struct can_rx_offload offload;
struct gs_usb *parent; struct gs_usb *parent;
struct net_device *netdev; struct net_device *netdev;
...@@ -506,27 +511,64 @@ static void gs_update_state(struct gs_can *dev, struct can_frame *cf) ...@@ -506,27 +511,64 @@ static void gs_update_state(struct gs_can *dev, struct can_frame *cf)
} }
} }
static void gs_usb_set_timestamp(struct gs_can *dev, struct sk_buff *skb, static u32 gs_usb_set_timestamp(struct gs_can *dev, struct sk_buff *skb,
const struct gs_host_frame *hf) const struct gs_host_frame *hf)
{ {
u32 timestamp; u32 timestamp;
if (!(dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP))
return;
if (hf->flags & GS_CAN_FLAG_FD) if (hf->flags & GS_CAN_FLAG_FD)
timestamp = le32_to_cpu(hf->canfd_ts->timestamp_us); timestamp = le32_to_cpu(hf->canfd_ts->timestamp_us);
else else
timestamp = le32_to_cpu(hf->classic_can_ts->timestamp_us); timestamp = le32_to_cpu(hf->classic_can_ts->timestamp_us);
gs_usb_skb_set_timestamp(dev, skb, timestamp); if (skb)
gs_usb_skb_set_timestamp(dev, skb, timestamp);
return timestamp;
}
static void gs_usb_rx_offload(struct gs_can *dev, struct sk_buff *skb,
const struct gs_host_frame *hf)
{
struct can_rx_offload *offload = &dev->offload;
int rc;
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) {
const u32 ts = gs_usb_set_timestamp(dev, skb, hf);
rc = can_rx_offload_queue_timestamp(offload, skb, ts);
} else {
rc = can_rx_offload_queue_tail(offload, skb);
}
if (rc)
dev->netdev->stats.rx_fifo_errors++;
}
static unsigned int
gs_usb_get_echo_skb(struct gs_can *dev, struct sk_buff *skb,
const struct gs_host_frame *hf)
{
struct can_rx_offload *offload = &dev->offload;
const u32 echo_id = hf->echo_id;
unsigned int len;
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) {
const u32 ts = gs_usb_set_timestamp(dev, skb, hf);
len = can_rx_offload_get_echo_skb_queue_timestamp(offload, echo_id,
ts, NULL);
} else {
len = can_rx_offload_get_echo_skb_queue_tail(offload, echo_id,
NULL);
}
return; return len;
} }
static void gs_usb_receive_bulk_callback(struct urb *urb) static void gs_usb_receive_bulk_callback(struct urb *urb)
{ {
struct gs_usb *usbcan = urb->context; struct gs_usb *parent = urb->context;
struct gs_can *dev; struct gs_can *dev;
struct net_device *netdev; struct net_device *netdev;
int rc; int rc;
...@@ -537,7 +579,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -537,7 +579,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
struct canfd_frame *cfd; struct canfd_frame *cfd;
struct sk_buff *skb; struct sk_buff *skb;
BUG_ON(!usbcan); BUG_ON(!parent);
switch (urb->status) { switch (urb->status) {
case 0: /* success */ case 0: /* success */
...@@ -554,7 +596,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -554,7 +596,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
if (hf->channel >= GS_MAX_INTF) if (hf->channel >= GS_MAX_INTF)
goto device_detach; goto device_detach;
dev = usbcan->canch[hf->channel]; dev = parent->canch[hf->channel];
netdev = dev->netdev; netdev = dev->netdev;
stats = &netdev->stats; stats = &netdev->stats;
...@@ -567,7 +609,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -567,7 +609,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
if (hf->echo_id == -1) { /* normal rx */ if (hf->echo_id == -1) { /* normal rx */
if (hf->flags & GS_CAN_FLAG_FD) { if (hf->flags & GS_CAN_FLAG_FD) {
skb = alloc_canfd_skb(dev->netdev, &cfd); skb = alloc_canfd_skb(netdev, &cfd);
if (!skb) if (!skb)
return; return;
...@@ -580,7 +622,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -580,7 +622,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
memcpy(cfd->data, hf->canfd->data, cfd->len); memcpy(cfd->data, hf->canfd->data, cfd->len);
} else { } else {
skb = alloc_can_skb(dev->netdev, &cf); skb = alloc_can_skb(netdev, &cf);
if (!skb) if (!skb)
return; return;
...@@ -594,12 +636,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -594,12 +636,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
gs_update_state(dev, cf); gs_update_state(dev, cf);
} }
gs_usb_set_timestamp(dev, skb, hf); gs_usb_rx_offload(dev, skb, hf);
netdev->stats.rx_packets++;
netdev->stats.rx_bytes += hf->can_dlc;
netif_rx(skb);
} else { /* echo_id == hf->echo_id */ } else { /* echo_id == hf->echo_id */
if (hf->echo_id >= GS_MAX_TX_URBS) { if (hf->echo_id >= GS_MAX_TX_URBS) {
netdev_err(netdev, netdev_err(netdev,
...@@ -619,12 +656,8 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -619,12 +656,8 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
} }
skb = dev->can.echo_skb[hf->echo_id]; skb = dev->can.echo_skb[hf->echo_id];
gs_usb_set_timestamp(dev, skb, hf); stats->tx_packets++;
stats->tx_bytes += gs_usb_get_echo_skb(dev, skb, hf);
netdev->stats.tx_packets++;
netdev->stats.tx_bytes += can_get_echo_skb(netdev, hf->echo_id,
NULL);
gs_free_tx_context(txc); gs_free_tx_context(txc);
atomic_dec(&dev->active_tx_urbs); atomic_dec(&dev->active_tx_urbs);
...@@ -633,6 +666,9 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -633,6 +666,9 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
} }
if (hf->flags & GS_CAN_FLAG_OVERFLOW) { if (hf->flags & GS_CAN_FLAG_OVERFLOW) {
stats->rx_over_errors++;
stats->rx_errors++;
skb = alloc_can_err_skb(netdev, &cf); skb = alloc_can_err_skb(netdev, &cf);
if (!skb) if (!skb)
goto resubmit_urb; goto resubmit_urb;
...@@ -640,25 +676,26 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) ...@@ -640,25 +676,26 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->len = CAN_ERR_DLC; cf->len = CAN_ERR_DLC;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
stats->rx_over_errors++;
stats->rx_errors++; gs_usb_rx_offload(dev, skb, hf);
netif_rx(skb);
} }
resubmit_urb: can_rx_offload_irq_finish(&dev->offload);
usb_fill_bulk_urb(urb, usbcan->udev,
usb_rcvbulkpipe(usbcan->udev, GS_USB_ENDPOINT_IN), resubmit_urb:
usb_fill_bulk_urb(urb, parent->udev,
usb_rcvbulkpipe(parent->udev, GS_USB_ENDPOINT_IN),
hf, dev->parent->hf_size_rx, hf, dev->parent->hf_size_rx,
gs_usb_receive_bulk_callback, usbcan); gs_usb_receive_bulk_callback, parent);
rc = usb_submit_urb(urb, GFP_ATOMIC); rc = usb_submit_urb(urb, GFP_ATOMIC);
/* USB failure take down all interfaces */ /* USB failure take down all interfaces */
if (rc == -ENODEV) { if (rc == -ENODEV) {
device_detach: device_detach:
for (rc = 0; rc < GS_MAX_INTF; rc++) { for (rc = 0; rc < GS_MAX_INTF; rc++) {
if (usbcan->canch[rc]) if (parent->canch[rc])
netif_device_detach(usbcan->canch[rc]->netdev); netif_device_detach(parent->canch[rc]->netdev);
} }
} }
} }
...@@ -742,10 +779,8 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, ...@@ -742,10 +779,8 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
goto nomem_urb; goto nomem_urb;
hf = kmalloc(dev->hf_size_tx, GFP_ATOMIC); hf = kmalloc(dev->hf_size_tx, GFP_ATOMIC);
if (!hf) { if (!hf)
netdev_err(netdev, "No memory left for USB buffer\n");
goto nomem_hf; goto nomem_hf;
}
idx = txc->echo_id; idx = txc->echo_id;
...@@ -818,12 +853,12 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, ...@@ -818,12 +853,12 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK; return NETDEV_TX_OK;
badidx: badidx:
kfree(hf); kfree(hf);
nomem_hf: nomem_hf:
usb_free_urb(urb); usb_free_urb(urb);
nomem_urb: nomem_urb:
gs_free_tx_context(txc); gs_free_tx_context(txc);
dev_kfree_skb(skb); dev_kfree_skb(skb);
stats->tx_dropped++; stats->tx_dropped++;
...@@ -860,6 +895,8 @@ static int gs_can_open(struct net_device *netdev) ...@@ -860,6 +895,8 @@ static int gs_can_open(struct net_device *netdev)
dev->hf_size_tx = struct_size(hf, classic_can, 1); dev->hf_size_tx = struct_size(hf, classic_can, 1);
} }
can_rx_offload_enable(&dev->offload);
if (!parent->active_channels) { if (!parent->active_channels) {
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
gs_usb_timestamp_init(parent); gs_usb_timestamp_init(parent);
...@@ -878,8 +915,6 @@ static int gs_can_open(struct net_device *netdev) ...@@ -878,8 +915,6 @@ static int gs_can_open(struct net_device *netdev)
buf = kmalloc(dev->parent->hf_size_rx, buf = kmalloc(dev->parent->hf_size_rx,
GFP_KERNEL); GFP_KERNEL);
if (!buf) { if (!buf) {
netdev_err(netdev,
"No memory left for USB buffer\n");
rc = -ENOMEM; rc = -ENOMEM;
goto out_usb_free_urb; goto out_usb_free_urb;
} }
...@@ -902,7 +937,8 @@ static int gs_can_open(struct net_device *netdev) ...@@ -902,7 +937,8 @@ static int gs_can_open(struct net_device *netdev)
netif_device_detach(dev->netdev); netif_device_detach(dev->netdev);
netdev_err(netdev, netdev_err(netdev,
"usb_submit failed (err=%d)\n", rc); "usb_submit_urb() failed, error %pe\n",
ERR_PTR(rc));
goto out_usb_unanchor_urb; goto out_usb_unanchor_urb;
} }
...@@ -969,6 +1005,7 @@ static int gs_can_open(struct net_device *netdev) ...@@ -969,6 +1005,7 @@ static int gs_can_open(struct net_device *netdev)
gs_usb_timestamp_stop(parent); gs_usb_timestamp_stop(parent);
} }
can_rx_offload_disable(&dev->offload);
close_candev(netdev); close_candev(netdev);
return rc; return rc;
...@@ -1033,9 +1070,7 @@ static int gs_can_close(struct net_device *netdev) ...@@ -1033,9 +1070,7 @@ static int gs_can_close(struct net_device *netdev)
dev->can.state = CAN_STATE_STOPPED; dev->can.state = CAN_STATE_STOPPED;
/* reset the device */ /* reset the device */
rc = gs_cmd_reset(dev); gs_cmd_reset(dev);
if (rc < 0)
netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc);
/* reset tx contexts */ /* reset tx contexts */
for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
...@@ -1043,6 +1078,8 @@ static int gs_can_close(struct net_device *netdev) ...@@ -1043,6 +1078,8 @@ static int gs_can_close(struct net_device *netdev)
dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
} }
can_rx_offload_disable(&dev->offload);
/* close the netdev */ /* close the netdev */
close_candev(netdev); close_candev(netdev);
...@@ -1342,6 +1379,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, ...@@ -1342,6 +1379,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
dev->can.data_bittiming_const = &dev->data_bt_const; dev->can.data_bittiming_const = &dev->data_bt_const;
} }
can_rx_offload_add_manual(netdev, &dev->offload, GS_NAPI_WEIGHT);
SET_NETDEV_DEV(netdev, &intf->dev); SET_NETDEV_DEV(netdev, &intf->dev);
rc = register_candev(dev->netdev); rc = register_candev(dev->netdev);
...@@ -1349,12 +1387,14 @@ static struct gs_can *gs_make_candev(unsigned int channel, ...@@ -1349,12 +1387,14 @@ static struct gs_can *gs_make_candev(unsigned int channel,
dev_err(&intf->dev, dev_err(&intf->dev,
"Couldn't register candev for channel %d (%pe)\n", "Couldn't register candev for channel %d (%pe)\n",
channel, ERR_PTR(rc)); channel, ERR_PTR(rc));
goto out_free_candev; goto out_can_rx_offload_del;
} }
return dev; return dev;
out_free_candev: out_can_rx_offload_del:
can_rx_offload_del(&dev->offload);
out_free_candev:
free_candev(dev->netdev); free_candev(dev->netdev);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
...@@ -1362,7 +1402,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, ...@@ -1362,7 +1402,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
static void gs_destroy_candev(struct gs_can *dev) static void gs_destroy_candev(struct gs_can *dev)
{ {
unregister_candev(dev->netdev); unregister_candev(dev->netdev);
usb_kill_anchored_urbs(&dev->tx_submitted); can_rx_offload_del(&dev->offload);
free_candev(dev->netdev); free_candev(dev->netdev);
} }
...@@ -1371,7 +1411,7 @@ static int gs_usb_probe(struct usb_interface *intf, ...@@ -1371,7 +1411,7 @@ static int gs_usb_probe(struct usb_interface *intf,
{ {
struct usb_device *udev = interface_to_usbdev(intf); struct usb_device *udev = interface_to_usbdev(intf);
struct gs_host_frame *hf; struct gs_host_frame *hf;
struct gs_usb *dev; struct gs_usb *parent;
struct gs_host_config hconf = { struct gs_host_config hconf = {
.byte_order = cpu_to_le32(0x0000beef), .byte_order = cpu_to_le32(0x0000beef),
}; };
...@@ -1414,49 +1454,49 @@ static int gs_usb_probe(struct usb_interface *intf, ...@@ -1414,49 +1454,49 @@ static int gs_usb_probe(struct usb_interface *intf,
return -EINVAL; return -EINVAL;
} }
dev = kzalloc(sizeof(*dev), GFP_KERNEL); parent = kzalloc(sizeof(*parent), GFP_KERNEL);
if (!dev) if (!parent)
return -ENOMEM; return -ENOMEM;
init_usb_anchor(&dev->rx_submitted); init_usb_anchor(&parent->rx_submitted);
usb_set_intfdata(intf, dev); usb_set_intfdata(intf, parent);
dev->udev = udev; parent->udev = udev;
for (i = 0; i < icount; i++) { for (i = 0; i < icount; i++) {
unsigned int hf_size_rx = 0; unsigned int hf_size_rx = 0;
dev->canch[i] = gs_make_candev(i, intf, &dconf); parent->canch[i] = gs_make_candev(i, intf, &dconf);
if (IS_ERR_OR_NULL(dev->canch[i])) { if (IS_ERR_OR_NULL(parent->canch[i])) {
/* save error code to return later */ /* save error code to return later */
rc = PTR_ERR(dev->canch[i]); rc = PTR_ERR(parent->canch[i]);
/* on failure destroy previously created candevs */ /* on failure destroy previously created candevs */
icount = i; icount = i;
for (i = 0; i < icount; i++) for (i = 0; i < icount; i++)
gs_destroy_candev(dev->canch[i]); gs_destroy_candev(parent->canch[i]);
usb_kill_anchored_urbs(&dev->rx_submitted); usb_kill_anchored_urbs(&parent->rx_submitted);
kfree(dev); kfree(parent);
return rc; return rc;
} }
dev->canch[i]->parent = dev; parent->canch[i]->parent = parent;
/* set RX packet size based on FD and if hardware /* set RX packet size based on FD and if hardware
* timestamps are supported. * timestamps are supported.
*/ */
if (dev->canch[i]->can.ctrlmode_supported & CAN_CTRLMODE_FD) { if (parent->canch[i]->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
if (dev->canch[i]->feature & GS_CAN_FEATURE_HW_TIMESTAMP) if (parent->canch[i]->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
hf_size_rx = struct_size(hf, canfd_ts, 1); hf_size_rx = struct_size(hf, canfd_ts, 1);
else else
hf_size_rx = struct_size(hf, canfd, 1); hf_size_rx = struct_size(hf, canfd, 1);
} else { } else {
if (dev->canch[i]->feature & GS_CAN_FEATURE_HW_TIMESTAMP) if (parent->canch[i]->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
hf_size_rx = struct_size(hf, classic_can_ts, 1); hf_size_rx = struct_size(hf, classic_can_ts, 1);
else else
hf_size_rx = struct_size(hf, classic_can, 1); hf_size_rx = struct_size(hf, classic_can, 1);
} }
dev->hf_size_rx = max(dev->hf_size_rx, hf_size_rx); parent->hf_size_rx = max(parent->hf_size_rx, hf_size_rx);
} }
return 0; return 0;
...@@ -1464,22 +1504,21 @@ static int gs_usb_probe(struct usb_interface *intf, ...@@ -1464,22 +1504,21 @@ static int gs_usb_probe(struct usb_interface *intf,
static void gs_usb_disconnect(struct usb_interface *intf) static void gs_usb_disconnect(struct usb_interface *intf)
{ {
struct gs_usb *dev = usb_get_intfdata(intf); struct gs_usb *parent = usb_get_intfdata(intf);
unsigned int i; unsigned int i;
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
if (!dev) { if (!parent) {
dev_err(&intf->dev, "Disconnect (nodata)\n"); dev_err(&intf->dev, "Disconnect (nodata)\n");
return; return;
} }
for (i = 0; i < GS_MAX_INTF; i++) for (i = 0; i < GS_MAX_INTF; i++)
if (dev->canch[i]) if (parent->canch[i])
gs_destroy_candev(dev->canch[i]); gs_destroy_candev(parent->canch[i]);
usb_kill_anchored_urbs(&dev->rx_submitted); kfree(parent);
kfree(dev);
} }
static const struct usb_device_id gs_usb_table[] = { static const struct usb_device_id gs_usb_table[] = {
......
...@@ -214,19 +214,6 @@ void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time) ...@@ -214,19 +214,6 @@ void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time)
} }
} }
/*
* post received skb after having set any hw timestamp
*/
int peak_usb_netif_rx(struct sk_buff *skb,
struct peak_time_ref *time_ref, u32 ts_low)
{
struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
peak_usb_get_ts_time(time_ref, ts_low, &hwts->hwtstamp);
return netif_rx(skb);
}
/* post received skb with native 64-bit hw timestamp */ /* post received skb with native 64-bit hw timestamp */
int peak_usb_netif_rx_64(struct sk_buff *skb, u32 ts_low, u32 ts_high) int peak_usb_netif_rx_64(struct sk_buff *skb, u32 ts_low, u32 ts_high)
{ {
......
...@@ -142,8 +142,6 @@ void peak_usb_init_time_ref(struct peak_time_ref *time_ref, ...@@ -142,8 +142,6 @@ void peak_usb_init_time_ref(struct peak_time_ref *time_ref,
void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now);
void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now);
void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *tv); void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *tv);
int peak_usb_netif_rx(struct sk_buff *skb,
struct peak_time_ref *time_ref, u32 ts_low);
int peak_usb_netif_rx_64(struct sk_buff *skb, u32 ts_low, u32 ts_high); int peak_usb_netif_rx_64(struct sk_buff *skb, u32 ts_low, u32 ts_high);
void peak_usb_async_complete(struct urb *urb); void peak_usb_async_complete(struct urb *urb);
void peak_usb_restart_complete(struct peak_usb_device *dev); void peak_usb_restart_complete(struct peak_usb_device *dev);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* linux/can/rx-offload.h * linux/can/rx-offload.h
* *
* Copyright (c) 2014 David Jander, Protonic Holland * Copyright (c) 2014 David Jander, Protonic Holland
* Copyright (c) 2014-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de> * Copyright (c) 2014-2017, 2023 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
*/ */
#ifndef _CAN_RX_OFFLOAD_H #ifndef _CAN_RX_OFFLOAD_H
...@@ -44,11 +44,14 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, ...@@ -44,11 +44,14 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload); int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
int can_rx_offload_queue_timestamp(struct can_rx_offload *offload, int can_rx_offload_queue_timestamp(struct can_rx_offload *offload,
struct sk_buff *skb, u32 timestamp); struct sk_buff *skb, u32 timestamp);
unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, unsigned int can_rx_offload_get_echo_skb_queue_timestamp(struct can_rx_offload *offload,
unsigned int idx, u32 timestamp, unsigned int idx, u32 timestamp,
unsigned int *frame_len_ptr); unsigned int *frame_len_ptr);
int can_rx_offload_queue_tail(struct can_rx_offload *offload, int can_rx_offload_queue_tail(struct can_rx_offload *offload,
struct sk_buff *skb); struct sk_buff *skb);
unsigned int can_rx_offload_get_echo_skb_queue_tail(struct can_rx_offload *offload,
unsigned int idx,
unsigned int *frame_len_ptr);
void can_rx_offload_irq_finish(struct can_rx_offload *offload); void can_rx_offload_irq_finish(struct can_rx_offload *offload);
void can_rx_offload_threaded_irq_finish(struct can_rx_offload *offload); void can_rx_offload_threaded_irq_finish(struct can_rx_offload *offload);
void can_rx_offload_del(struct can_rx_offload *offload); void can_rx_offload_del(struct can_rx_offload *offload);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment