From nobody Mon Dec 1 23:03:13 2025 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AF2812FFDE0 for ; Wed, 26 Nov 2025 10:17:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764152228; cv=none; b=mp5ErgZ3fj0mI+do2YqJ0YcVbW4yyO/uVg1sC7lsg/rxxFY0DD/vH/yGKihwAZL1uqUWZt0Sf2tN/mcu4TJ1hnprY0u1STQOaYntUZe6guapK7Rr1oMrN66nEtn/yxxR25BGmQB4Ovr5+egggsTV6vBqz+hs2C0SVwuljmNetN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764152228; c=relaxed/simple; bh=dHh+0CxMZuabS8R/sUctAv5QPvgEtCZMX0p9pffH1DE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FNYMgyABAvpvKLfO7s5Xk9ogp1VbnruKe2T8Z7CQ0nLQAr2zp7gerqEEQlLoZe23+sKzRgoM5md0MUpke1Oav7kX4M5qZZSHEXtnUrQhO8EvzvcdDLYDfMEMfxfQHKW5n7iNVJwKTUYY2x7KFzD6ncf19WbnOxdXCJu1E5yYLZw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1vOCZw-0000SF-7L; Wed, 26 Nov 2025 11:16:52 +0100 Received: from moin.white.stw.pengutronix.de ([2a0a:edc0:0:b01:1d::7b] helo=bjornoya.blackshift.org) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vOCZv-002aTN-1y; Wed, 26 Nov 2025 11:16:51 +0100 Received: from hardanger.blackshift.org (p54b152ce.dip0.t-ipconnect.de [84.177.82.206]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) (Authenticated sender: mkl-all@blackshift.org) by smtp.blackshift.org (Postfix) with ESMTPSA id 30C194A88E0; Wed, 26 Nov 2025 10:16:51 +0000 (UTC) From: Marc Kleine-Budde Date: Wed, 26 Nov 2025 11:16:16 +0100 Subject: [PATCH can-next v8 15/17] can: add dummy_can driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251126-canxl-v8-15-e7e3eb74f889@pengutronix.de> References: <20251126-canxl-v8-0-e7e3eb74f889@pengutronix.de> In-Reply-To: <20251126-canxl-v8-0-e7e3eb74f889@pengutronix.de> To: Marc Kleine-Budde , Vincent Mailhol , Oliver Hartkopp Cc: kernel@pengutronix.de, linux-can@vger.kernel.org, linux-kernel@vger.kernel.org, =?utf-8?q?St=C3=A9phane_Grosjean?= X-Mailer: b4 0.15-dev-a6db3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11015; i=mkl@pengutronix.de; h=from:subject:message-id; bh=MEu1fb5xA2EjspKTS/EWNMdWezEhy1B7PZP5yjBWc5I=; b=owEBbQGS/pANAwAKAQx0Zd/5kJGcAcsmYgBpJtOM7gAF3b78Msg/sNbDVfaOdPuYmOmp27jbP ihxhe2oKpqJATMEAAEKAB0WIQSf+wzYr2eoX/wVbPMMdGXf+ZCRnAUCaSbTjAAKCRAMdGXf+ZCR nMo2CACVXMFjEmmQtUsDVLvvorExZcxSEKUi5JueHeYW2uMvcMBTw9N7O5OJhFwvbRRkwZdm08k fgMX60AXlDwvdVjOElmEl/yq6zXnDYZ7/ea6dJgr+UDf2RxEsMzqzjEtPAnpWYcZADw8VqHzcGf +IrEXz2I2wCGQf70gLZ7v9BFdicbHErzPVBCrnKDvIbv8ic/dIGt1rgENZrM4Qo8LQiDSaznsxP 6sruW10YYwc+iVvWa0JM6j5CBmrGAh+DDwUyUalPopK75mXY2/e9n/ooTo2QwTkGRFEdrL+A0vF 9tDU+opFrWtcatfyEpyF4Y9nUbmK/07FyVr7ETjQua52K3YR X-Developer-Key: i=mkl@pengutronix.de; a=openpgp; fpr=C1400BA0B3989E6FBC7D5B5C2B5EE211C58AEA54 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: mkl@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org From: Vincent Mailhol During the development of CAN XL, we found the need of creating a dummy CAN XL driver in order to test the new netlink interface. While this code was initially intended to be some throwaway, it received some positive feedback. Add the dummy_can driver. This driver acts similarly to the vcan interface in the sense that it will echo back any packet it receives. The difference is that it exposes a set on bittiming parameters as a real device would and thus must be configured as if it was a real physical interface. The driver comes with a debug mode. If debug message are enabled (for example by enabling CONFIG_CAN_DEBUG_DEVICES), it will print in the kernel log all the bittiming values, similar to what a: ip --details link show can0 would do. This driver is mostly intended for debugging and testing, but some developers also may want to look at it as a simple reference implementation. Signed-off-by: Vincent Mailhol Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 17 +++ drivers/net/can/Makefile | 1 + drivers/net/can/dummy_can.c | 285 ++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 303 insertions(+) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d43d56694667..e15e320db476 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -124,6 +124,23 @@ config CAN_CAN327 =20 If this driver is built as a module, it will be called can327. =20 +config CAN_DUMMY + tristate "Dummy CAN" + help + A dummy CAN module supporting Classical CAN, CAN FD and CAN XL. It + exposes bittiming values which can be configured through the netlink + interface. + + The module will simply echo any frame sent to it. If debug messages + are activated, it prints all the CAN bittiming information in the + kernel log. Aside from that it does nothing. + + This is convenient for testing the CAN netlink interface. Most of the + users will never need this. If unsure, say NO. + + To compile this driver as a module, choose M here: the module will be + called dummy-can. + config CAN_FLEXCAN tristate "Support for Freescale FLEXCAN based chips" depends on OF || COLDFIRE || COMPILE_TEST diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 56138d8ddfd2..d7bc10a6b8ea 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_CAN_CAN327) +=3D can327.o obj-$(CONFIG_CAN_CC770) +=3D cc770/ obj-$(CONFIG_CAN_C_CAN) +=3D c_can/ obj-$(CONFIG_CAN_CTUCANFD) +=3D ctucanfd/ +obj-$(CONFIG_CAN_DUMMY) +=3D dummy_can.o obj-$(CONFIG_CAN_FLEXCAN) +=3D flexcan/ obj-$(CONFIG_CAN_GRCAN) +=3D grcan.o obj-$(CONFIG_CAN_IFI_CANFD) +=3D ifi_canfd/ diff --git a/drivers/net/can/dummy_can.c b/drivers/net/can/dummy_can.c new file mode 100644 index 000000000000..41953655e3d3 --- /dev/null +++ b/drivers/net/can/dummy_can.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Copyright (c) 2025 Vincent Mailhol */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct dummy_can { + struct can_priv can; + struct net_device *dev; +}; + +static struct dummy_can *dummy_can; + +static const struct can_bittiming_const dummy_can_bittiming_const =3D { + .name =3D "dummy_can CC", + .tseg1_min =3D 2, + .tseg1_max =3D 256, + .tseg2_min =3D 2, + .tseg2_max =3D 128, + .sjw_max =3D 128, + .brp_min =3D 1, + .brp_max =3D 512, + .brp_inc =3D 1 +}; + +static const struct can_bittiming_const dummy_can_fd_databittiming_const = =3D { + .name =3D "dummy_can FD", + .tseg1_min =3D 2, + .tseg1_max =3D 256, + .tseg2_min =3D 2, + .tseg2_max =3D 128, + .sjw_max =3D 128, + .brp_min =3D 1, + .brp_max =3D 512, + .brp_inc =3D 1 +}; + +static const struct can_tdc_const dummy_can_fd_tdc_const =3D { + .tdcv_min =3D 0, + .tdcv_max =3D 0, /* Manual mode not supported. */ + .tdco_min =3D 0, + .tdco_max =3D 127, + .tdcf_min =3D 0, + .tdcf_max =3D 127 +}; + +static const struct can_bittiming_const dummy_can_xl_databittiming_const = =3D { + .name =3D "dummy_can XL", + .tseg1_min =3D 2, + .tseg1_max =3D 256, + .tseg2_min =3D 2, + .tseg2_max =3D 128, + .sjw_max =3D 128, + .brp_min =3D 1, + .brp_max =3D 512, + .brp_inc =3D 1 +}; + +static const struct can_tdc_const dummy_can_xl_tdc_const =3D { + .tdcv_min =3D 0, + .tdcv_max =3D 0, /* Manual mode not supported. */ + .tdco_min =3D 0, + .tdco_max =3D 127, + .tdcf_min =3D 0, + .tdcf_max =3D 127 +}; + +static const struct can_pwm_const dummy_can_pwm_const =3D { + .pwms_min =3D 1, + .pwms_max =3D 8, + .pwml_min =3D 2, + .pwml_max =3D 24, + .pwmo_min =3D 0, + .pwmo_max =3D 16, +}; + +static void dummy_can_print_bittiming(struct net_device *dev, + struct can_bittiming *bt) +{ + netdev_dbg(dev, "\tbitrate: %u\n", bt->bitrate); + netdev_dbg(dev, "\tsample_point: %u\n", bt->sample_point); + netdev_dbg(dev, "\ttq: %u\n", bt->tq); + netdev_dbg(dev, "\tprop_seg: %u\n", bt->prop_seg); + netdev_dbg(dev, "\tphase_seg1: %u\n", bt->phase_seg1); + netdev_dbg(dev, "\tphase_seg2: %u\n", bt->phase_seg2); + netdev_dbg(dev, "\tsjw: %u\n", bt->sjw); + netdev_dbg(dev, "\tbrp: %u\n", bt->brp); +} + +static void dummy_can_print_tdc(struct net_device *dev, struct can_tdc *td= c) +{ + netdev_dbg(dev, "\t\ttdcv: %u\n", tdc->tdcv); + netdev_dbg(dev, "\t\ttdco: %u\n", tdc->tdco); + netdev_dbg(dev, "\t\ttdcf: %u\n", tdc->tdcf); +} + +static void dummy_can_print_pwm(struct net_device *dev, struct can_pwm *pw= m, + struct can_bittiming *dbt) +{ + netdev_dbg(dev, "\t\tpwms: %u\n", pwm->pwms); + netdev_dbg(dev, "\t\tpwml: %u\n", pwm->pwml); + netdev_dbg(dev, "\t\tpwmo: %u\n", pwm->pwmo); +} + +static void dummy_can_print_ctrlmode(struct net_device *dev) +{ + struct dummy_can *priv =3D netdev_priv(dev); + struct can_priv *can_priv =3D &priv->can; + unsigned long supported =3D can_priv->ctrlmode_supported; + u32 enabled =3D can_priv->ctrlmode; + + netdev_dbg(dev, "Control modes:\n"); + netdev_dbg(dev, "\tsupported: 0x%08x\n", (u32)supported); + netdev_dbg(dev, "\tenabled: 0x%08x\n", enabled); + + if (supported) { + int idx; + + netdev_dbg(dev, "\tlist:"); + for_each_set_bit(idx, &supported, BITS_PER_TYPE(u32)) + netdev_dbg(dev, "\t\t%s: %s\n", + can_get_ctrlmode_str(BIT(idx)), + enabled & BIT(idx) ? "on" : "off"); + } +} + +static void dummy_can_print_bittiming_info(struct net_device *dev) +{ + struct dummy_can *priv =3D netdev_priv(dev); + struct can_priv *can_priv =3D &priv->can; + + netdev_dbg(dev, "Clock frequency: %u\n", can_priv->clock.freq); + netdev_dbg(dev, "Maximum bitrate: %u\n", can_priv->bitrate_max); + netdev_dbg(dev, "MTU: %u\n", dev->mtu); + netdev_dbg(dev, "\n"); + + dummy_can_print_ctrlmode(dev); + netdev_dbg(dev, "\n"); + + netdev_dbg(dev, "Classical CAN nominal bittiming:\n"); + dummy_can_print_bittiming(dev, &can_priv->bittiming); + netdev_dbg(dev, "\n"); + + if (can_priv->ctrlmode & CAN_CTRLMODE_FD) { + netdev_dbg(dev, "CAN FD databittiming:\n"); + dummy_can_print_bittiming(dev, &can_priv->fd.data_bittiming); + if (can_fd_tdc_is_enabled(can_priv)) { + netdev_dbg(dev, "\tCAN FD TDC:\n"); + dummy_can_print_tdc(dev, &can_priv->fd.tdc); + } + } + netdev_dbg(dev, "\n"); + + if (can_priv->ctrlmode & CAN_CTRLMODE_XL) { + netdev_dbg(dev, "CAN XL databittiming:\n"); + dummy_can_print_bittiming(dev, &can_priv->xl.data_bittiming); + if (can_xl_tdc_is_enabled(can_priv)) { + netdev_dbg(dev, "\tCAN XL TDC:\n"); + dummy_can_print_tdc(dev, &can_priv->xl.tdc); + } + if (can_priv->ctrlmode & CAN_CTRLMODE_XL_TMS) { + netdev_dbg(dev, "\tCAN XL PWM:\n"); + dummy_can_print_pwm(dev, &can_priv->xl.pwm, + &can_priv->xl.data_bittiming); + } + } + netdev_dbg(dev, "\n"); +} + +static int dummy_can_netdev_open(struct net_device *dev) +{ + int ret; + struct can_priv *priv =3D netdev_priv(dev); + + dummy_can_print_bittiming_info(dev); + netdev_dbg(dev, "error-signalling is %s\n", + str_enabled_disabled(!can_dev_in_xl_only_mode(priv))); + + ret =3D open_candev(dev); + if (ret) + return ret; + netif_start_queue(dev); + netdev_dbg(dev, "dummy-can is up\n"); + + return 0; +} + +static int dummy_can_netdev_close(struct net_device *dev) +{ + netif_stop_queue(dev); + close_candev(dev); + netdev_dbg(dev, "dummy-can is down\n"); + + return 0; +} + +static netdev_tx_t dummy_can_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + if (can_dev_dropped_skb(dev, skb)) + return NETDEV_TX_OK; + + can_put_echo_skb(skb, dev, 0, 0); + dev->stats.tx_packets++; + dev->stats.tx_bytes +=3D can_get_echo_skb(dev, 0, NULL); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops dummy_can_netdev_ops =3D { + .ndo_open =3D dummy_can_netdev_open, + .ndo_stop =3D dummy_can_netdev_close, + .ndo_start_xmit =3D dummy_can_start_xmit, +}; + +static const struct ethtool_ops dummy_can_ethtool_ops =3D { + .get_ts_info =3D ethtool_op_get_ts_info, +}; + +static int __init dummy_can_init(void) +{ + struct net_device *dev; + struct dummy_can *priv; + int ret; + + dev =3D alloc_candev(sizeof(*priv), 1); + if (!dev) + return -ENOMEM; + + dev->netdev_ops =3D &dummy_can_netdev_ops; + dev->ethtool_ops =3D &dummy_can_ethtool_ops; + priv =3D netdev_priv(dev); + priv->can.bittiming_const =3D &dummy_can_bittiming_const; + priv->can.bitrate_max =3D 20 * MEGA /* BPS */; + priv->can.clock.freq =3D 160 * MEGA /* Hz */; + priv->can.fd.data_bittiming_const =3D &dummy_can_fd_databittiming_const; + priv->can.fd.tdc_const =3D &dummy_can_fd_tdc_const; + priv->can.xl.data_bittiming_const =3D &dummy_can_xl_databittiming_const; + priv->can.xl.tdc_const =3D &dummy_can_xl_tdc_const; + priv->can.xl.pwm_const =3D &dummy_can_pwm_const; + priv->can.ctrlmode_supported =3D CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | CAN_CTRLMODE_TDC_AUTO | + CAN_CTRLMODE_RESTRICTED | CAN_CTRLMODE_XL | + CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TMS; + priv->dev =3D dev; + + ret =3D register_candev(priv->dev); + if (ret) { + free_candev(priv->dev); + return ret; + } + + dummy_can =3D priv; + netdev_dbg(dev, "dummy-can ready\n"); + + return 0; +} + +static void __exit dummy_can_exit(void) +{ + struct net_device *dev =3D dummy_can->dev; + + netdev_dbg(dev, "dummy-can bye bye\n"); + unregister_candev(dev); + free_candev(dev); +} + +module_init(dummy_can_init); +module_exit(dummy_can_exit); + +MODULE_DESCRIPTION("A dummy CAN driver, mainly to test the netlink interfa= ce"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vincent Mailhol "); --=20 2.51.0