Re: [PATCH 3/9] can: netlink: add initial CAN XL support

From: Oliver Hartkopp

Date: Sat Nov 15 2025 - 08:59:02 EST




On 15.11.25 14:35, Vincent Mailhol wrote:
On 14/11/2025 at 14:19, Oliver Hartkopp wrote:
Hi Vincent,

On 13.10.25 13:01, Vincent Mailhol wrote:
CAN XL uses bittiming parameters different from Classical CAN and CAN
FD. Thus, all the data bittiming parameters, including TDC, need to be
duplicated for CAN XL.

Add the CAN XL netlink interface for all the features which are common
with CAN FD. Any new CAN XL specific features are added later on.

Add a check that CAN XL capable nodes correctly provide
CAN_CTRLMODE_RESTRIC_OP as mandated by ISO 11898-1:2024 §6.6.19.

The first time CAN XL is activated, the MTU is set by default to
CANXL_MAX_MTU. The user may then configure a custom MTU within the
CANXL_MIN_MTU to CANXL_MIN_MTU range, in which case, the custom MTU
value will be kept as long as CAN XL remains active.

Signed-off-by: Vincent Mailhol <mailhol@xxxxxxxxxx>
---
Changelog:

RFC -> v1:

   - Correctly wipe out the CAN XL data bittiming and TDC parameters
     when switching CAN_CTRLMODE_XL off.

   - Add one level on nesting for xl parameters so that:

      - bittiming are under priv->xl.data_bittiming{,_const}¨
      - pwm are under priv->xl.pwm{,_const}

   - Many other code refactors.
---
  drivers/net/can/dev/dev.c        | 14 ++++++-
  drivers/net/can/dev/netlink.c    | 87 ++++++++++++++++++++++++++++++++--------
  include/linux/can/bittiming.h    |  6 ++-
  include/linux/can/dev.h          |  7 +++-
  include/uapi/linux/can/netlink.h |  7 ++++
  5 files changed, 100 insertions(+), 21 deletions(-)

diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c
index 3377afb6f1c4..32f11db88295 100644
--- a/drivers/net/can/dev/dev.c
+++ b/drivers/net/can/dev/dev.c
@@ -117,6 +117,12 @@ const char *can_get_ctrlmode_str(u32 ctrlmode)
          return "fd-tdc-manual";
      case CAN_CTRLMODE_RESTRICTED:
          return "restricted-operation";
+    case CAN_CTRLMODE_XL:
+        return "xl";
+    case CAN_CTRLMODE_XL_TDC_AUTO:
+        return "xl-tdc-auto";
+    case CAN_CTRLMODE_XL_TDC_MANUAL:
+        return "xl-tdc-manual";
      default:
          return "<unknown>";
      }
@@ -350,7 +356,13 @@ void can_set_default_mtu(struct net_device *dev)
  {
      struct can_priv *priv = netdev_priv(dev);
  -    if (priv->ctrlmode & CAN_CTRLMODE_FD) {
+    if (priv->ctrlmode & CAN_CTRLMODE_XL) {
+        if (can_is_canxl_dev_mtu(dev->mtu))
+            return;
+        dev->mtu = CANXL_MTU;
+        dev->min_mtu = CANXL_MIN_MTU;
+        dev->max_mtu = CANXL_MAX_MTU;
+    } else if (priv->ctrlmode & CAN_CTRLMODE_FD) {
          dev->mtu = CANFD_MTU;
          dev->min_mtu = CANFD_MTU;
          dev->max_mtu = CANFD_MTU;
diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c
index f44b5dffa176..2405f1265488 100644
--- a/drivers/net/can/dev/netlink.c
+++ b/drivers/net/can/dev/netlink.c
@@ -2,7 +2,7 @@
  /* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
   * Copyright (C) 2006 Andrey Volkov, Varma Electronics
   * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@xxxxxxxxxxxxxx>
- * Copyright (C) 2021 Vincent Mailhol <mailhol.vincent@xxxxxxxxxx>
+ * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@xxxxxxxxxx>
   */
    #include <linux/can/dev.h>
@@ -22,6 +22,9 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
      [IFLA_CAN_TERMINATION] = { .type = NLA_U16 },
      [IFLA_CAN_TDC] = { .type = NLA_NESTED },
      [IFLA_CAN_CTRLMODE_EXT] = { .type = NLA_NESTED },
+    [IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },
+    [IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct
can_bittiming_const) },
+    [IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },
  };
    static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
@@ -70,7 +73,7 @@ static int can_validate_tdc(struct nlattr *data_tdc,
          return -EOPNOTSUPP;
      }
  -    /* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
+    /* If one of the CAN_CTRLMODE_{,XL}_TDC_* flags is set then TDC
       * must be set and vice-versa
       */
      if ((tdc_auto || tdc_manual) && !data_tdc) {
@@ -82,8 +85,8 @@ static int can_validate_tdc(struct nlattr *data_tdc,
          return -EOPNOTSUPP;
      }
  -    /* If providing TDC parameters, at least TDCO is needed. TDCV
-     * is needed if and only if CAN_CTRLMODE_TDC_MANUAL is set
+    /* If providing TDC parameters, at least TDCO is needed. TDCV is
+     * needed if and only if CAN_CTRLMODE_{,XL}_TDC_MANUAL is set
       */
      if (data_tdc) {
          struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
@@ -126,10 +129,10 @@ static int can_validate_databittiming(struct nlattr
*data[],
      bool is_on;
      int err;
  -    /* Make sure that valid CAN FD configurations always consist of
+    /* Make sure that valid CAN FD/XL configurations always consist of
       * - nominal/arbitration bittiming
       * - data bittiming
-     * - control mode with CAN_CTRLMODE_FD set
+     * - control mode with CAN_CTRLMODE_{FD,XL} set
       * - TDC parameters are coherent (details in can_validate_tdc())
       */
  @@ -139,7 +142,10 @@ static int can_validate_databittiming(struct nlattr
*data[],
          is_on = flags & CAN_CTRLMODE_FD;
          type = "FD";
      } else {
-        return -EOPNOTSUPP; /* Place holder for CAN XL */
+        data_tdc = data[IFLA_CAN_XL_TDC];
+        tdc_flags = flags & CAN_CTRLMODE_XL_TDC_MASK;
+        is_on = flags & CAN_CTRLMODE_XL;
+        type = "XL";
      }
        if (is_on) {
@@ -206,6 +212,11 @@ static int can_validate(struct nlattr *tb[], struct
nlattr *data[],
      if (err)
          return err;
  +    err = can_validate_databittiming(data, extack,
+                     IFLA_CAN_XL_DATA_BITTIMING, flags);
+    if (err)
+        return err;
+
      return 0;
  }
  @@ -215,7 +226,8 @@ static int can_ctrlmode_changelink(struct net_device *dev,
  {
      struct can_priv *priv = netdev_priv(dev);
      struct can_ctrlmode *cm;
-    u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
+    const u32 xl_mandatory = CAN_CTRLMODE_RESTRICTED;
+    u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing, xl_missing;
        if (!data[IFLA_CAN_CTRLMODE])
          return 0;
@@ -229,6 +241,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
      maskedflags = cm->flags & cm->mask;
      notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
      ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;
+    xl_missing = (priv->ctrlmode_supported & xl_mandatory) ^ xl_mandatory;
        if (notsupp) {
          NL_SET_ERR_MSG_FMT(extack,
@@ -248,21 +261,36 @@ static int can_ctrlmode_changelink(struct net_device *dev,
          return -EOPNOTSUPP;
      }
  +    if ((priv->ctrlmode_supported & CAN_CTRLMODE_XL) && xl_missing) {
+        NL_SET_ERR_MSG_FMT(extack,
+                   "bad device: CAN XL capable nodes must support the %s mode",
+                   can_get_ctrlmode_str(xl_missing));
+        return -EOPNOTSUPP;
+    }
+

I'm not sure if it is our job to check for ISO 11898-1:2024 compliance of CAN
controllers when CAN_CTRLMODE_RESTRICTED is not supported.

IMO an appropriate error message (only) when the user requests this feature
would be better and that is already the standard behaviour.

For this one, the initial logic was to avoid some incorrect configurations such
as providing CAN_CTRLMODE_XL_TMS without CAN_CTRLMODE_XL_ERR_SIGNAL.

Contrarily to the other flags, CAN XL introduces some intricacies between some
of the flags and giving full freedom on what must be set or not is asking for
troubles.

That said, the CAN_CTRLMODE_RESTRICTED is different and does not depend on the
other flags. I just added it at the end after adding the check logic for the
other XL flags (this appears first in the series because of some rebase, but
trust me, I implemented this last).

Then, should we go for this or not? The benefit I see to add this check would be
less effort on the patch reviews for new devices (which I am mostly doing
recently). I can foreseen that people will wrongly use CAN_CTRLMODE_LISTENONLY
instead of CAN_CTRLMODE_RESTRICTED. This piece of code will make sure that they
will implement CAN_CTRLMODE_RESTRICTED. And because this feature is mandatory, I
thought this wasn't a too bad idea. This will also save me the effort to ask if
to people submitting patches if this feature is supported or not.

If someone comes with a non-compliant device, we can still statically set the
flag. So this isn't even a blocker for non compliant devices.

We had the same issue with non-iso CAN FD devices.

IMO we should provide the CAN_CTRLMODE_RESTRICTED feature like any other feature in the control modes (like e.g. triple-sampling). And when a device shows up that needs a special handling beyond our good working netlink API capabilities we talk about it in we will find a solution. As always.

Best regards,
Oliver
>
So, at the end, this is mostly a selfish feature to remove some patch reviewing
effort (while still pushing toward ISO compliance, which isn't a bad thing either).

I prefer to have it like this, but this isn't a strong opinion either.


Yours sincerely,
Vincent Mailhol