root/drivers/net/can/dev/netlink.c
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
 * Copyright (C) 2006 Andrey Volkov, Varma Electronics
 * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
 * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
 */

#include <linux/can/dev.h>
#include <net/rtnetlink.h>

static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
        [IFLA_CAN_STATE] = { .type = NLA_U32 },
        [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) },
        [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 },
        [IFLA_CAN_RESTART] = { .type = NLA_U32 },
        [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) },
        [IFLA_CAN_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },
        [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) },
        [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },
        [IFLA_CAN_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },
        [IFLA_CAN_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },
        [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 },
        [IFLA_CAN_XL_PWM] = { .type = NLA_NESTED },
};

static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
        [IFLA_CAN_TDC_TDCV_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCV_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCO_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCO_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCF_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCF_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCV] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCO] = { .type = NLA_U32 },
        [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },
};

static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = {
        [IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWML] = { .type = NLA_U32 },
        [IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 },
};

static int can_validate_bittiming(struct nlattr *data[],
                                  struct netlink_ext_ack *extack,
                                  int ifla_can_bittiming)
{
        struct can_bittiming *bt;

        if (!data[ifla_can_bittiming])
                return 0;

        static_assert(__alignof__(*bt) <= NLA_ALIGNTO);
        bt = nla_data(data[ifla_can_bittiming]);

        /* sample point is in one-tenth of a percent */
        if (bt->sample_point >= 1000) {
                NL_SET_ERR_MSG(extack, "sample point must be between 0 and 100%");
                return -EINVAL;
        }

        return 0;
}

static int can_validate_tdc(struct nlattr *data_tdc,
                            struct netlink_ext_ack *extack, u32 tdc_flags)
{
        bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK;
        bool tdc_auto = tdc_flags & CAN_CTRLMODE_TDC_AUTO_MASK;
        int err;

        if (tdc_auto && tdc_manual) {
                NL_SET_ERR_MSG(extack,
                               "TDC manual and auto modes are mutually exclusive");
                return -EOPNOTSUPP;
        }

        /* 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) {
                NL_SET_ERR_MSG(extack, "TDC parameters are missing");
                return -EOPNOTSUPP;
        }
        if (!(tdc_auto || tdc_manual) && data_tdc) {
                NL_SET_ERR_MSG(extack, "TDC mode (auto or manual) is missing");
                return -EOPNOTSUPP;
        }

        /* 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];

                err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX,
                                       data_tdc, can_tdc_policy, extack);
                if (err)
                        return err;

                if (tb_tdc[IFLA_CAN_TDC_TDCV]) {
                        if (tdc_auto) {
                                NL_SET_ERR_MSG(extack,
                                               "TDCV is incompatible with TDC auto mode");
                                return -EOPNOTSUPP;
                        }
                } else {
                        if (tdc_manual) {
                                NL_SET_ERR_MSG(extack,
                                               "TDC manual mode requires TDCV");
                                return -EOPNOTSUPP;
                        }
                }

                if (!tb_tdc[IFLA_CAN_TDC_TDCO]) {
                        NL_SET_ERR_MSG(extack, "TDCO is missing");
                        return -EOPNOTSUPP;
                }
        }

        return 0;
}

static int can_validate_pwm(struct nlattr *data[],
                            struct netlink_ext_ack *extack, u32 flags)
{
        struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
        int err;

        if (!data[IFLA_CAN_XL_PWM])
                return 0;

        if (!(flags & CAN_CTRLMODE_XL_TMS)) {
                NL_SET_ERR_MSG(extack, "PWM requires TMS");
                return -EOPNOTSUPP;
        }

        err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM],
                               can_pwm_policy, extack);
        if (err)
                return err;

        if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) {
                NL_SET_ERR_MSG(extack,
                               "Provide either both PWMS and PWML, or none for automatic calculation");
                return -EOPNOTSUPP;
        }

        if (tb_pwm[IFLA_CAN_PWM_PWMO] &&
            (!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) {
                NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML");
                return -EOPNOTSUPP;
        }

        return 0;
}

static int can_validate_databittiming(struct nlattr *data[],
                                      struct netlink_ext_ack *extack,
                                      int ifla_can_data_bittiming, u32 flags)
{
        struct nlattr *data_tdc;
        const char *type;
        u32 tdc_flags;
        bool is_on;
        int err;

        /* Make sure that valid CAN FD/XL configurations always consist of
         * - nominal/arbitration bittiming
         * - data bittiming
         * - control mode with CAN_CTRLMODE_{FD,XL} set
         * - TDC parameters are coherent (details in can_validate_tdc())
         */

        if (ifla_can_data_bittiming == IFLA_CAN_DATA_BITTIMING) {
                data_tdc = data[IFLA_CAN_TDC];
                tdc_flags = flags & CAN_CTRLMODE_FD_TDC_MASK;
                is_on = flags & CAN_CTRLMODE_FD;
                type = "FD";
        } else {
                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) {
                if (!data[IFLA_CAN_BITTIMING] || !data[ifla_can_data_bittiming]) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "Provide both nominal and %s data bittiming",
                                           type);
                        return -EOPNOTSUPP;
                }
        } else {
                if (data[ifla_can_data_bittiming]) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "%s data bittiming requires CAN %s",
                                           type, type);
                        return -EOPNOTSUPP;
                }
                if (data_tdc) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "%s TDC requires CAN %s",
                                           type, type);
                        return -EOPNOTSUPP;
                }
        }

        err = can_validate_bittiming(data, extack, ifla_can_data_bittiming);
        if (err)
                return err;

        err = can_validate_tdc(data_tdc, extack, tdc_flags);
        if (err)
                return err;

        return 0;
}

static int can_validate_xl_flags(struct netlink_ext_ack *extack,
                                 u32 masked_flags, u32 mask)
{
        if (masked_flags & CAN_CTRLMODE_XL) {
                if (masked_flags & CAN_CTRLMODE_XL_TMS) {
                        const u32 tms_conflicts_mask = CAN_CTRLMODE_FD |
                                CAN_CTRLMODE_XL_TDC_MASK;
                        u32 tms_conflicts = masked_flags & tms_conflicts_mask;

                        if (tms_conflicts) {
                                NL_SET_ERR_MSG_FMT(extack,
                                                   "TMS and %s are mutually exclusive",
                                                   can_get_ctrlmode_str(tms_conflicts));
                                return -EOPNOTSUPP;
                        }
                }
        } else {
                if (mask & CAN_CTRLMODE_XL_TMS) {
                        NL_SET_ERR_MSG(extack, "TMS requires CAN XL");
                        return -EOPNOTSUPP;
                }
        }

        return 0;
}

static int can_validate(struct nlattr *tb[], struct nlattr *data[],
                        struct netlink_ext_ack *extack)
{
        u32 flags = 0;
        int err;

        if (!data)
                return 0;

        if (data[IFLA_CAN_CTRLMODE]) {
                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_xl_flags(extack, flags, cm->mask);
                if (err)
                        return err;
        }

        err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);
        if (err)
                return err;

        err = can_validate_databittiming(data, extack,
                                         IFLA_CAN_DATA_BITTIMING, flags);
        if (err)
                return err;

        err = can_validate_databittiming(data, extack,
                                         IFLA_CAN_XL_DATA_BITTIMING, flags);
        if (err)
                return err;

        err = can_validate_pwm(data, extack, flags);
        if (err)
                return err;

        return 0;
}

static int can_ctrlmode_changelink(struct net_device *dev,
                                   struct nlattr *data[],
                                   struct netlink_ext_ack *extack)
{
        struct can_priv *priv = netdev_priv(dev);
        struct can_ctrlmode *cm;
        u32 ctrlstatic, maskedflags, deactivated, notsupp, ctrlstatic_missing;

        if (!data[IFLA_CAN_CTRLMODE])
                return 0;

        /* Do not allow changing controller mode while running */
        if (dev->flags & IFF_UP)
                return -EBUSY;

        cm = nla_data(data[IFLA_CAN_CTRLMODE]);
        ctrlstatic = can_get_static_ctrlmode(priv);
        maskedflags = cm->flags & cm->mask;
        deactivated = ~cm->flags & cm->mask;
        notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
        ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;

        if (notsupp) {
                NL_SET_ERR_MSG_FMT(extack,
                                   "requested control mode %s not supported",
                                   can_get_ctrlmode_str(notsupp));
                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;

        if (ctrlstatic_missing) {
                NL_SET_ERR_MSG_FMT(extack,
                                   "missing required %s static control mode",
                                   can_get_ctrlmode_str(ctrlstatic_missing));
                return -EOPNOTSUPP;
        }

        /* If FD was active and is not turned off, check for XL conflicts */
        if (priv->ctrlmode & CAN_CTRLMODE_FD & ~deactivated) {
                if (maskedflags & CAN_CTRLMODE_XL_TMS) {
                        NL_SET_ERR_MSG(extack,
                                       "TMS can not be activated while CAN FD is on");
                        return -EOPNOTSUPP;
                }
        }

        /* If a top dependency flag is provided, reset all its dependencies */
        if (cm->mask & CAN_CTRLMODE_FD)
                priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
        if (cm->mask & CAN_CTRLMODE_XL)
                priv->ctrlmode &= ~(CAN_CTRLMODE_XL_TDC_MASK |
                                    CAN_CTRLMODE_XL_TMS);

        /* clear bits to be modified and copy the flag values */
        priv->ctrlmode &= ~cm->mask;
        priv->ctrlmode |= maskedflags;

        /* Wipe potential leftovers from previous CAN FD/XL config */
        if (!(priv->ctrlmode & CAN_CTRLMODE_FD)) {
                memset(&priv->fd.data_bittiming, 0,
                       sizeof(priv->fd.data_bittiming));
                priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
                memset(&priv->fd.tdc, 0, sizeof(priv->fd.tdc));
        }
        if (!(priv->ctrlmode & CAN_CTRLMODE_XL)) {
                memset(&priv->xl.data_bittiming, 0,
                       sizeof(priv->fd.data_bittiming));
                priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;
                memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));
                memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm));
        }

        can_set_default_mtu(dev);
        can_set_cap_info(dev);

        return 0;
}

static int can_tdc_changelink(struct data_bittiming_params *dbt_params,
                              const struct nlattr *nla,
                              struct netlink_ext_ack *extack)
{
        struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
        struct can_tdc tdc = { 0 };
        const struct can_tdc_const *tdc_const = dbt_params->tdc_const;
        int err;

        if (!tdc_const) {
                NL_SET_ERR_MSG(extack, "The device does not support TDC");
                return -EOPNOTSUPP;
        }

        err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, nla,
                               can_tdc_policy, extack);
        if (err)
                return err;

        if (tb_tdc[IFLA_CAN_TDC_TDCV]) {
                u32 tdcv = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCV]);

                if (tdcv < tdc_const->tdcv_min || tdcv > tdc_const->tdcv_max)
                        return -EINVAL;

                tdc.tdcv = tdcv;
        }

        if (tb_tdc[IFLA_CAN_TDC_TDCO]) {
                u32 tdco = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCO]);

                if (tdco < tdc_const->tdco_min || tdco > tdc_const->tdco_max)
                        return -EINVAL;

                tdc.tdco = tdco;
        }

        if (tb_tdc[IFLA_CAN_TDC_TDCF]) {
                u32 tdcf = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCF]);

                if (tdcf < tdc_const->tdcf_min || tdcf > tdc_const->tdcf_max)
                        return -EINVAL;

                tdc.tdcf = tdcf;
        }

        dbt_params->tdc = tdc;

        return 0;
}

static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
                              bool fd, struct netlink_ext_ack *extack)
{
        struct nlattr *data_bittiming, *data_tdc;
        struct can_priv *priv = netdev_priv(dev);
        struct data_bittiming_params *dbt_params;
        struct can_bittiming dbt;
        bool need_tdc_calc = false;
        u32 tdc_mask;
        int err;

        if (fd) {
                data_bittiming = data[IFLA_CAN_DATA_BITTIMING];
                data_tdc = data[IFLA_CAN_TDC];
                dbt_params = &priv->fd;
                tdc_mask = CAN_CTRLMODE_FD_TDC_MASK;
        } else {
                data_bittiming = data[IFLA_CAN_XL_DATA_BITTIMING];
                data_tdc = data[IFLA_CAN_XL_TDC];
                dbt_params = &priv->xl;
                tdc_mask = CAN_CTRLMODE_XL_TDC_MASK;
        }

        if (!data_bittiming)
                return 0;

        /* Do not allow changing bittiming while running */
        if (dev->flags & IFF_UP)
                return -EBUSY;

        /* Calculate bittiming parameters based on data_bittiming_const
         * if set, otherwise pass bitrate directly via do_set_bitrate().
         * Bail out if neither is given.
         */
        if (!dbt_params->data_bittiming_const && !dbt_params->do_set_data_bittiming &&
            !dbt_params->data_bitrate_const)
                return -EOPNOTSUPP;

        memcpy(&dbt, nla_data(data_bittiming), sizeof(dbt));
        err = can_get_bittiming(dev, &dbt, dbt_params->data_bittiming_const,
                                dbt_params->data_bitrate_const,
                                dbt_params->data_bitrate_const_cnt, extack);
        if (err)
                return err;

        if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
                NL_SET_ERR_MSG_FMT(extack,
                                   "CAN data bitrate %u bps surpasses transceiver capabilities of %u bps",
                                   dbt.bitrate, priv->bitrate_max);
                return -EINVAL;
        }

        memset(&dbt_params->tdc, 0, sizeof(dbt_params->tdc));
        if (data[IFLA_CAN_CTRLMODE]) {
                struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);

                if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
                        need_tdc_calc = !(cm->mask & tdc_mask);
        }
        if (data_tdc) {
                /* TDC parameters are provided: use them */
                err = can_tdc_changelink(dbt_params, data_tdc, extack);
                if (err) {
                        priv->ctrlmode &= ~tdc_mask;
                        return err;
                }
        } else if (need_tdc_calc) {
                /* Neither of TDC parameters nor TDC flags are provided:
                 * do calculation
                 */
                can_calc_tdco(&dbt_params->tdc, dbt_params->tdc_const, &dbt,
                              tdc_mask, &priv->ctrlmode, priv->ctrlmode_supported);
        } /* else: both CAN_CTRLMODE_{,XL}_TDC_{AUTO,MANUAL} are explicitly
           * turned off. TDC is disabled: do nothing
           */

        memcpy(&dbt_params->data_bittiming, &dbt, sizeof(dbt));

        if (dbt_params->do_set_data_bittiming) {
                /* Finally, set the bit-timing registers */
                err = dbt_params->do_set_data_bittiming(dev);
                if (err)
                        return err;
        }

        return 0;
}

static int can_pwm_changelink(struct net_device *dev,
                              const struct nlattr *pwm_nla,
                              struct netlink_ext_ack *extack)
{
        struct can_priv *priv = netdev_priv(dev);
        const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
        struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
        struct can_pwm pwm = { 0 };
        int err;

        if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
                return 0;

        if (!pwm_const) {
                NL_SET_ERR_MSG(extack, "The device does not support PWM");
                return -EOPNOTSUPP;
        }

        if (!pwm_nla)
                return can_calc_pwm(dev, extack);

        err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla,
                               can_pwm_policy, extack);
        if (err)
                return err;

        if (tb_pwm[IFLA_CAN_PWM_PWMS]) {
                pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]);
                if (pwm.pwms < pwm_const->pwms_min ||
                    pwm.pwms > pwm_const->pwms_max) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "PWMS: %u tqmin is out of range: %u...%u",
                                           pwm.pwms, pwm_const->pwms_min,
                                           pwm_const->pwms_max);
                        return -EINVAL;
                }
        }

        if (tb_pwm[IFLA_CAN_PWM_PWML]) {
                pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]);
                if (pwm.pwml < pwm_const->pwml_min ||
                    pwm.pwml > pwm_const->pwml_max) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "PWML: %u tqmin is out of range: %u...%u",
                                           pwm.pwml, pwm_const->pwml_min,
                                           pwm_const->pwml_max);
                        return -EINVAL;
                }
        }

        if (tb_pwm[IFLA_CAN_PWM_PWMO]) {
                pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]);
                if (pwm.pwmo < pwm_const->pwmo_min ||
                    pwm.pwmo > pwm_const->pwmo_max) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "PWMO: %u tqmin is out of range: %u...%u",
                                           pwm.pwmo, pwm_const->pwmo_min,
                                           pwm_const->pwmo_max);
                        return -EINVAL;
                }
        }

        err = can_validate_pwm_bittiming(dev, &pwm, extack);
        if (err)
                return err;

        priv->xl.pwm = pwm;
        return 0;
}

static int can_changelink(struct net_device *dev, struct nlattr *tb[],
                          struct nlattr *data[],
                          struct netlink_ext_ack *extack)
{
        struct can_priv *priv = netdev_priv(dev);
        int err;

        /* We need synchronization with dev->stop() */
        ASSERT_RTNL();

        err = can_ctrlmode_changelink(dev, data, extack);
        if (err)
                return err;

        if (data[IFLA_CAN_BITTIMING]) {
                struct can_bittiming bt;

                /* Do not allow changing bittiming while running */
                if (dev->flags & IFF_UP)
                        return -EBUSY;

                /* Calculate bittiming parameters based on
                 * bittiming_const if set, otherwise pass bitrate
                 * directly via do_set_bitrate(). Bail out if neither
                 * is given.
                 */
                if (!priv->bittiming_const && !priv->do_set_bittiming &&
                    !priv->bitrate_const)
                        return -EOPNOTSUPP;

                memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
                err = can_get_bittiming(dev, &bt,
                                        priv->bittiming_const,
                                        priv->bitrate_const,
                                        priv->bitrate_const_cnt,
                                        extack);
                if (err)
                        return err;

                if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) {
                        NL_SET_ERR_MSG_FMT(extack,
                                           "arbitration bitrate %u bps surpasses transceiver capabilities of %u bps",
                                           bt.bitrate, priv->bitrate_max);
                        return -EINVAL;
                }

                memcpy(&priv->bittiming, &bt, sizeof(bt));

                if (priv->do_set_bittiming) {
                        /* Finally, set the bit-timing registers */
                        err = priv->do_set_bittiming(dev);
                        if (err)
                                return err;
                }
        }

        if (data[IFLA_CAN_RESTART_MS]) {
                unsigned int restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);

                if (restart_ms != 0 && !priv->do_set_mode) {
                        NL_SET_ERR_MSG(extack,
                                       "Device doesn't support restart from Bus Off");
                        return -EOPNOTSUPP;
                }

                /* Do not allow changing restart delay while running */
                if (dev->flags & IFF_UP)
                        return -EBUSY;
                priv->restart_ms = restart_ms;
        }

        if (data[IFLA_CAN_RESTART]) {
                if (!priv->do_set_mode) {
                        NL_SET_ERR_MSG(extack,
                                       "Device doesn't support restart from Bus Off");
                        return -EOPNOTSUPP;
                }

                /* Do not allow a restart while not running */
                if (!(dev->flags & IFF_UP))
                        return -EINVAL;
                err = can_restart_now(dev);
                if (err)
                        return err;
        }

        /* CAN FD */
        err = can_dbt_changelink(dev, data, true, extack);
        if (err)
                return err;

        /* CAN XL */
        err = can_dbt_changelink(dev, data, false, extack);
        if (err)
                return err;
        err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], extack);
        if (err)
                return err;

        if (data[IFLA_CAN_TERMINATION]) {
                const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
                const unsigned int num_term = priv->termination_const_cnt;
                unsigned int i;

                if (!priv->do_set_termination) {
                        NL_SET_ERR_MSG(extack,
                                       "Termination is not configurable on this device");
                        return -EOPNOTSUPP;
                }

                /* check whether given value is supported by the interface */
                for (i = 0; i < num_term; i++) {
                        if (termval == priv->termination_const[i])
                                break;
                }
                if (i >= num_term)
                        return -EINVAL;

                /* Finally, set the termination value */
                err = priv->do_set_termination(dev, termval);
                if (err)
                        return err;

                priv->termination = termval;
        }

        return 0;
}

static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params,
                               u32 tdc_flags)
{
        bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK;
        size_t size;

        if (!dbt_params->tdc_const)
                return 0;

        size = nla_total_size(0);                       /* nest IFLA_CAN_TDC */
        if (tdc_manual) {
                size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCV_MIN */
                size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCV_MAX */
        }
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_TDCO_MIN */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_TDCO_MAX */
        if (dbt_params->tdc_const->tdcf_max) {
                size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCF_MIN */
                size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCF_MAX */
        }

        if (tdc_flags) {
                if (tdc_manual || dbt_params->do_get_auto_tdcv)
                        size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCV */
                size += nla_total_size(sizeof(u32));            /* IFLA_CAN_TDCO */
                if (dbt_params->tdc_const->tdcf_max)
                        size += nla_total_size(sizeof(u32));    /* IFLA_CAN_TDCF */
        }

        return size;
}

static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_params,
                                          u32 tdc_flags)
{
        size_t size = 0;

        if (dbt_params->data_bittiming.bitrate)         /* IFLA_CAN_{,XL}_DATA_BITTIMING */
                size += nla_total_size(sizeof(dbt_params->data_bittiming));
        if (dbt_params->data_bittiming_const)           /* IFLA_CAN_{,XL}_DATA_BITTIMING_CONST */
                size += nla_total_size(sizeof(*dbt_params->data_bittiming_const));
        if (dbt_params->data_bitrate_const)             /* IFLA_CAN_{,XL}_DATA_BITRATE_CONST */
                size += nla_total_size(sizeof(*dbt_params->data_bitrate_const) *
                                       dbt_params->data_bitrate_const_cnt);
        size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */

        return size;
}

static size_t can_ctrlmode_ext_get_size(void)
{
        return nla_total_size(0) +              /* nest IFLA_CAN_CTRLMODE_EXT */
                nla_total_size(sizeof(u32));    /* IFLA_CAN_CTRLMODE_SUPPORTED */
}

static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const,
                               bool pwm_on)
{
        size_t size;

        if (!pwm_const || !pwm_on)
                return 0;

        size = nla_total_size(0);                       /* nest IFLA_CAN_PWM */

        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMS_MIN */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMS_MAX */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWML_MIN */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWML_MAX */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMO_MIN */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMO_MAX */

        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMS */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWML */
        size += nla_total_size(sizeof(u32));            /* IFLA_CAN_PWM_PWMO */

        return size;
}

static size_t can_get_size(const struct net_device *dev)
{
        struct can_priv *priv = netdev_priv(dev);
        size_t size = 0;

        if (priv->bittiming.bitrate)                            /* IFLA_CAN_BITTIMING */
                size += nla_total_size(sizeof(struct can_bittiming));
        if (priv->bittiming_const)                              /* IFLA_CAN_BITTIMING_CONST */
                size += nla_total_size(sizeof(struct can_bittiming_const));
        size += nla_total_size(sizeof(struct can_clock));       /* IFLA_CAN_CLOCK */
        size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_STATE */
        size += nla_total_size(sizeof(struct can_ctrlmode));    /* IFLA_CAN_CTRLMODE */
        size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_RESTART_MS */
        if (priv->do_get_berr_counter)                          /* IFLA_CAN_BERR_COUNTER */
                size += nla_total_size(sizeof(struct can_berr_counter));
        if (priv->termination_const) {
                size += nla_total_size(sizeof(priv->termination));              /* IFLA_CAN_TERMINATION */
                size += nla_total_size(sizeof(*priv->termination_const) *       /* IFLA_CAN_TERMINATION_CONST */
                                       priv->termination_const_cnt);
        }
        if (priv->bitrate_const)                                /* IFLA_CAN_BITRATE_CONST */
                size += nla_total_size(sizeof(*priv->bitrate_const) *
                                       priv->bitrate_const_cnt);
        size += sizeof(priv->bitrate_max);                      /* IFLA_CAN_BITRATE_MAX */
        size += can_ctrlmode_ext_get_size();                    /* IFLA_CAN_CTRLMODE_EXT */

        size += can_data_bittiming_get_size(&priv->fd,
                                            priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);

        size += can_data_bittiming_get_size(&priv->xl,
                                            priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
        size += can_pwm_get_size(priv->xl.pwm_const,            /* IFLA_CAN_XL_PWM */
                                 priv->ctrlmode & CAN_CTRLMODE_XL_TMS);

        return size;
}

static int can_bittiming_fill_info(struct sk_buff *skb, int ifla_can_bittiming,
                                   struct can_bittiming *bittiming)
{
        return bittiming->bitrate != CAN_BITRATE_UNSET &&
                bittiming->bitrate != CAN_BITRATE_UNKNOWN &&
                nla_put(skb, ifla_can_bittiming, sizeof(*bittiming), bittiming);
}

static int can_bittiming_const_fill_info(struct sk_buff *skb,
                                         int ifla_can_bittiming_const,
                                         const struct can_bittiming_const *bittiming_const)
{
        return bittiming_const &&
                nla_put(skb, ifla_can_bittiming_const,
                        sizeof(*bittiming_const), bittiming_const);
}

static int can_bitrate_const_fill_info(struct sk_buff *skb,
                                       int ifla_can_bitrate_const,
                                       const u32 *bitrate_const, unsigned int cnt)
{
        return bitrate_const &&
                nla_put(skb, ifla_can_bitrate_const,
                        sizeof(*bitrate_const) * cnt, bitrate_const);
}

static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
                             int ifla_can_tdc)
{
        struct can_priv *priv = netdev_priv(dev);
        struct data_bittiming_params *dbt_params;
        const struct can_tdc_const *tdc_const;
        struct can_tdc *tdc;
        struct nlattr *nest;
        bool tdc_is_enabled, tdc_manual;

        if (ifla_can_tdc == IFLA_CAN_TDC) {
                dbt_params = &priv->fd;
                tdc_is_enabled = can_fd_tdc_is_enabled(priv);
                tdc_manual = priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL;
        } else {
                dbt_params = &priv->xl;
                tdc_is_enabled = can_xl_tdc_is_enabled(priv);
                tdc_manual = priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MANUAL;
        }
        tdc_const = dbt_params->tdc_const;
        tdc = &dbt_params->tdc;

        if (!tdc_const)
                return 0;

        nest = nla_nest_start(skb, ifla_can_tdc);
        if (!nest)
                return -EMSGSIZE;

        if (tdc_manual &&
            (nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MIN, tdc_const->tdcv_min) ||
             nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MAX, tdc_const->tdcv_max)))
                goto err_cancel;
        if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MIN, tdc_const->tdco_min) ||
            nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MAX, tdc_const->tdco_max))
                goto err_cancel;
        if (tdc_const->tdcf_max &&
            (nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MIN, tdc_const->tdcf_min) ||
             nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MAX, tdc_const->tdcf_max)))
                goto err_cancel;

        if (tdc_is_enabled) {
                u32 tdcv;
                int err = -EINVAL;

                if (tdc_manual) {
                        tdcv = tdc->tdcv;
                        err = 0;
                } else if (dbt_params->do_get_auto_tdcv) {
                        err = dbt_params->do_get_auto_tdcv(dev, &tdcv);
                }
                if (!err && nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdcv))
                        goto err_cancel;
                if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO, tdc->tdco))
                        goto err_cancel;
                if (tdc_const->tdcf_max &&
                    nla_put_u32(skb, IFLA_CAN_TDC_TDCF, tdc->tdcf))
                        goto err_cancel;
        }

        nla_nest_end(skb, nest);
        return 0;

err_cancel:
        nla_nest_cancel(skb, nest);
        return -EMSGSIZE;
}

static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv)
{
        const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
        const struct can_pwm *pwm = &priv->xl.pwm;
        struct nlattr *nest;

        if (!pwm_const)
                return 0;

        nest = nla_nest_start(skb, IFLA_CAN_XL_PWM);
        if (!nest)
                return -EMSGSIZE;

        if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) ||
            nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) ||
            nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) ||
            nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) ||
            nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) ||
            nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max))
                goto err_cancel;

        if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
                if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) ||
                    nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) ||
                    nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo))
                        goto err_cancel;
        }

        nla_nest_end(skb, nest);
        return 0;

err_cancel:
        nla_nest_cancel(skb, nest);
        return -EMSGSIZE;
}

static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,
                                      const struct can_priv *priv)
{
        struct nlattr *nest;

        nest = nla_nest_start(skb, IFLA_CAN_CTRLMODE_EXT);
        if (!nest)
                return -EMSGSIZE;

        if (nla_put_u32(skb, IFLA_CAN_CTRLMODE_SUPPORTED,
                        priv->ctrlmode_supported)) {
                nla_nest_cancel(skb, nest);
                return -EMSGSIZE;
        }

        nla_nest_end(skb, nest);
        return 0;
}

static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
        struct can_priv *priv = netdev_priv(dev);
        struct can_ctrlmode cm = {.flags = priv->ctrlmode};
        struct can_berr_counter bec = { };
        enum can_state state = priv->state;

        if (priv->do_get_state)
                priv->do_get_state(dev, &state);

        if (can_bittiming_fill_info(skb, IFLA_CAN_BITTIMING,
                                    &priv->bittiming) ||

            can_bittiming_const_fill_info(skb, IFLA_CAN_BITTIMING_CONST,
                                          priv->bittiming_const) ||

            nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
            nla_put_u32(skb, IFLA_CAN_STATE, state) ||
            nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
            nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||

            (priv->do_get_berr_counter &&
             !priv->do_get_berr_counter(dev, &bec) &&
             nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||

            can_bittiming_fill_info(skb, IFLA_CAN_DATA_BITTIMING,
                                    &priv->fd.data_bittiming) ||

            can_bittiming_const_fill_info(skb, IFLA_CAN_DATA_BITTIMING_CONST,
                                          priv->fd.data_bittiming_const) ||

            (priv->termination_const &&
             (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
              nla_put(skb, IFLA_CAN_TERMINATION_CONST,
                      sizeof(*priv->termination_const) *
                      priv->termination_const_cnt,
                      priv->termination_const))) ||

            can_bitrate_const_fill_info(skb, IFLA_CAN_BITRATE_CONST,
                                        priv->bitrate_const,
                                        priv->bitrate_const_cnt) ||

            can_bitrate_const_fill_info(skb, IFLA_CAN_DATA_BITRATE_CONST,
                                        priv->fd.data_bitrate_const,
                                        priv->fd.data_bitrate_const_cnt) ||

            (nla_put(skb, IFLA_CAN_BITRATE_MAX,
                     sizeof(priv->bitrate_max),
                     &priv->bitrate_max)) ||

            can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) ||

            can_ctrlmode_ext_fill_info(skb, priv) ||

            can_bittiming_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING,
                                    &priv->xl.data_bittiming) ||

            can_bittiming_const_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING_CONST,
                                          priv->xl.data_bittiming_const) ||

            can_bitrate_const_fill_info(skb, IFLA_CAN_XL_DATA_BITRATE_CONST,
                                        priv->xl.data_bitrate_const,
                                        priv->xl.data_bitrate_const_cnt) ||

            can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) ||

            can_pwm_fill_info(skb, priv)
            )
                return -EMSGSIZE;

        return 0;
}

static size_t can_get_xstats_size(const struct net_device *dev)
{
        return sizeof(struct can_device_stats);
}

static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev)
{
        struct can_priv *priv = netdev_priv(dev);

        if (nla_put(skb, IFLA_INFO_XSTATS,
                    sizeof(priv->can_stats), &priv->can_stats))
                goto nla_put_failure;
        return 0;

nla_put_failure:
        return -EMSGSIZE;
}

static int can_newlink(struct net_device *dev,
                       struct rtnl_newlink_params *params,
                       struct netlink_ext_ack *extack)
{
        return -EOPNOTSUPP;
}

static void can_dellink(struct net_device *dev, struct list_head *head)
{
}

struct rtnl_link_ops can_link_ops __read_mostly = {
        .kind           = "can",
        .netns_refund   = true,
        .maxtype        = IFLA_CAN_MAX,
        .policy         = can_policy,
        .setup          = can_setup,
        .validate       = can_validate,
        .newlink        = can_newlink,
        .changelink     = can_changelink,
        .dellink        = can_dellink,
        .get_size       = can_get_size,
        .fill_info      = can_fill_info,
        .get_xstats_size = can_get_xstats_size,
        .fill_xstats    = can_fill_xstats,
};

int can_netlink_register(void)
{
        return rtnl_link_register(&can_link_ops);
}

void can_netlink_unregister(void)
{
        rtnl_link_unregister(&can_link_ops);
}