diff options
Diffstat (limited to 'drivers/net/can/dev')
| -rw-r--r-- | drivers/net/can/dev/bittiming.c | 63 | ||||
| -rw-r--r-- | drivers/net/can/dev/calc_bittiming.c | 114 | ||||
| -rw-r--r-- | drivers/net/can/dev/dev.c | 125 | ||||
| -rw-r--r-- | drivers/net/can/dev/netlink.c | 319 |
4 files changed, 502 insertions, 119 deletions
diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c index 0b93900b1dfa..8f82418230ce 100644 --- a/drivers/net/can/dev/bittiming.c +++ b/drivers/net/can/dev/bittiming.c @@ -2,6 +2,7 @@ /* 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) 2025 Vincent Mailhol <mailhol@kernel.org> */ #include <linux/can/dev.h> @@ -151,3 +152,65 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt, return -EINVAL; } + +int can_validate_pwm_bittiming(const struct net_device *dev, + const struct can_pwm *pwm, + struct netlink_ext_ack *extack) +{ + const struct can_priv *priv = netdev_priv(dev); + u32 xl_bit_time_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming); + u32 nom_bit_time_tqmin = can_bit_time_tqmin(&priv->bittiming); + u32 pwms_ns = can_tqmin_to_ns(pwm->pwms, priv->clock.freq); + u32 pwml_ns = can_tqmin_to_ns(pwm->pwml, priv->clock.freq); + + if (pwms_ns + pwml_ns > CAN_PWM_NS_MAX) { + NL_SET_ERR_MSG_FMT(extack, + "The PWM symbol duration: %u ns may not exceed %u ns", + pwms_ns + pwml_ns, CAN_PWM_NS_MAX); + return -EINVAL; + } + + if (pwms_ns < CAN_PWM_DECODE_NS) { + NL_SET_ERR_MSG_FMT(extack, + "PWMS: %u ns shall be at least %u ns", + pwms_ns, CAN_PWM_DECODE_NS); + return -EINVAL; + } + + if (pwm->pwms >= pwm->pwml) { + NL_SET_ERR_MSG_FMT(extack, + "PWMS: %u tqmin shall be smaller than PWML: %u tqmin", + pwm->pwms, pwm->pwml); + return -EINVAL; + } + + if (pwml_ns - pwms_ns < 2 * CAN_PWM_DECODE_NS) { + NL_SET_ERR_MSG_FMT(extack, + "At least %u ns shall separate PWMS: %u ns from PMWL: %u ns", + 2 * CAN_PWM_DECODE_NS, pwms_ns, pwml_ns); + return -EINVAL; + } + + if (xl_bit_time_tqmin % (pwm->pwms + pwm->pwml) != 0) { + NL_SET_ERR_MSG_FMT(extack, + "PWM duration: %u tqmin does not divide XL's bit time: %u tqmin", + pwm->pwms + pwm->pwml, xl_bit_time_tqmin); + return -EINVAL; + } + + if (pwm->pwmo >= pwm->pwms + pwm->pwml) { + NL_SET_ERR_MSG_FMT(extack, + "PWMO: %u tqmin can not be greater than PWMS + PWML: %u tqmin", + pwm->pwmo, pwm->pwms + pwm->pwml); + return -EINVAL; + } + + if (nom_bit_time_tqmin % (pwm->pwms + pwm->pwml) != pwm->pwmo) { + NL_SET_ERR_MSG_FMT(extack, + "Can not assemble nominal bit time: %u tqmin out of PWMS + PMWL and PWMO", + nom_bit_time_tqmin); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/can/dev/calc_bittiming.c b/drivers/net/can/dev/calc_bittiming.c index 394d6974f481..cc4022241553 100644 --- a/drivers/net/can/dev/calc_bittiming.c +++ b/drivers/net/can/dev/calc_bittiming.c @@ -2,6 +2,7 @@ /* 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/units.h> @@ -9,6 +10,33 @@ #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ +/* CiA recommended sample points for Non Return to Zero encoding. */ +static int can_calc_sample_point_nrz(const struct can_bittiming *bt) +{ + if (bt->bitrate > 800 * KILO /* BPS */) + return 750; + + if (bt->bitrate > 500 * KILO /* BPS */) + return 800; + + return 875; +} + +/* Sample points for Pulse-Width Modulation encoding. */ +static int can_calc_sample_point_pwm(const struct can_bittiming *bt) +{ + if (bt->bitrate > 15 * MEGA /* BPS */) + return 625; + + if (bt->bitrate > 9 * MEGA /* BPS */) + return 600; + + if (bt->bitrate > 4 * MEGA /* BPS */) + return 560; + + return 520; +} + /* Bit-timing calculation derived from: * * Code based on LinCAN sources and H8S2638 project @@ -23,7 +51,7 @@ */ static int can_update_sample_point(const struct can_bittiming_const *btc, - const unsigned int sample_point_nominal, const unsigned int tseg, + const unsigned int sample_point_reference, const unsigned int tseg, unsigned int *tseg1_ptr, unsigned int *tseg2_ptr, unsigned int *sample_point_error_ptr) { @@ -34,7 +62,7 @@ can_update_sample_point(const struct can_bittiming_const *btc, for (i = 0; i <= 1; i++) { tseg2 = tseg + CAN_SYNC_SEG - - (sample_point_nominal * (tseg + CAN_SYNC_SEG)) / + (sample_point_reference * (tseg + CAN_SYNC_SEG)) / 1000 - i; tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max); tseg1 = tseg - tseg2; @@ -45,9 +73,9 @@ can_update_sample_point(const struct can_bittiming_const *btc, sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) / (tseg + CAN_SYNC_SEG); - sample_point_error = abs(sample_point_nominal - sample_point); + sample_point_error = abs(sample_point_reference - sample_point); - if (sample_point <= sample_point_nominal && + if (sample_point <= sample_point_reference && sample_point_error < best_sample_point_error) { best_sample_point = sample_point; best_sample_point_error = sample_point_error; @@ -67,28 +95,24 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, { struct can_priv *priv = netdev_priv(dev); unsigned int bitrate; /* current bitrate */ - unsigned int bitrate_error; /* difference between current and nominal value */ + unsigned int bitrate_error; /* diff between calculated and reference value */ unsigned int best_bitrate_error = UINT_MAX; - unsigned int sample_point_error; /* difference between current and nominal value */ + unsigned int sample_point_error; /* diff between calculated and reference value */ unsigned int best_sample_point_error = UINT_MAX; - unsigned int sample_point_nominal; /* nominal sample point */ + unsigned int sample_point_reference; /* reference sample point */ unsigned int best_tseg = 0; /* current best value for tseg */ unsigned int best_brp = 0; /* current best value for brp */ unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0; u64 v64; int err; - /* Use CiA recommended sample points */ - if (bt->sample_point) { - sample_point_nominal = bt->sample_point; - } else { - if (bt->bitrate > 800 * KILO /* BPS */) - sample_point_nominal = 750; - else if (bt->bitrate > 500 * KILO /* BPS */) - sample_point_nominal = 800; - else - sample_point_nominal = 875; - } + if (bt->sample_point) + sample_point_reference = bt->sample_point; + else if (btc == priv->xl.data_bittiming_const && + (priv->ctrlmode & CAN_CTRLMODE_XL_TMS)) + sample_point_reference = can_calc_sample_point_pwm(bt); + else + sample_point_reference = can_calc_sample_point_nrz(bt); /* tseg even = round down, odd = round up */ for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; @@ -114,7 +138,7 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, if (bitrate_error < best_bitrate_error) best_sample_point_error = UINT_MAX; - can_update_sample_point(btc, sample_point_nominal, tseg / 2, + can_update_sample_point(btc, sample_point_reference, tseg / 2, &tseg1, &tseg2, &sample_point_error); if (sample_point_error >= best_sample_point_error) continue; @@ -129,23 +153,26 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, } if (best_bitrate_error) { - /* Error in one-tenth of a percent */ - v64 = (u64)best_bitrate_error * 1000; + /* Error in one-hundredth of a percent */ + v64 = (u64)best_bitrate_error * 10000; do_div(v64, bt->bitrate); bitrate_error = (u32)v64; + /* print at least 0.01% if the error is smaller */ + bitrate_error = max(bitrate_error, 1U); if (bitrate_error > CAN_CALC_MAX_ERROR) { NL_SET_ERR_MSG_FMT(extack, - "bitrate error: %u.%u%% too high", - bitrate_error / 10, bitrate_error % 10); + "bitrate error: %u.%02u%% too high", + bitrate_error / 100, + bitrate_error % 100); return -EINVAL; } NL_SET_ERR_MSG_FMT(extack, - "bitrate error: %u.%u%%", - bitrate_error / 10, bitrate_error % 10); + "bitrate error: %u.%02u%%", + bitrate_error / 100, bitrate_error % 100); } /* real sample point */ - bt->sample_point = can_update_sample_point(btc, sample_point_nominal, + bt->sample_point = can_update_sample_point(btc, sample_point_reference, best_tseg, &tseg1, &tseg2, NULL); @@ -198,3 +225,38 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const, *ctrlmode |= tdc_auto; } } + +int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack) +{ + struct can_priv *priv = netdev_priv(dev); + const struct can_pwm_const *pwm_const = priv->xl.pwm_const; + struct can_pwm *pwm = &priv->xl.pwm; + u32 xl_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming); + u32 xl_ns = can_tqmin_to_ns(xl_tqmin, priv->clock.freq); + u32 nom_tqmin = can_bit_time_tqmin(&priv->bittiming); + int pwm_per_bit_max = xl_tqmin / (pwm_const->pwms_min + pwm_const->pwml_min); + int pwm_per_bit; + u32 pwm_tqmin; + + /* For 5 MB/s databitrate or greater, xl_ns < CAN_PWM_NS_MAX + * giving us a pwm_per_bit of 1 and the loop immediately breaks + */ + for (pwm_per_bit = DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX); + pwm_per_bit <= pwm_per_bit_max; pwm_per_bit++) + if (xl_tqmin % pwm_per_bit == 0) + break; + + if (pwm_per_bit > pwm_per_bit_max) { + NL_SET_ERR_MSG_FMT(extack, + "Can not divide the XL data phase's bit time: %u tqmin into multiple PWM symbols", + xl_tqmin); + return -EINVAL; + } + + pwm_tqmin = xl_tqmin / pwm_per_bit; + pwm->pwms = DIV_ROUND_UP_POW2(pwm_tqmin, 4); + pwm->pwml = pwm_tqmin - pwm->pwms; + pwm->pwmo = nom_tqmin % pwm_tqmin; + + return 0; +} diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c index 15ccedbb3f8d..091f30e94c61 100644 --- a/drivers/net/can/dev/dev.c +++ b/drivers/net/can/dev/dev.c @@ -92,29 +92,39 @@ const char *can_get_ctrlmode_str(u32 ctrlmode) { switch (ctrlmode & ~(ctrlmode - 1)) { case 0: - return "none"; + return "(none)"; case CAN_CTRLMODE_LOOPBACK: - return "loopback"; + return "LOOPBACK"; case CAN_CTRLMODE_LISTENONLY: - return "listen-only"; + return "LISTEN-ONLY"; case CAN_CTRLMODE_3_SAMPLES: - return "triple-sampling"; + return "TRIPLE-SAMPLING"; case CAN_CTRLMODE_ONE_SHOT: - return "one-shot"; + return "ONE-SHOT"; case CAN_CTRLMODE_BERR_REPORTING: - return "berr-reporting"; + return "BERR-REPORTING"; case CAN_CTRLMODE_FD: - return "fd"; + return "FD"; case CAN_CTRLMODE_PRESUME_ACK: - return "presume-ack"; + return "PRESUME-ACK"; case CAN_CTRLMODE_FD_NON_ISO: - return "fd-non-iso"; + return "FD-NON-ISO"; case CAN_CTRLMODE_CC_LEN8_DLC: - return "cc-len8-dlc"; + return "CC-LEN8-DLC"; case CAN_CTRLMODE_TDC_AUTO: - return "fd-tdc-auto"; + return "TDC-AUTO"; case CAN_CTRLMODE_TDC_MANUAL: - return "fd-tdc-manual"; + return "TDC-MANUAL"; + case CAN_CTRLMODE_RESTRICTED: + return "RESTRICTED"; + 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"; + case CAN_CTRLMODE_XL_TMS: + return "TMS"; default: return "<unknown>"; } @@ -348,7 +358,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; @@ -359,44 +375,6 @@ void can_set_default_mtu(struct net_device *dev) } } -/* changing MTU and control mode for CAN/CANFD devices */ -int can_change_mtu(struct net_device *dev, int new_mtu) -{ - struct can_priv *priv = netdev_priv(dev); - u32 ctrlmode_static = can_get_static_ctrlmode(priv); - - /* Do not allow changing the MTU while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - - /* 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 (ctrlmode_static & CAN_CTRLMODE_FD) - return -EINVAL; - - priv->ctrlmode &= ~CAN_CTRLMODE_FD; - break; - - case CANFD_MTU: - /* check for potential CANFD ability */ - if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && - !(ctrlmode_static & CAN_CTRLMODE_FD)) - return -EINVAL; - - priv->ctrlmode |= CAN_CTRLMODE_FD; - break; - - default: - return -EINVAL; - } - - WRITE_ONCE(dev->mtu, new_mtu); - return 0; -} -EXPORT_SYMBOL_GPL(can_change_mtu); - /* helper to define static CAN controller features at device creation time */ int can_set_static_ctrlmode(struct net_device *dev, u32 static_mode) { @@ -417,34 +395,33 @@ int can_set_static_ctrlmode(struct net_device *dev, u32 static_mode) } EXPORT_SYMBOL_GPL(can_set_static_ctrlmode); -/* generic implementation of netdev_ops::ndo_eth_ioctl for CAN devices +/* generic implementation of netdev_ops::ndo_hwtstamp_get for CAN devices * supporting hardware timestamps */ -int can_eth_ioctl_hwts(struct net_device *netdev, struct ifreq *ifr, int cmd) +int can_hwtstamp_get(struct net_device *netdev, + struct kernel_hwtstamp_config *cfg) { - struct hwtstamp_config hwts_cfg = { 0 }; - - switch (cmd) { - case SIOCSHWTSTAMP: /* set */ - if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg))) - return -EFAULT; - if (hwts_cfg.tx_type == HWTSTAMP_TX_ON && - hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL) - return 0; - return -ERANGE; - - case SIOCGHWTSTAMP: /* get */ - hwts_cfg.tx_type = HWTSTAMP_TX_ON; - hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL; - if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg))) - return -EFAULT; - return 0; + cfg->tx_type = HWTSTAMP_TX_ON; + cfg->rx_filter = HWTSTAMP_FILTER_ALL; - default: - return -EOPNOTSUPP; - } + return 0; +} +EXPORT_SYMBOL(can_hwtstamp_get); + +/* generic implementation of netdev_ops::ndo_hwtstamp_set for CAN devices + * supporting hardware timestamps + */ +int can_hwtstamp_set(struct net_device *netdev, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) +{ + if (cfg->tx_type == HWTSTAMP_TX_ON && + cfg->rx_filter == HWTSTAMP_FILTER_ALL) + return 0; + NL_SET_ERR_MSG_MOD(extack, "Only TX on and RX all packets filter supported"); + return -ERANGE; } -EXPORT_SYMBOL(can_eth_ioctl_hwts); +EXPORT_SYMBOL(can_hwtstamp_set); /* generic implementation of ethtool_ops::get_ts_info for CAN devices * supporting hardware timestamps diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c index 6f83b87d54fc..d6b0e686fb11 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@grandegger.com> - * Copyright (C) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org> */ #include <linux/can/dev.h> @@ -22,6 +22,10 @@ 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 }, + [IFLA_CAN_XL_PWM] = { .type = NLA_NESTED }, }; static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { @@ -36,6 +40,18 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { [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) @@ -70,7 +86,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 +98,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]; @@ -116,6 +132,40 @@ static int can_validate_tdc(struct nlattr *data_tdc, 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) @@ -126,10 +176,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 +189,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) { @@ -175,6 +228,32 @@ static int can_validate_databittiming(struct nlattr *data[], 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) { @@ -188,6 +267,17 @@ 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_xl_flags(extack, flags, cm->mask); + if (err) + return err; } err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING); @@ -199,6 +289,15 @@ 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; + + err = can_validate_pwm(data, extack, flags); + if (err) + return err; + return 0; } @@ -208,7 +307,7 @@ 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; + u32 ctrlstatic, maskedflags, deactivated, notsupp, ctrlstatic_missing; if (!data[IFLA_CAN_CTRLMODE]) return 0; @@ -220,6 +319,7 @@ static int can_ctrlmode_changelink(struct net_device *dev, 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; @@ -241,21 +341,40 @@ static int can_ctrlmode_changelink(struct net_device *dev, 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 config */ + /* 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); @@ -330,7 +449,10 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[], dbt_params = &priv->fd; tdc_mask = CAN_CTRLMODE_FD_TDC_MASK; } else { - return -EOPNOTSUPP; /* Place holder for CAN XL */ + 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) @@ -366,7 +488,8 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[], if (data[IFLA_CAN_CTRLMODE]) { struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); - need_tdc_calc = !(cm->mask & tdc_mask); + if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS)) + need_tdc_calc = !(cm->mask & tdc_mask); } if (data_tdc) { /* TDC parameters are provided: use them */ @@ -381,7 +504,7 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[], */ can_calc_tdco(&dbt_params->tdc, dbt_params->tdc_const, &dbt, tdc_mask, &priv->ctrlmode, priv->ctrlmode_supported); - } /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly + } /* else: both CAN_CTRLMODE_{,XL}_TDC_{AUTO,MANUAL} are explicitly * turned off. TDC is disabled: do nothing */ @@ -397,6 +520,76 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[], 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) @@ -486,6 +679,14 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[], 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; @@ -553,14 +754,14 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para { size_t size = 0; - if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */ + 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_DATA_BITTIMING_CONST */ + 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_DATA_BITRATE_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_TDC */ + size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */ return size; } @@ -571,6 +772,30 @@ static size_t can_ctrlmode_ext_get_size(void) 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); @@ -600,6 +825,11 @@ static size_t can_get_size(const struct net_device *dev) 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; } @@ -644,7 +874,9 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev, tdc_is_enabled = can_fd_tdc_is_enabled(priv); tdc_manual = priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL; } else { - return -EOPNOTSUPP; /* Place holder for CAN XL */ + 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; @@ -695,6 +927,42 @@ err_cancel: 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) { @@ -766,9 +1034,22 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) || - can_ctrlmode_ext_fill_info(skb, priv) - ) + 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; |
