History

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().

Vincent Mailhol committed 11 months ago

Commit 46552321 parent 98923392 files · +215 −2Browse files

Unified · Split

2 files
  1. drivers/net/can/dev/netlink.c +190 −2
  2. include/uapi/linux/can/netlink.h +25 −0
drivers/net/can/dev/netlink.c+190−2
@@ -25,6 +25,7 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { · 24 unmodified lines
25 [IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },25 [IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },
26 [IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },26 [IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },
27 [IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },27 [IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },
28 [IFLA_CAN_XL_PWM] = { .type = NLA_NESTED },
28};29};
29 30
30static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {31static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
@@ -39,6 +40,18 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { · 8 unmodified lines
39 [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },40 [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },
40};41};
41 42
43static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = {
44 [IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 },
45 [IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 },
46 [IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 },
47 [IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 },
48 [IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 },
49 [IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 },
50 [IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 },
51 [IFLA_CAN_PWM_PWML] = { .type = NLA_U32 },
52 [IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 },
53};
54
42static int can_validate_bittiming(struct nlattr *data[],55static int can_validate_bittiming(struct nlattr *data[],
43 struct netlink_ext_ack *extack,56 struct netlink_ext_ack *extack,
44 int ifla_can_bittiming)57 int ifla_can_bittiming)
@@ -119,6 +132,40 @@ static int can_validate_tdc(struct nlattr *data_tdc, · 74 unmodified lines
119 return 0;132 return 0;
120}133}
121 134
135static int can_validate_pwm(struct nlattr *data[],
136 struct netlink_ext_ack *extack, u32 flags)
137{
138 struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
139 int err;
140
141 if (!data[IFLA_CAN_XL_PWM])
142 return 0;
143
144 if (!(flags & CAN_CTRLMODE_XL_TMS)) {
145 NL_SET_ERR_MSG(extack, "PWM requires TMS");
146 return -EOPNOTSUPP;
147 }
148
149 err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM],
150 can_pwm_policy, extack);
151 if (err)
152 return err;
153
154 if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) {
155 NL_SET_ERR_MSG(extack,
156 "Provide either both PWMS and PWML, or none for automatic calculation");
157 return -EOPNOTSUPP;
158 }
159
160 if (tb_pwm[IFLA_CAN_PWM_PWMO] &&
161 (!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) {
162 NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML");
163 return -EOPNOTSUPP;
164 }
165
166 return 0;
167}
168
122static int can_validate_databittiming(struct nlattr *data[],169static int can_validate_databittiming(struct nlattr *data[],
123 struct netlink_ext_ack *extack,170 struct netlink_ext_ack *extack,
124 int ifla_can_data_bittiming, u32 flags)171 int ifla_can_data_bittiming, u32 flags)
@@ -247,6 +294,10 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[], · 122 unmodified lines
247 if (err)294 if (err)
248 return err;295 return err;
249 296
297 err = can_validate_pwm(data, extack, flags);
298 if (err)
299 return err;
300
250 return 0;301 return 0;
251}302}
252 303
@@ -322,6 +373,7 @@ static int can_ctrlmode_changelink(struct net_device *dev, · 69 unmodified lines
322 sizeof(priv->fd.data_bittiming));373 sizeof(priv->fd.data_bittiming));
323 priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;374 priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;
324 memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));375 memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));
376 memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm));
325 }377 }
326 378
327 can_set_default_mtu(dev);379 can_set_default_mtu(dev);
@@ -468,6 +520,76 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[], · 140 unmodified lines
468 return 0;520 return 0;
469}521}
470 522
523static int can_pwm_changelink(struct net_device *dev,
524 const struct nlattr *pwm_nla,
525 struct netlink_ext_ack *extack)
526{
527 struct can_priv *priv = netdev_priv(dev);
528 const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
529 struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
530 struct can_pwm pwm = { 0 };
531 int err;
532
533 if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
534 return 0;
535
536 if (!pwm_const) {
537 NL_SET_ERR_MSG(extack, "The device does not support PWM");
538 return -EOPNOTSUPP;
539 }
540
541 if (!pwm_nla)
542 return can_calc_pwm(dev, extack);
543
544 err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla,
545 can_pwm_policy, extack);
546 if (err)
547 return err;
548
549 if (tb_pwm[IFLA_CAN_PWM_PWMS]) {
550 pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]);
551 if (pwm.pwms < pwm_const->pwms_min ||
552 pwm.pwms > pwm_const->pwms_max) {
553 NL_SET_ERR_MSG_FMT(extack,
554 "PWMS: %u tqmin is out of range: %u...%u",
555 pwm.pwms, pwm_const->pwms_min,
556 pwm_const->pwms_max);
557 return -EINVAL;
558 }
559 }
560
561 if (tb_pwm[IFLA_CAN_PWM_PWML]) {
562 pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]);
563 if (pwm.pwml < pwm_const->pwml_min ||
564 pwm.pwml > pwm_const->pwml_max) {
565 NL_SET_ERR_MSG_FMT(extack,
566 "PWML: %u tqmin is out of range: %u...%u",
567 pwm.pwml, pwm_const->pwml_min,
568 pwm_const->pwml_max);
569 return -EINVAL;
570 }
571 }
572
573 if (tb_pwm[IFLA_CAN_PWM_PWMO]) {
574 pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]);
575 if (pwm.pwmo < pwm_const->pwmo_min ||
576 pwm.pwmo > pwm_const->pwmo_max) {
577 NL_SET_ERR_MSG_FMT(extack,
578 "PWMO: %u tqmin is out of range: %u...%u",
579 pwm.pwmo, pwm_const->pwmo_min,
580 pwm_const->pwmo_max);
581 return -EINVAL;
582 }
583 }
584
585 err = can_validate_pwm_bittiming(dev, &pwm, extack);
586 if (err)
587 return err;
588
589 priv->xl.pwm = pwm;
590 return 0;
591}
592
471static int can_changelink(struct net_device *dev, struct nlattr *tb[],593static int can_changelink(struct net_device *dev, struct nlattr *tb[],
472 struct nlattr *data[],594 struct nlattr *data[],
473 struct netlink_ext_ack *extack)595 struct netlink_ext_ack *extack)
@@ -559,6 +681,9 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[], · 85 unmodified lines
559 681
560 /* CAN XL */682 /* CAN XL */
561 err = can_dbt_changelink(dev, data, false, extack);683 err = can_dbt_changelink(dev, data, false, extack);
684 if (err)
685 return err;
686 err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], extack);
562 if (err)687 if (err)
563 return err;688 return err;
564 689
@@ -647,6 +772,30 @@ static size_t can_ctrlmode_ext_get_size(void) · 82 unmodified lines
647 nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */772 nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */
648}773}
649 774
775static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const,
776 bool pwm_on)
777{
778 size_t size;
779
780 if (!pwm_const || !pwm_on)
781 return 0;
782
783 size = nla_total_size(0); /* nest IFLA_CAN_PWM */
784
785 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MIN */
786 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MAX */
787 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MIN */
788 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MAX */
789 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MIN */
790 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MAX */
791
792 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS */
793 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML */
794 size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO */
795
796 return size;
797}
798
650static size_t can_get_size(const struct net_device *dev)799static size_t can_get_size(const struct net_device *dev)
651{800{
652 struct can_priv *priv = netdev_priv(dev);801 struct can_priv *priv = netdev_priv(dev);
@@ -678,6 +827,8 @@ static size_t can_get_size(const struct net_device *dev) · 25 unmodified lines
678 827
679 size += can_data_bittiming_get_size(&priv->xl,828 size += can_data_bittiming_get_size(&priv->xl,
680 priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);829 priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
830 size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */
831 priv->ctrlmode & CAN_CTRLMODE_XL_TMS);
681 832
682 return size;833 return size;
683}834}
@@ -776,6 +927,42 @@ err_cancel: · 92 unmodified lines
776 return -EMSGSIZE;927 return -EMSGSIZE;
777}928}
778 929
930static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv)
931{
932 const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
933 const struct can_pwm *pwm = &priv->xl.pwm;
934 struct nlattr *nest;
935
936 if (!pwm_const)
937 return 0;
938
939 nest = nla_nest_start(skb, IFLA_CAN_XL_PWM);
940 if (!nest)
941 return -EMSGSIZE;
942
943 if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) ||
944 nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) ||
945 nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) ||
946 nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) ||
947 nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) ||
948 nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max))
949 goto err_cancel;
950
951 if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
952 if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) ||
953 nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) ||
954 nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo))
955 goto err_cancel;
956 }
957
958 nla_nest_end(skb, nest);
959 return 0;
960
961err_cancel:
962 nla_nest_cancel(skb, nest);
963 return -EMSGSIZE;
964}
965
779static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,966static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,
780 const struct can_priv *priv)967 const struct can_priv *priv)
781{968{
@@ -859,9 +1046,10 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) · 77 unmodified lines
859 priv->xl.data_bitrate_const,1046 priv->xl.data_bitrate_const,
860 priv->xl.data_bitrate_const_cnt) ||1047 priv->xl.data_bitrate_const_cnt) ||
861 1048
862 can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC)1049 can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) ||
863 )
864 1050
1051 can_pwm_fill_info(skb, priv)
1052 )
865 return -EMSGSIZE;1053 return -EMSGSIZE;
866 1054
867 return 0;1055 return 0;
include/uapi/linux/can/netlink.h+25−0
@@ -5,6 +5,7 @@ · 4 unmodified lines
5 * Definitions for the CAN netlink interface5 * Definitions for the CAN netlink interface
6 *6 *
7 * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>7 * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
8 * Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
8 *9 *
9 * This program is free software; you can redistribute it and/or modify10 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the version 2 of the GNU General Public License11 * it under the terms of the version 2 of the GNU General Public License
@@ -147,6 +148,7 @@ enum { · 136 unmodified lines
147 IFLA_CAN_XL_DATA_BITTIMING_CONST,148 IFLA_CAN_XL_DATA_BITTIMING_CONST,
148 IFLA_CAN_XL_DATA_BITRATE_CONST,149 IFLA_CAN_XL_DATA_BITRATE_CONST,
149 IFLA_CAN_XL_TDC,150 IFLA_CAN_XL_TDC,
151 IFLA_CAN_XL_PWM,
150 152
151 /* add new constants above here */153 /* add new constants above here */
152 __IFLA_CAN_MAX,154 __IFLA_CAN_MAX,
@@ -188,6 +190,29 @@ enum { · 35 unmodified lines
188 IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1190 IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1
189};191};
190 192
193/*
194 * CAN FD/XL Pulse-Width Modulation (PWM)
195 *
196 * Please refer to struct can_pwm_const and can_pwm in
197 * include/linux/can/bittiming.h for further details.
198 */
199enum {
200 IFLA_CAN_PWM_UNSPEC,
201 IFLA_CAN_PWM_PWMS_MIN, /* u32 */
202 IFLA_CAN_PWM_PWMS_MAX, /* u32 */
203 IFLA_CAN_PWM_PWML_MIN, /* u32 */
204 IFLA_CAN_PWM_PWML_MAX, /* u32 */
205 IFLA_CAN_PWM_PWMO_MIN, /* u32 */
206 IFLA_CAN_PWM_PWMO_MAX, /* u32 */
207 IFLA_CAN_PWM_PWMS, /* u32 */
208 IFLA_CAN_PWM_PWML, /* u32 */
209 IFLA_CAN_PWM_PWMO, /* u32 */
210
211 /* add new constants above here */
212 __IFLA_CAN_PWM,
213 IFLA_CAN_PWM_MAX = __IFLA_CAN_PWM - 1
214};
215
191/* u16 termination range: 1..65535 Ohms */216/* u16 termination range: 1..65535 Ohms */
192#define CAN_TERMINATION_DISABLED 0217#define CAN_TERMINATION_DISABLED 0
193 218