can: fix handling of unmodifiable configuration options
[cascardo/linux.git] / drivers / net / can / dev.c
index 141c2a4..910c12e 100644 (file)
@@ -696,11 +696,17 @@ int can_change_mtu(struct net_device *dev, int new_mtu)
        /* allow change of MTU according to the CANFD ability of the device */
        switch (new_mtu) {
        case CAN_MTU:
+               /* 'CANFD-only' controllers can not switch to CAN_MTU */
+               if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
+                       return -EINVAL;
+
                priv->ctrlmode &= ~CAN_CTRLMODE_FD;
                break;
 
        case CANFD_MTU:
-               if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD))
+               /* check for potential CANFD ability */
+               if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
+                   !(priv->ctrlmode_static & CAN_CTRLMODE_FD))
                        return -EINVAL;
 
                priv->ctrlmode |= CAN_CTRLMODE_FD;
@@ -782,6 +788,35 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
                                = { .len = sizeof(struct can_bittiming_const) },
 };
 
+static int can_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       bool is_can_fd = false;
+
+       /* Make sure that valid CAN FD configurations always consist of
+        * - nominal/arbitration bittiming
+        * - data bittiming
+        * - control mode with CAN_CTRLMODE_FD set
+        */
+
+       if (data[IFLA_CAN_CTRLMODE]) {
+               struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+
+               is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
+       }
+
+       if (is_can_fd) {
+               if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
+                       return -EOPNOTSUPP;
+       }
+
+       if (data[IFLA_CAN_DATA_BITTIMING]) {
+               if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
+                       return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
 static int can_changelink(struct net_device *dev,
                          struct nlattr *tb[], struct nlattr *data[])
 {
@@ -813,19 +848,31 @@ static int can_changelink(struct net_device *dev,
 
        if (data[IFLA_CAN_CTRLMODE]) {
                struct can_ctrlmode *cm;
+               u32 ctrlstatic;
+               u32 maskedflags;
 
                /* Do not allow changing controller mode while running */
                if (dev->flags & IFF_UP)
                        return -EBUSY;
                cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+               ctrlstatic = priv->ctrlmode_static;
+               maskedflags = cm->flags & cm->mask;
+
+               /* check whether provided bits are allowed to be passed */
+               if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
+                       return -EOPNOTSUPP;
+
+               /* do not check for static fd-non-iso if 'fd' is disabled */
+               if (!(maskedflags & CAN_CTRLMODE_FD))
+                       ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
 
-               /* check whether changed bits are allowed to be modified */
-               if (cm->mask & ~priv->ctrlmode_supported)
+               /* make sure static options are provided by configuration */
+               if ((maskedflags & ctrlstatic) != ctrlstatic)
                        return -EOPNOTSUPP;
 
                /* clear bits to be modified and copy the flag values */
                priv->ctrlmode &= ~cm->mask;
-               priv->ctrlmode |= (cm->flags & cm->mask);
+               priv->ctrlmode |= maskedflags;
 
                /* CAN_CTRLMODE_FD can only be set when driver supports FD */
                if (priv->ctrlmode & CAN_CTRLMODE_FD)
@@ -966,6 +1013,7 @@ static struct rtnl_link_ops can_link_ops __read_mostly = {
        .maxtype        = IFLA_CAN_MAX,
        .policy         = can_policy,
        .setup          = can_setup,
+       .validate       = can_validate,
        .newlink        = can_newlink,
        .changelink     = can_changelink,
        .get_size       = can_get_size,