]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
can: netlink: add CAN_CTRLMODE_RESTRICTED
authorVincent Mailhol <mailhol@kernel.org>
Wed, 26 Nov 2025 10:16:05 +0000 (11:16 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 26 Nov 2025 10:20:43 +0000 (11:20 +0100)
ISO 11898-1:2024 adds a new restricted operation mode. This mode is
added as a mandatory feature for nodes which support CAN XL and is
retrofitted as optional for legacy nodes (i.e. the ones which only
support Classical CAN and CAN FD).

The restricted operation mode is nearly the same as the listen only
mode: the node can not send data frames or remote frames and can not
send dominant bits if an error occurs. The only exception is that the
node shall still send the acknowledgment bit. A second niche exception
is that the node may still send a data frame containing a time
reference message if the node is a primary time provider, but because
the time provider feature is not yet implemented in the kernel, this
second exception is not relevant to us at the moment.

Add the CAN_CTRLMODE_RESTRICTED control mode flag and update the
can_dev_dropped_skb() helper function accordingly.

Finally, bail out if both CAN_CTRLMODE_LISTENONLY and
CAN_CTRLMODE_RESTRICTED are provided.

Signed-off-by: Vincent Mailhol <mailhol@kernel.org>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20251126-canxl-v8-4-e7e3eb74f889@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev/dev.c
drivers/net/can/dev/netlink.c
include/linux/can/dev.h
include/uapi/linux/can/netlink.h

index b392483e4499a75d6ddffba0773f54c3bd64a1c3..b6980d32e5b4fb9d9a42241b3a37754bdc7c69b8 100644 (file)
@@ -115,6 +115,8 @@ const char *can_get_ctrlmode_str(u32 ctrlmode)
                return "TDC-AUTO";
        case CAN_CTRLMODE_TDC_MANUAL:
                return "TDC-MANUAL";
+       case CAN_CTRLMODE_RESTRICTED:
+               return "RESTRICTED";
        default:
                return "<unknown>";
        }
index 6f83b87d54fcbdbc8ad23d9b3ae4bce90c0ddd88..87e731527dd7ae394eef9280a092f691109f1c89 100644 (file)
@@ -188,6 +188,13 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
                struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
 
                flags = cm->flags & cm->mask;
+
+               if ((flags & CAN_CTRLMODE_LISTENONLY) &&
+                   (flags & CAN_CTRLMODE_RESTRICTED)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "LISTEN-ONLY and RESTRICTED modes are mutually exclusive");
+                       return -EOPNOTSUPP;
+               }
        }
 
        err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);
index a7a39a6101d9442fe26a52d60fc7ebcfb2666b80..ab11c0e9111bd4111be4c99dd0d0ec80842811eb 100644 (file)
@@ -95,30 +95,6 @@ static inline bool can_is_canxl_dev_mtu(unsigned int mtu)
        return (mtu >= CANXL_MIN_MTU && mtu <= CANXL_MAX_MTU);
 }
 
-/* drop skb if it does not contain a valid CAN frame for sending */
-static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (priv->ctrlmode & CAN_CTRLMODE_LISTENONLY) {
-               netdev_info_once(dev,
-                                "interface in listen only mode, dropping skb\n");
-               goto invalid_skb;
-       }
-
-       if (!(priv->ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
-               netdev_info_once(dev, "CAN FD is disabled, dropping skb\n");
-               goto invalid_skb;
-       }
-
-       return can_dropped_invalid_skb(dev, skb);
-
-invalid_skb:
-       kfree_skb(skb);
-       dev->stats.tx_dropped++;
-       return true;
-}
-
 void can_setup(struct net_device *dev);
 
 struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
@@ -154,6 +130,32 @@ void can_bus_off(struct net_device *dev);
 const char *can_get_state_str(const enum can_state state);
 const char *can_get_ctrlmode_str(u32 ctrlmode);
 
+/* drop skb if it does not contain a valid CAN frame for sending */
+static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       u32 silent_mode = priv->ctrlmode & (CAN_CTRLMODE_LISTENONLY |
+                                           CAN_CTRLMODE_RESTRICTED);
+
+       if (silent_mode) {
+               netdev_info_once(dev, "interface in %s mode, dropping skb\n",
+                                can_get_ctrlmode_str(silent_mode));
+               goto invalid_skb;
+       }
+
+       if (!(priv->ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
+               netdev_info_once(dev, "CAN FD is disabled, dropping skb\n");
+               goto invalid_skb;
+       }
+
+       return can_dropped_invalid_skb(dev, skb);
+
+invalid_skb:
+       kfree_skb(skb);
+       dev->stats.tx_dropped++;
+       return true;
+}
+
 void can_state_get_by_berr_counter(const struct net_device *dev,
                                   const struct can_berr_counter *bec,
                                   enum can_state *tx_state,
index ef62f56eaaefda9f2fb39345776a483734682cd0..fafd1cce4798dea9e7597686a126a1d507ec6376 100644 (file)
@@ -103,6 +103,7 @@ struct can_ctrlmode {
 #define CAN_CTRLMODE_CC_LEN8_DLC       0x100   /* Classic CAN DLC option */
 #define CAN_CTRLMODE_TDC_AUTO          0x200   /* FD transceiver automatically calculates TDCV */
 #define CAN_CTRLMODE_TDC_MANUAL                0x400   /* FD TDCV is manually set up by user */
+#define CAN_CTRLMODE_RESTRICTED                0x800   /* Restricted operation mode */
 
 /*
  * CAN device statistics