From nobody Mon Feb 9 01:29:56 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 833492C11CB; Mon, 1 Dec 2025 22:56:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764629808; cv=none; b=J2daRZJnNuL7Dq4yWU6ILphmHxVEBH6VvEWiRLC5ppQTKwAPhFTg5BWTqyefIjxHDcgMIqp2hWc44qYKjhJ+n5GXnk40rC+oaM5KaBky0kBW6xRRAEwJyUhuGKf5ntW5k6OYmwNuphPeLmTgHv89ofXy4eSIGIaW6RpMHIq6vSQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764629808; c=relaxed/simple; bh=CAqoEU5ZBw9f4vxKsNKUAmN5pLXt7BE/7ve/chDvvqE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JYIBWzr1wBU5Tz8rc22STKm0ZZ3bqlX2fxtl0n+D2ievKZk3VD3I/Zm09PyeKPKbQwbVQROzqDXTjnkcDS+XKpzBLIU5V00QTzZ1LumY7BTquWJsB1osL1IoPZwHtCyl6lQ3hUbEdSg3z56geaAHvPq8VgsPdYLGd5qYsZ0a91c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Er1vY3Mz; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Er1vY3Mz" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AEDCAC116C6; Mon, 1 Dec 2025 22:56:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1764629808; bh=CAqoEU5ZBw9f4vxKsNKUAmN5pLXt7BE/7ve/chDvvqE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Er1vY3Mz1VGG9PSA9rBZabF50+1qF+Y8N6YuXJ3lK+canho2dMOSKaW5no+g78A2/ akNj+3VFoNlHi3qKY6uuFWZLP2YCZ6ZxLT+rbn0Uj0afdcMpX/emzz7uB37CbehCku uVFBmnS9ebVqmKHHs+BkfqDx/x/3PCldDO7pXP07ZqmerL0vRwJ5hC3Kp2Ke3FnteL VMGVkKmy4wAMq2z5GpQSEIM0C7IRvCVWxDLvDz3OOg4ThYAiWlwHUhTqX4Y0YJqckl fwdDfmRBOCwq1qYJ6tHusYK0rITBuZy/PbfXoqPoEy69ker67invPQk1V9H5h/1KxK Lv86g+OMREVxQ== From: Vincent Mailhol Date: Mon, 01 Dec 2025 23:55:15 +0100 Subject: [PATCH iproute2-next v2 7/7] iplink_can: add CAN XL's PWM interface Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251201-canxl-netlink-v2-7-dadfac811872@kernel.org> References: <20251201-canxl-netlink-v2-0-dadfac811872@kernel.org> In-Reply-To: <20251201-canxl-netlink-v2-0-dadfac811872@kernel.org> To: netdev@vger.kernel.org, Stephen Hemminger , Marc Kleine-Budde , Oliver Hartkopp , David Ahern Cc: Rakuram Eswaran , =?utf-8?q?St=C3=A9phane_Grosjean?= , linux-kernel@vger.kernel.org, linux-can@vger.kernel.org, Vincent Mailhol X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=7660; i=mailhol@kernel.org; h=from:subject:message-id; bh=CAqoEU5ZBw9f4vxKsNKUAmN5pLXt7BE/7ve/chDvvqE=; b=owGbwMvMwCV2McXO4Xp97WbG02pJDJl6ssJ9F5km20itayt/3+xzoVn3BNP/7+EXzi6RyXufd rb7ztJ1HaUsDGJcDLJiiizLyjm5FToKvcMO/bWEmcPKBDKEgYtTACYSPYfhv+trhWIps92rd5/m sec62XbBsmhJYvVj/t4Lr7f3fE99bszwTz1byOTDAaeq8On7r6s43lVKTDWLfbrsjMd670vbmfp XcAAA X-Developer-Key: i=mailhol@kernel.org; a=openpgp; fpr=ED8F700574E67F20E574E8E2AB5FEB886DBB99C2 This is the iproute2 counterpart of Linux kernel's commit 46552323fa67 ("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, the CAN netlink interface defines 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. Add the "pwms", "pwml" and "pwmo" options to iplink_can which controls the IFLA_CAN_PWM_PWM* runtime values. Add the logic to query and print all those IFLA values. Update print_usage() accordingly. Example using the dummy_can driver: # modprobe dummy_can # ip link set can0 type can bitrate 1000000 xl on xbitrate 20000000 tms on $ ip --details link show can0 5: can0: mtu 2060 qdisc noop state DOWN mode DEFAULT group defaul= t qlen 10 link/can promiscuity 0 allmulti 0 minmtu 76 maxmtu 2060 can state STOPPED restart-ms 0 bitrate 1000000 sample-point 0.750 tq 6 prop-seg 59 phase-seg1 60 phase-seg2 40 sjw 20 brp 1 dummy_can CC: tseg1 2..256 tseg2 2..128 sjw 1..128 brp 1..512 brp_inc 1 dummy_can FD: dtseg1 2..256 dtseg2 2..128 dsjw 1..128 dbrp 1..512 dbrp= _inc 1 tdco 0..127 tdcf 0..127 xbitrate 20000000 xsample-point 0.625 xtq 6 xprop-seg 2 xphase-seg1 2 xphase-seg2 3 xsjw 1 xbrp 1 pwms 2 pwml 6 pwmo 0 dummy_can XL: xtseg1 2..256 xtseg2 2..128 xsjw 1..128 xbrp 1..512 xbrp= _inc 1 xtdco 0..127 xtdcf 0..127 pwms 1..8 pwml 2..24 pwmo 0..16 clock 160000000 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max= _segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ip= v4_max_size 65536 gro_ipv4_max_size 65536 Signed-off-by: Vincent Mailhol --- Changelog: v1 -> v2: - Remove a double space in patch description - s/matches/strcmp/g in can_parse_opt() --- ip/iplink_can.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/ip/iplink_can.c b/ip/iplink_can.c index d91e807b..2ce54f0a 100644 --- a/ip/iplink_can.c +++ b/ip/iplink_can.c @@ -34,7 +34,7 @@ static void print_usage(FILE *f) "\n" "\t[ xbitrate BITRATE [ xsample-point SAMPLE-POINT] ] |\n" "\t[ xtq TQ xprop-seg PROP_SEG xphase-seg1 PHASE-SEG1\n \t xphase-seg2 = PHASE-SEG2 [ xsjw SJW ] ]\n" - "\t[ xtdcv TDCV xtdco TDCO xtdcf TDCF ]\n" + "\t[ xtdcv TDCV xtdco TDCO xtdcf TDCF pwms PWMS pwml PWML pwmo PWMO]\n" "\n" "\t[ loopback { on | off } ]\n" "\t[ listen-only { on | off } ]\n" @@ -67,6 +67,9 @@ static void print_usage(FILE *f) "\t TDCV :=3D { NUMBER in mtq }\n" "\t TDCO :=3D { NUMBER in mtq }\n" "\t TDCF :=3D { NUMBER in mtq }\n" + "\t PWMS :=3D { NUMBER in mtq }\n" + "\t PWML :=3D { NUMBER in mtq }\n" + "\t PWMO :=3D { NUMBER in mtq }\n" "\t RESTART-MS :=3D { 0 | NUMBER in ms }\n" "\n" "\tUnits:\n" @@ -143,6 +146,7 @@ static int can_parse_opt(struct link_util *lu, int argc= , char **argv, struct can_ctrlmode cm =3D { 0 }; struct can_tdc fd =3D { .tdcv =3D -1, .tdco =3D -1, .tdcf =3D -1 }; struct can_tdc xl =3D { .tdcv =3D -1, .tdco =3D -1, .tdcf =3D -1 }; + __u32 pwms =3D -1, pwml =3D -1, pwmo =3D -1; =20 while (argc > 0) { if (matches(*argv, "bitrate") =3D=3D 0) { @@ -266,6 +270,18 @@ static int can_parse_opt(struct link_util *lu, int arg= c, char **argv, NEXT_ARG(); if (get_u32(&xl.tdcf, *argv, 0)) invarg("invalid \"xtdcf\" value", *argv); + } else if (strcmp(*argv, "pwms") =3D=3D 0) { + NEXT_ARG(); + if (get_u32(&pwms, *argv, 0)) + invarg("invalid \"pwms\" value", *argv); + } else if (strcmp(*argv, "pwml") =3D=3D 0) { + NEXT_ARG(); + if (get_u32(&pwml, *argv, 0)) + invarg("invalid \"pwml\" value", *argv); + } else if (strcmp(*argv, "pwmo") =3D=3D 0) { + NEXT_ARG(); + if (get_u32(&pwmo, *argv, 0)) + invarg("invalid \"pwmo\" value", *argv); } else if (matches(*argv, "loopback") =3D=3D 0) { NEXT_ARG(); set_ctrlmode("loopback", *argv, &cm, @@ -401,6 +417,18 @@ static int can_parse_opt(struct link_util *lu, int arg= c, char **argv, addattr32(n, 1024, IFLA_CAN_TDC_TDCF, xl.tdcf); addattr_nest_end(n, tdc); } + if (pwms !=3D -1 || pwml !=3D -1 || pwmo !=3D -1) { + struct rtattr *pwm =3D addattr_nest(n, 1024, + IFLA_CAN_XL_PWM | NLA_F_NESTED); + + if (pwms !=3D -1) + addattr32(n, 1024, IFLA_CAN_PWM_PWMS, pwms); + if (pwml !=3D -1) + addattr32(n, 1024, IFLA_CAN_PWM_PWML, pwml); + if (pwmo !=3D -1) + addattr32(n, 1024, IFLA_CAN_PWM_PWMO, pwmo); + addattr_nest_end(n, pwm); + } =20 return 0; } @@ -496,6 +524,62 @@ static void can_print_tdc_const_opt(struct rtattr *tdc= _attr, bool is_xl) close_json_object(); } =20 +static void can_print_pwm_opt(struct rtattr *pwm_attr) +{ + struct rtattr *tb[IFLA_CAN_PWM_MAX + 1]; + + parse_rtattr_nested(tb, IFLA_CAN_PWM_MAX, pwm_attr); + if (tb[IFLA_CAN_PWM_PWMS] || tb[IFLA_CAN_PWM_PWML] || + tb[IFLA_CAN_PWM_PWMO]) { + open_json_object("pwm"); + can_print_nl_indent(); + if (tb[IFLA_CAN_PWM_PWMS]) { + __u32 *pwms =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMS]); + + print_uint(PRINT_ANY, " pwms", " pwms %u", *pwms); + } + if (tb[IFLA_CAN_PWM_PWML]) { + __u32 *pwml =3D RTA_DATA(tb[IFLA_CAN_PWM_PWML]); + + print_uint(PRINT_ANY, " pwml", " pwml %u", *pwml); + } + if (tb[IFLA_CAN_PWM_PWMO]) { + __u32 *pwmo =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMO]); + + print_uint(PRINT_ANY, " pwmo", " pwmo %u", *pwmo); + } + close_json_object(); + } +} + +static void can_print_pwm_const_opt(struct rtattr *pwm_attr) +{ + struct rtattr *tb[IFLA_CAN_PWM_MAX + 1]; + + parse_rtattr_nested(tb, IFLA_CAN_PWM_MAX, pwm_attr); + open_json_object("pwm"); + can_print_nl_indent(); + if (tb[IFLA_CAN_PWM_PWMS_MAX]) { + __u32 *pwms_min =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMS_MIN]); + __u32 *pwms_max =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMS_MAX]); + + can_print_timing_min_max("pwms", " pwms", *pwms_min, *pwms_max); + } + if (tb[IFLA_CAN_PWM_PWML_MAX]) { + __u32 *pwml_min =3D RTA_DATA(tb[IFLA_CAN_PWM_PWML_MIN]); + __u32 *pwml_max =3D RTA_DATA(tb[IFLA_CAN_PWM_PWML_MAX]); + + can_print_timing_min_max("pwml", " pwml", *pwml_min, *pwml_max); + } + if (tb[IFLA_CAN_PWM_PWMO_MAX]) { + __u32 *pwmo_min =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMO_MIN]); + __u32 *pwmo_max =3D RTA_DATA(tb[IFLA_CAN_PWM_PWMO_MAX]); + + can_print_timing_min_max("pwmo", " pwmo", *pwmo_min, *pwmo_max); + } + close_json_object(); +} + static void can_print_ctrlmode_ext(struct rtattr *ctrlmode_ext_attr, __u32 cm_flags) { @@ -735,6 +819,9 @@ static void can_print_opt(struct link_util *lu, FILE *f= , struct rtattr *tb[]) if (tb[IFLA_CAN_XL_TDC]) can_print_tdc_opt(tb[IFLA_CAN_XL_TDC], true); =20 + if (tb[IFLA_CAN_XL_PWM]) + can_print_pwm_opt(tb[IFLA_CAN_XL_PWM]); + close_json_object(); } =20 @@ -759,6 +846,9 @@ static void can_print_opt(struct link_util *lu, FILE *f= , struct rtattr *tb[]) if (tb[IFLA_CAN_XL_TDC]) can_print_tdc_const_opt(tb[IFLA_CAN_XL_TDC], true); =20 + if (tb[IFLA_CAN_XL_PWM]) + can_print_pwm_const_opt(tb[IFLA_CAN_XL_PWM]); + close_json_object(); } =20 --=20 2.51.2