Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: netlink: add interface for CAN-FD Transmitter Delay Compensation (TDC)

Add the netlink interface for TDC parameters of struct can_tdc_const
and can_tdc.

Contrary to the can_bittiming(_const) structures for which there is
just a single IFLA_CAN(_DATA)_BITTMING(_CONST) entry per structure,
here, we create a nested entry IFLA_CAN_TDC. Within this nested entry,
additional IFLA_CAN_TDC_TDC* entries are added for each of the TDC
parameters of the newly introduced struct can_tdc_const and struct
can_tdc.

For struct can_tdc_const, these are:
IFLA_CAN_TDC_TDCV_MIN
IFLA_CAN_TDC_TDCV_MAX
IFLA_CAN_TDC_TDCO_MIN
IFLA_CAN_TDC_TDCO_MAX
IFLA_CAN_TDC_TDCF_MIN
IFLA_CAN_TDC_TDCF_MAX

For struct can_tdc, these are:
IFLA_CAN_TDC_TDCV
IFLA_CAN_TDC_TDCO
IFLA_CAN_TDC_TDCF

This is done so that changes can be applied in the future to the
structures without breaking the netlink interface.

The TDC netlink logic works as follow:

* CAN_CTRLMODE_FD is not provided:
- if any TDC parameters are provided: error.

- TDC parameters not provided: TDC parameters unchanged.

* CAN_CTRLMODE_FD is provided and is false:
- TDC is deactivated: both the structure and the
CAN_CTRLMODE_TDC_{AUTO,MANUAL} flags are flushed.

* CAN_CTRLMODE_FD provided and is true:
- CAN_CTRLMODE_TDC_{AUTO,MANUAL} and tdc{v,o,f} not provided: call
can_calc_tdco() to automatically decide whether TDC should be
activated and, if so, set CAN_CTRLMODE_TDC_AUTO and uses the
calculated tdco value.

- CAN_CTRLMODE_TDC_AUTO and tdco provided: set
CAN_CTRLMODE_TDC_AUTO and use the provided tdco value. Here,
tdcv is illegal and tdcf is optional.

- CAN_CTRLMODE_TDC_MANUAL and both of tdcv and tdco provided: set
CAN_CTRLMODE_TDC_MANUAL and use the provided tdcv and tdco
value. Here, tdcf is optional.

- CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually exclusive. Whenever
one flag is turned on, the other will automatically be turned
off. Providing both returns an error.

- Combination other than the one listed above are illegal and will
return an error.

N.B. above rules mean that whenever CAN_CTRLMODE_FD is provided, the
previous TDC values will be overwritten. The only option to reuse
previous TDC value is to not provide CAN_CTRLMODE_FD.

All the new parameters are defined as u32. This arbitrary choice is
done to mimic the other bittiming values with are also all of type
u32. An u16 would have been sufficient to hold the TDC values.

This patch completes below series (c.f. [1]):
- commit 289ea9e4ae59 ("can: add new CAN FD bittiming parameters:
Transmitter Delay Compensation (TDC)")
- commit c25cc7993243 ("can: bittiming: add calculation for CAN FD
Transmitter Delay Compensation (TDC)")

[1] https://lore.kernel.org/linux-can/20210224002008.4158-1-mailhol.vincent@wanadoo.fr/T/#t

Link: https://lore.kernel.org/all/20210918095637.20108-5-mailhol.vincent@wanadoo.fr
Signed-off-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Vincent Mailhol and committed by
Marc Kleine-Budde
d99755f7 da45a1e4

+235 -7
+208 -5
drivers/net/can/dev/netlink.c
··· 2 2 /* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix 3 3 * Copyright (C) 2006 Andrey Volkov, Varma Electronics 4 4 * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> 5 + * Copyright (C) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> 5 6 */ 6 7 7 8 #include <linux/can/dev.h> ··· 20 19 [IFLA_CAN_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) }, 21 20 [IFLA_CAN_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) }, 22 21 [IFLA_CAN_TERMINATION] = { .type = NLA_U16 }, 22 + [IFLA_CAN_TDC] = { .type = NLA_NESTED }, 23 + }; 24 + 25 + static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { 26 + [IFLA_CAN_TDC_TDCV_MIN] = { .type = NLA_U32 }, 27 + [IFLA_CAN_TDC_TDCV_MAX] = { .type = NLA_U32 }, 28 + [IFLA_CAN_TDC_TDCO_MIN] = { .type = NLA_U32 }, 29 + [IFLA_CAN_TDC_TDCO_MAX] = { .type = NLA_U32 }, 30 + [IFLA_CAN_TDC_TDCF_MIN] = { .type = NLA_U32 }, 31 + [IFLA_CAN_TDC_TDCF_MAX] = { .type = NLA_U32 }, 32 + [IFLA_CAN_TDC_TDCV] = { .type = NLA_U32 }, 33 + [IFLA_CAN_TDC_TDCO] = { .type = NLA_U32 }, 34 + [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 }, 23 35 }; 24 36 25 37 static int can_validate(struct nlattr *tb[], struct nlattr *data[], ··· 44 30 * - nominal/arbitration bittiming 45 31 * - data bittiming 46 32 * - control mode with CAN_CTRLMODE_FD set 33 + * - TDC parameters are coherent (details below) 47 34 */ 48 35 49 36 if (!data) ··· 52 37 53 38 if (data[IFLA_CAN_CTRLMODE]) { 54 39 struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); 40 + u32 tdc_flags = cm->flags & CAN_CTRLMODE_TDC_MASK; 55 41 56 42 is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; 43 + 44 + /* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually exclusive */ 45 + if (tdc_flags == CAN_CTRLMODE_TDC_MASK) 46 + return -EOPNOTSUPP; 47 + /* If one of the CAN_CTRLMODE_TDC_* flag is set then 48 + * TDC must be set and vice-versa 49 + */ 50 + if (!!tdc_flags != !!data[IFLA_CAN_TDC]) 51 + return -EOPNOTSUPP; 52 + /* If providing TDC parameters, at least TDCO is 53 + * needed. TDCV is needed if and only if 54 + * CAN_CTRLMODE_TDC_MANUAL is set 55 + */ 56 + if (data[IFLA_CAN_TDC]) { 57 + struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1]; 58 + int err; 59 + 60 + err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, 61 + data[IFLA_CAN_TDC], 62 + can_tdc_policy, extack); 63 + if (err) 64 + return err; 65 + 66 + if (tb_tdc[IFLA_CAN_TDC_TDCV]) { 67 + if (tdc_flags & CAN_CTRLMODE_TDC_AUTO) 68 + return -EOPNOTSUPP; 69 + } else { 70 + if (tdc_flags & CAN_CTRLMODE_TDC_MANUAL) 71 + return -EOPNOTSUPP; 72 + } 73 + 74 + if (!tb_tdc[IFLA_CAN_TDC_TDCO]) 75 + return -EOPNOTSUPP; 76 + } 57 77 } 58 78 59 79 if (is_can_fd) { ··· 96 46 return -EOPNOTSUPP; 97 47 } 98 48 99 - if (data[IFLA_CAN_DATA_BITTIMING]) { 49 + if (data[IFLA_CAN_DATA_BITTIMING] || data[IFLA_CAN_TDC]) { 100 50 if (!is_can_fd) 101 51 return -EOPNOTSUPP; 102 52 } 53 + 54 + return 0; 55 + } 56 + 57 + static int can_tdc_changelink(struct can_priv *priv, const struct nlattr *nla, 58 + struct netlink_ext_ack *extack) 59 + { 60 + struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1]; 61 + struct can_tdc tdc = { 0 }; 62 + const struct can_tdc_const *tdc_const = priv->tdc_const; 63 + int err; 64 + 65 + if (!tdc_const || !can_tdc_is_enabled(priv)) 66 + return -EOPNOTSUPP; 67 + 68 + err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, nla, 69 + can_tdc_policy, extack); 70 + if (err) 71 + return err; 72 + 73 + if (tb_tdc[IFLA_CAN_TDC_TDCV]) { 74 + u32 tdcv = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCV]); 75 + 76 + if (tdcv < tdc_const->tdcv_min || tdcv > tdc_const->tdcv_max) 77 + return -EINVAL; 78 + 79 + tdc.tdcv = tdcv; 80 + } 81 + 82 + if (tb_tdc[IFLA_CAN_TDC_TDCO]) { 83 + u32 tdco = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCO]); 84 + 85 + if (tdco < tdc_const->tdco_min || tdco > tdc_const->tdco_max) 86 + return -EINVAL; 87 + 88 + tdc.tdco = tdco; 89 + } 90 + 91 + if (tb_tdc[IFLA_CAN_TDC_TDCF]) { 92 + u32 tdcf = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCF]); 93 + 94 + if (tdcf < tdc_const->tdcf_min || tdcf > tdc_const->tdcf_max) 95 + return -EINVAL; 96 + 97 + tdc.tdcf = tdcf; 98 + } 99 + 100 + priv->tdc = tdc; 103 101 104 102 return 0; 105 103 } ··· 157 59 struct netlink_ext_ack *extack) 158 60 { 159 61 struct can_priv *priv = netdev_priv(dev); 62 + u32 tdc_mask = 0; 160 63 int err; 161 64 162 65 /* We need synchronization with dev->stop() */ ··· 237 138 dev->mtu = CAN_MTU; 238 139 memset(&priv->data_bittiming, 0, 239 140 sizeof(priv->data_bittiming)); 141 + priv->ctrlmode &= ~CAN_CTRLMODE_TDC_MASK; 142 + memset(&priv->tdc, 0, sizeof(priv->tdc)); 240 143 } 144 + 145 + tdc_mask = cm->mask & CAN_CTRLMODE_TDC_MASK; 146 + /* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually 147 + * exclusive: make sure to turn the other one off 148 + */ 149 + if (tdc_mask) 150 + priv->ctrlmode &= cm->flags | ~CAN_CTRLMODE_TDC_MASK; 241 151 } 242 152 243 153 if (data[IFLA_CAN_RESTART_MS]) { ··· 295 187 return -EINVAL; 296 188 } 297 189 298 - memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); 190 + memset(&priv->tdc, 0, sizeof(priv->tdc)); 191 + if (data[IFLA_CAN_TDC]) { 192 + /* TDC parameters are provided: use them */ 193 + err = can_tdc_changelink(priv, data[IFLA_CAN_TDC], 194 + extack); 195 + if (err) { 196 + priv->ctrlmode &= ~CAN_CTRLMODE_TDC_MASK; 197 + return err; 198 + } 199 + } else if (!tdc_mask) { 200 + /* Neither of TDC parameters nor TDC flags are 201 + * provided: do calculation 202 + */ 203 + can_calc_tdco(&priv->tdc, priv->tdc_const, &priv->data_bittiming, 204 + &priv->ctrlmode, priv->ctrlmode_supported); 205 + } /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly 206 + * turned off. TDC is disabled: do nothing 207 + */ 299 208 300 - can_calc_tdco(&priv->tdc, priv->tdc_const, &priv->data_bittiming, 301 - &priv->ctrlmode, priv->ctrlmode_supported); 209 + memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); 302 210 303 211 if (priv->do_set_data_bittiming) { 304 212 /* Finally, set the bit-timing registers */ ··· 351 227 return 0; 352 228 } 353 229 230 + static size_t can_tdc_get_size(const struct net_device *dev) 231 + { 232 + struct can_priv *priv = netdev_priv(dev); 233 + size_t size; 234 + 235 + if (!priv->tdc_const) 236 + return 0; 237 + 238 + size = nla_total_size(0); /* nest IFLA_CAN_TDC */ 239 + if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL) { 240 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MIN */ 241 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MAX */ 242 + } 243 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MIN */ 244 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MAX */ 245 + if (priv->tdc_const->tdcf_max) { 246 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MIN */ 247 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MAX */ 248 + } 249 + 250 + if (can_tdc_is_enabled(priv)) { 251 + if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL) 252 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV */ 253 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO */ 254 + if (priv->tdc_const->tdcf_max) 255 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF */ 256 + } 257 + 258 + return size; 259 + } 260 + 354 261 static size_t can_get_size(const struct net_device *dev) 355 262 { 356 263 struct can_priv *priv = netdev_priv(dev); ··· 413 258 size += nla_total_size(sizeof(*priv->data_bitrate_const) * 414 259 priv->data_bitrate_const_cnt); 415 260 size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ 261 + size += can_tdc_get_size(dev); /* IFLA_CAN_TDC */ 416 262 417 263 return size; 264 + } 265 + 266 + static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev) 267 + { 268 + struct nlattr *nest; 269 + struct can_priv *priv = netdev_priv(dev); 270 + struct can_tdc *tdc = &priv->tdc; 271 + const struct can_tdc_const *tdc_const = priv->tdc_const; 272 + 273 + if (!tdc_const) 274 + return 0; 275 + 276 + nest = nla_nest_start(skb, IFLA_CAN_TDC); 277 + if (!nest) 278 + return -EMSGSIZE; 279 + 280 + if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL && 281 + (nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MIN, tdc_const->tdcv_min) || 282 + nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MAX, tdc_const->tdcv_max))) 283 + goto err_cancel; 284 + if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MIN, tdc_const->tdco_min) || 285 + nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MAX, tdc_const->tdco_max)) 286 + goto err_cancel; 287 + if (tdc_const->tdcf_max && 288 + (nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MIN, tdc_const->tdcf_min) || 289 + nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MAX, tdc_const->tdcf_max))) 290 + goto err_cancel; 291 + 292 + if (can_tdc_is_enabled(priv)) { 293 + if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL && 294 + nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdc->tdcv)) 295 + goto err_cancel; 296 + if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO, tdc->tdco)) 297 + goto err_cancel; 298 + if (tdc_const->tdcf_max && 299 + nla_put_u32(skb, IFLA_CAN_TDC_TDCF, tdc->tdcf)) 300 + goto err_cancel; 301 + } 302 + 303 + nla_nest_end(skb, nest); 304 + return 0; 305 + 306 + err_cancel: 307 + nla_nest_cancel(skb, nest); 308 + return -EMSGSIZE; 418 309 } 419 310 420 311 static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) ··· 520 319 521 320 (nla_put(skb, IFLA_CAN_BITRATE_MAX, 522 321 sizeof(priv->bitrate_max), 523 - &priv->bitrate_max)) 322 + &priv->bitrate_max)) || 323 + 324 + (can_tdc_fill_info(skb, dev)) 524 325 ) 525 326 526 327 return -EMSGSIZE;
+27 -2
include/uapi/linux/can/netlink.h
··· 136 136 IFLA_CAN_BITRATE_CONST, 137 137 IFLA_CAN_DATA_BITRATE_CONST, 138 138 IFLA_CAN_BITRATE_MAX, 139 - __IFLA_CAN_MAX 139 + IFLA_CAN_TDC, 140 + 141 + /* add new constants above here */ 142 + __IFLA_CAN_MAX, 143 + IFLA_CAN_MAX = __IFLA_CAN_MAX - 1 140 144 }; 141 145 142 - #define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) 146 + /* 147 + * CAN FD Transmitter Delay Compensation (TDC) 148 + * 149 + * Please refer to struct can_tdc_const and can_tdc in 150 + * include/linux/can/bittiming.h for further details. 151 + */ 152 + enum { 153 + IFLA_CAN_TDC_UNSPEC, 154 + IFLA_CAN_TDC_TDCV_MIN, /* u32 */ 155 + IFLA_CAN_TDC_TDCV_MAX, /* u32 */ 156 + IFLA_CAN_TDC_TDCO_MIN, /* u32 */ 157 + IFLA_CAN_TDC_TDCO_MAX, /* u32 */ 158 + IFLA_CAN_TDC_TDCF_MIN, /* u32 */ 159 + IFLA_CAN_TDC_TDCF_MAX, /* u32 */ 160 + IFLA_CAN_TDC_TDCV, /* u32 */ 161 + IFLA_CAN_TDC_TDCO, /* u32 */ 162 + IFLA_CAN_TDC_TDCF, /* u32 */ 163 + 164 + /* add new constants above here */ 165 + __IFLA_CAN_TDC, 166 + IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1 167 + }; 143 168 144 169 /* u16 termination range: 1..65535 Ohms */ 145 170 #define CAN_TERMINATION_DISABLED 0