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

can: netlink: add PWM netlink interface

When the TMS is switched on, the node uses PWM (Pulse Width
Modulation) during the data phase instead of the classic NRZ (Non
Return to Zero) encoding.

PWM is configured by three parameters:

- PWMS: Pulse Width Modulation Short phase
- PWML: Pulse Width Modulation Long phase
- PWMO: Pulse Width Modulation Offset time

For each of these parameters, define three IFLA symbols:

- IFLA_CAN_PWM_PWM*_MIN: the minimum allowed value.
- IFLA_CAN_PWM_PWM*_MAX: the maximum allowed value.
- IFLA_CAN_PWM_PWM*: the runtime value.

This results in a total of nine IFLA symbols which are all nested in a
parent IFLA_CAN_XL_PWM symbol.

IFLA_CAN_PWM_PWM*_MIN and IFLA_CAN_PWM_PWM*_MAX define the range of
allowed values and will match the value statically configured by the
device in struct can_pwm_const.

IFLA_CAN_PWM_PWM* match the runtime values stored in struct can_pwm.
Those parameters may only be configured when the tms mode is on. If
the PWMS, PWML and PWMO parameters are provided, check that all the
needed parameters are present using can_validate_pwm(), then check
their value using can_validate_pwm_bittiming(). PWMO defaults to zero
if omitted. Otherwise, if CAN_CTRLMODE_XL_TMS is true but none of the
PWM parameters are provided, calculate them using can_calc_pwm().

Signed-off-by: Vincent Mailhol <mailhol@kernel.org>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20251126-canxl-v8-11-e7e3eb74f889@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Vincent Mailhol and committed by
Marc Kleine-Budde
46552323 9892339c

+215 -2
+190 -2
drivers/net/can/dev/netlink.c
··· 25 25 [IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) }, 26 26 [IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) }, 27 27 [IFLA_CAN_XL_TDC] = { .type = NLA_NESTED }, 28 + [IFLA_CAN_XL_PWM] = { .type = NLA_NESTED }, 28 29 }; 29 30 30 31 static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { ··· 38 37 [IFLA_CAN_TDC_TDCV] = { .type = NLA_U32 }, 39 38 [IFLA_CAN_TDC_TDCO] = { .type = NLA_U32 }, 40 39 [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 }, 40 + }; 41 + 42 + static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = { 43 + [IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 }, 44 + [IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 }, 45 + [IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 }, 46 + [IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 }, 47 + [IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 }, 48 + [IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 }, 49 + [IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 }, 50 + [IFLA_CAN_PWM_PWML] = { .type = NLA_U32 }, 51 + [IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 }, 41 52 }; 42 53 43 54 static int can_validate_bittiming(struct nlattr *data[], ··· 127 114 NL_SET_ERR_MSG(extack, "TDCO is missing"); 128 115 return -EOPNOTSUPP; 129 116 } 117 + } 118 + 119 + return 0; 120 + } 121 + 122 + static int can_validate_pwm(struct nlattr *data[], 123 + struct netlink_ext_ack *extack, u32 flags) 124 + { 125 + struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1]; 126 + int err; 127 + 128 + if (!data[IFLA_CAN_XL_PWM]) 129 + return 0; 130 + 131 + if (!(flags & CAN_CTRLMODE_XL_TMS)) { 132 + NL_SET_ERR_MSG(extack, "PWM requires TMS"); 133 + return -EOPNOTSUPP; 134 + } 135 + 136 + err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM], 137 + can_pwm_policy, extack); 138 + if (err) 139 + return err; 140 + 141 + if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) { 142 + NL_SET_ERR_MSG(extack, 143 + "Provide either both PWMS and PWML, or none for automatic calculation"); 144 + return -EOPNOTSUPP; 145 + } 146 + 147 + if (tb_pwm[IFLA_CAN_PWM_PWMO] && 148 + (!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) { 149 + NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML"); 150 + return -EOPNOTSUPP; 130 151 } 131 152 132 153 return 0; ··· 294 247 if (err) 295 248 return err; 296 249 250 + err = can_validate_pwm(data, extack, flags); 251 + if (err) 252 + return err; 253 + 297 254 return 0; 298 255 } 299 256 ··· 373 322 sizeof(priv->fd.data_bittiming)); 374 323 priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK; 375 324 memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc)); 325 + memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm)); 376 326 } 377 327 378 328 can_set_default_mtu(dev); ··· 520 468 return 0; 521 469 } 522 470 471 + static int can_pwm_changelink(struct net_device *dev, 472 + const struct nlattr *pwm_nla, 473 + struct netlink_ext_ack *extack) 474 + { 475 + struct can_priv *priv = netdev_priv(dev); 476 + const struct can_pwm_const *pwm_const = priv->xl.pwm_const; 477 + struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1]; 478 + struct can_pwm pwm = { 0 }; 479 + int err; 480 + 481 + if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS)) 482 + return 0; 483 + 484 + if (!pwm_const) { 485 + NL_SET_ERR_MSG(extack, "The device does not support PWM"); 486 + return -EOPNOTSUPP; 487 + } 488 + 489 + if (!pwm_nla) 490 + return can_calc_pwm(dev, extack); 491 + 492 + err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla, 493 + can_pwm_policy, extack); 494 + if (err) 495 + return err; 496 + 497 + if (tb_pwm[IFLA_CAN_PWM_PWMS]) { 498 + pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]); 499 + if (pwm.pwms < pwm_const->pwms_min || 500 + pwm.pwms > pwm_const->pwms_max) { 501 + NL_SET_ERR_MSG_FMT(extack, 502 + "PWMS: %u tqmin is out of range: %u...%u", 503 + pwm.pwms, pwm_const->pwms_min, 504 + pwm_const->pwms_max); 505 + return -EINVAL; 506 + } 507 + } 508 + 509 + if (tb_pwm[IFLA_CAN_PWM_PWML]) { 510 + pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]); 511 + if (pwm.pwml < pwm_const->pwml_min || 512 + pwm.pwml > pwm_const->pwml_max) { 513 + NL_SET_ERR_MSG_FMT(extack, 514 + "PWML: %u tqmin is out of range: %u...%u", 515 + pwm.pwml, pwm_const->pwml_min, 516 + pwm_const->pwml_max); 517 + return -EINVAL; 518 + } 519 + } 520 + 521 + if (tb_pwm[IFLA_CAN_PWM_PWMO]) { 522 + pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]); 523 + if (pwm.pwmo < pwm_const->pwmo_min || 524 + pwm.pwmo > pwm_const->pwmo_max) { 525 + NL_SET_ERR_MSG_FMT(extack, 526 + "PWMO: %u tqmin is out of range: %u...%u", 527 + pwm.pwmo, pwm_const->pwmo_min, 528 + pwm_const->pwmo_max); 529 + return -EINVAL; 530 + } 531 + } 532 + 533 + err = can_validate_pwm_bittiming(dev, &pwm, extack); 534 + if (err) 535 + return err; 536 + 537 + priv->xl.pwm = pwm; 538 + return 0; 539 + } 540 + 523 541 static int can_changelink(struct net_device *dev, struct nlattr *tb[], 524 542 struct nlattr *data[], 525 543 struct netlink_ext_ack *extack) ··· 683 561 err = can_dbt_changelink(dev, data, false, extack); 684 562 if (err) 685 563 return err; 564 + err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], extack); 565 + if (err) 566 + return err; 686 567 687 568 if (data[IFLA_CAN_TERMINATION]) { 688 569 const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]); ··· 772 647 nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */ 773 648 } 774 649 650 + static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const, 651 + bool pwm_on) 652 + { 653 + size_t size; 654 + 655 + if (!pwm_const || !pwm_on) 656 + return 0; 657 + 658 + size = nla_total_size(0); /* nest IFLA_CAN_PWM */ 659 + 660 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MIN */ 661 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MAX */ 662 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MIN */ 663 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MAX */ 664 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MIN */ 665 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MAX */ 666 + 667 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS */ 668 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML */ 669 + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO */ 670 + 671 + return size; 672 + } 673 + 775 674 static size_t can_get_size(const struct net_device *dev) 776 675 { 777 676 struct can_priv *priv = netdev_priv(dev); ··· 827 678 828 679 size += can_data_bittiming_get_size(&priv->xl, 829 680 priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK); 681 + size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */ 682 + priv->ctrlmode & CAN_CTRLMODE_XL_TMS); 830 683 831 684 return size; 832 685 } ··· 927 776 return -EMSGSIZE; 928 777 } 929 778 779 + static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv) 780 + { 781 + const struct can_pwm_const *pwm_const = priv->xl.pwm_const; 782 + const struct can_pwm *pwm = &priv->xl.pwm; 783 + struct nlattr *nest; 784 + 785 + if (!pwm_const) 786 + return 0; 787 + 788 + nest = nla_nest_start(skb, IFLA_CAN_XL_PWM); 789 + if (!nest) 790 + return -EMSGSIZE; 791 + 792 + if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) || 793 + nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) || 794 + nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) || 795 + nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) || 796 + nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) || 797 + nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max)) 798 + goto err_cancel; 799 + 800 + if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) { 801 + if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) || 802 + nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) || 803 + nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo)) 804 + goto err_cancel; 805 + } 806 + 807 + nla_nest_end(skb, nest); 808 + return 0; 809 + 810 + err_cancel: 811 + nla_nest_cancel(skb, nest); 812 + return -EMSGSIZE; 813 + } 814 + 930 815 static int can_ctrlmode_ext_fill_info(struct sk_buff *skb, 931 816 const struct can_priv *priv) 932 817 { ··· 1046 859 priv->xl.data_bitrate_const, 1047 860 priv->xl.data_bitrate_const_cnt) || 1048 861 1049 - can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) 1050 - ) 862 + can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) || 1051 863 864 + can_pwm_fill_info(skb, priv) 865 + ) 1052 866 return -EMSGSIZE; 1053 867 1054 868 return 0;
+25
include/uapi/linux/can/netlink.h
··· 5 5 * Definitions for the CAN netlink interface 6 6 * 7 7 * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com> 8 + * Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org> 8 9 * 9 10 * This program is free software; you can redistribute it and/or modify 10 11 * it under the terms of the version 2 of the GNU General Public License ··· 148 147 IFLA_CAN_XL_DATA_BITTIMING_CONST, 149 148 IFLA_CAN_XL_DATA_BITRATE_CONST, 150 149 IFLA_CAN_XL_TDC, 150 + IFLA_CAN_XL_PWM, 151 151 152 152 /* add new constants above here */ 153 153 __IFLA_CAN_MAX, ··· 188 186 /* add new constants above here */ 189 187 __IFLA_CAN_CTRLMODE, 190 188 IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1 189 + }; 190 + 191 + /* 192 + * CAN FD/XL Pulse-Width Modulation (PWM) 193 + * 194 + * Please refer to struct can_pwm_const and can_pwm in 195 + * include/linux/can/bittiming.h for further details. 196 + */ 197 + enum { 198 + IFLA_CAN_PWM_UNSPEC, 199 + IFLA_CAN_PWM_PWMS_MIN, /* u32 */ 200 + IFLA_CAN_PWM_PWMS_MAX, /* u32 */ 201 + IFLA_CAN_PWM_PWML_MIN, /* u32 */ 202 + IFLA_CAN_PWM_PWML_MAX, /* u32 */ 203 + IFLA_CAN_PWM_PWMO_MIN, /* u32 */ 204 + IFLA_CAN_PWM_PWMO_MAX, /* u32 */ 205 + IFLA_CAN_PWM_PWMS, /* u32 */ 206 + IFLA_CAN_PWM_PWML, /* u32 */ 207 + IFLA_CAN_PWM_PWMO, /* u32 */ 208 + 209 + /* add new constants above here */ 210 + __IFLA_CAN_PWM, 211 + IFLA_CAN_PWM_MAX = __IFLA_CAN_PWM - 1 191 212 }; 192 213 193 214 /* u16 termination range: 1..65535 Ohms */