Merge patch series "can: netlink: preparation before introduction of CAN XL step 3/3"

Vincent Mailhol <mailhol@kernel.org> says:

In November last year, I sent an RFC to introduce CAN XL [1]. That
RFC, despite positive feedback, was put on hold due to some unanswered
question concerning the PWM encoding [2].

While stuck, some small preparation work was done in parallel in [3]
by refactoring the struct can_priv and doing some trivial clean-up and
renaming. Initially, [3] received zero feedback but was eventually
merged after splitting it in smaller parts and resending it.

Finally, in July this year, we clarified the remaining mysteries about
PWM calculation, thus unlocking the series. Summer being a bit busy
because of some personal matters brings us to now.

After doing all the refactoring and adding all the CAN XL features,
the final result is more than 30 patches, definitively too much for a
single series. So I am splitting the remaining changes three:

  - can: rework the CAN MTU logic [4]
  - can: netlink: preparation before introduction of CAN XL (this series)
  - CAN XL (will come right after the two preparation series get merged)

And thus, this series continues and finishes the preparation work done
in [3] and [4]. It contains all the refactoring needed to smoothly
introduce CAN XL. The goal is to:

  - split the functions in smaller pieces: CAN XL will introduce a
    fair amount of code. And some functions which are already fairly
    long (86 lines for can_validate(), 215 lines for can_changelink())
    would grow to disproportionate sizes if the CAN XL logic were to
    be inlined in those functions.

  - repurpose the existing code to handle both CAN FD and CAN XL: a
    huge part of CAN XL simply reuses the CAN FD logic. All the
    existing CAN FD logic is made more generic to handle both CAN FD
    and XL.

In more details:

  - Patch #1 moves struct data_bittiming_params from dev.h to
    bittiming.h and patch #2 makes can_get_relative_tdco() FD agnostic
    before also moving it to bittiming.h.

  - Patch #3 adds some comments to netlink.h tagging which IFLA
    symbols are FD specific.

  - Patches #4 to #6 are refactoring can_validate() and
    can_validate_bittiming().

  - Patches #7 to #11 are refactoring can_changelink() and
    can_tdc_changelink().

  - Patches #12 and #13 are refactoring can_get_size() and
    can_tdc_get_size().

  - Patches #14 to #17 are refactoring can_fill_info() and
    can_tdc_fill_info().

  - Patch #18 makes can_calc_tdco() FD agnostic.

  - Patch #19 adds can_get_ctrlmode_str() which converts control mode
    flags into strings. This is done in preparation of patch #20.

  - Patch #20 is the final patch and improves the user experience by
    providing detailed error messages whenever invalid parameters are
    provided. All those error messages came into handy when debugging
    the upcoming CAN XL patches.

Aside from the last patch, the other changes do not impact any of the
existing functionalities.

The follow up series which introduces CAN XL is nearly completed but
will be sent only once this one is approved: one thing at a time, I do
not want to overwhelm people (including myself).

[1] https://lore.kernel.org/linux-can/20241110155902.72807-16-mailhol.vincent@wanadoo.fr/
[2] https://lore.kernel.org/linux-can/c4771c16-c578-4a6d-baee-918fe276dbe9@wanadoo.fr/
[3] https://lore.kernel.org/linux-can/20241110155902.72807-16-mailhol.vincent@wanadoo.fr/
[4] https://lore.kernel.org/linux-can/20250923-can-fix-mtu-v2-0-984f9868db69@kernel.org/

Link: https://patch.msgid.link/20250923-canxl-netlink-prep-v4-0-e720d28f66fe@kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde
2025-09-24 17:10:01 +02:00
6 changed files with 485 additions and 289 deletions
+6 -4
View File
@@ -173,13 +173,15 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
const struct can_bittiming *dbt,
u32 *ctrlmode, u32 ctrlmode_supported)
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported)
{
if (!tdc_const || !(ctrlmode_supported & CAN_CTRLMODE_TDC_AUTO))
u32 tdc_auto = tdc_mask & CAN_CTRLMODE_TDC_AUTO_MASK;
if (!tdc_const || !(ctrlmode_supported & tdc_auto))
return;
*ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
*ctrlmode &= ~tdc_mask;
/* As specified in ISO 11898-1 section 11.3.3 "Transmitter
* delay compensation" (TDC) is only applicable if data BRP is
@@ -193,6 +195,6 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
if (sample_point_in_tc < tdc_const->tdco_min)
return;
tdc->tdco = min(sample_point_in_tc, tdc_const->tdco_max);
*ctrlmode |= CAN_CTRLMODE_TDC_AUTO;
*ctrlmode |= tdc_auto;
}
}
+33
View File
@@ -88,6 +88,39 @@ const char *can_get_state_str(const enum can_state state)
}
EXPORT_SYMBOL_GPL(can_get_state_str);
const char *can_get_ctrlmode_str(u32 ctrlmode)
{
switch (ctrlmode & ~(ctrlmode - 1)) {
case 0:
return "none";
case CAN_CTRLMODE_LOOPBACK:
return "loopback";
case CAN_CTRLMODE_LISTENONLY:
return "listen-only";
case CAN_CTRLMODE_3_SAMPLES:
return "triple-sampling";
case CAN_CTRLMODE_ONE_SHOT:
return "one-shot";
case CAN_CTRLMODE_BERR_REPORTING:
return "berr-reporting";
case CAN_CTRLMODE_FD:
return "fd";
case CAN_CTRLMODE_PRESUME_ACK:
return "presume-ack";
case CAN_CTRLMODE_FD_NON_ISO:
return "fd-non-iso";
case CAN_CTRLMODE_CC_LEN8_DLC:
return "cc-len8-dlc";
case CAN_CTRLMODE_TDC_AUTO:
return "fd-tdc-auto";
case CAN_CTRLMODE_TDC_MANUAL:
return "fd-tdc-manual";
default:
return "<unknown>";
}
}
EXPORT_SYMBOL_GPL(can_get_ctrlmode_str);
static enum can_state can_state_err_to_state(u16 err)
{
if (err < CAN_ERROR_WARNING_THRESHOLD)
+391 -236
View File
@@ -36,116 +36,245 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
[IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },
};
static int can_validate_bittiming(const struct can_bittiming *bt,
struct netlink_ext_ack *extack)
static int can_validate_bittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_bittiming)
{
struct can_bittiming *bt;
if (!data[ifla_can_bittiming])
return 0;
static_assert(__alignof__(*bt) <= NLA_ALIGNTO);
bt = nla_data(data[ifla_can_bittiming]);
/* sample point is in one-tenth of a percent */
if (bt->sample_point >= 1000) {
NL_SET_ERR_MSG(extack, "sample point must be between 0 and 100%");
return -EINVAL;
}
return 0;
}
static int can_validate_tdc(struct nlattr *data_tdc,
struct netlink_ext_ack *extack, u32 tdc_flags)
{
bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK;
bool tdc_auto = tdc_flags & CAN_CTRLMODE_TDC_AUTO_MASK;
int err;
if (tdc_auto && tdc_manual) {
NL_SET_ERR_MSG(extack,
"TDC manual and auto modes are mutually exclusive");
return -EOPNOTSUPP;
}
/* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
* must be set and vice-versa
*/
if ((tdc_auto || tdc_manual) && !data_tdc) {
NL_SET_ERR_MSG(extack, "TDC parameters are missing");
return -EOPNOTSUPP;
}
if (!(tdc_auto || tdc_manual) && data_tdc) {
NL_SET_ERR_MSG(extack, "TDC mode (auto or manual) is missing");
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 (data_tdc) {
struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX,
data_tdc, can_tdc_policy, extack);
if (err)
return err;
if (tb_tdc[IFLA_CAN_TDC_TDCV]) {
if (tdc_auto) {
NL_SET_ERR_MSG(extack,
"TDCV is incompatible with TDC auto mode");
return -EOPNOTSUPP;
}
} else {
if (tdc_manual) {
NL_SET_ERR_MSG(extack,
"TDC manual mode requires TDCV");
return -EOPNOTSUPP;
}
}
if (!tb_tdc[IFLA_CAN_TDC_TDCO]) {
NL_SET_ERR_MSG(extack, "TDCO is missing");
return -EOPNOTSUPP;
}
}
return 0;
}
static int can_validate_databittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_data_bittiming, u32 flags)
{
struct nlattr *data_tdc;
const char *type;
u32 tdc_flags;
bool is_on;
int err;
/* Make sure that valid CAN FD configurations always consist of
* - nominal/arbitration bittiming
* - data bittiming
* - control mode with CAN_CTRLMODE_FD set
* - TDC parameters are coherent (details in can_validate_tdc())
*/
if (ifla_can_data_bittiming == IFLA_CAN_DATA_BITTIMING) {
data_tdc = data[IFLA_CAN_TDC];
tdc_flags = flags & CAN_CTRLMODE_FD_TDC_MASK;
is_on = flags & CAN_CTRLMODE_FD;
type = "FD";
} else {
return -EOPNOTSUPP; /* Place holder for CAN XL */
}
if (is_on) {
if (!data[IFLA_CAN_BITTIMING] || !data[ifla_can_data_bittiming]) {
NL_SET_ERR_MSG_FMT(extack,
"Provide both nominal and %s data bittiming",
type);
return -EOPNOTSUPP;
}
} else {
if (data[ifla_can_data_bittiming]) {
NL_SET_ERR_MSG_FMT(extack,
"%s data bittiming requires CAN %s",
type, type);
return -EOPNOTSUPP;
}
if (data_tdc) {
NL_SET_ERR_MSG_FMT(extack,
"%s TDC requires CAN %s",
type, type);
return -EOPNOTSUPP;
}
}
err = can_validate_bittiming(data, extack, ifla_can_data_bittiming);
if (err)
return err;
err = can_validate_tdc(data_tdc, extack, tdc_flags);
if (err)
return err;
return 0;
}
static int can_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
bool is_can_fd = false;
u32 flags = 0;
int err;
/* Make sure that valid CAN FD configurations always consist of
* - nominal/arbitration bittiming
* - data bittiming
* - control mode with CAN_CTRLMODE_FD set
* - TDC parameters are coherent (details below)
*/
if (!data)
return 0;
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
u32 tdc_flags = cm->flags & CAN_CTRLMODE_FD_TDC_MASK;
is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
/* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually exclusive */
if (tdc_flags == CAN_CTRLMODE_FD_TDC_MASK)
return -EOPNOTSUPP;
/* If one of the CAN_CTRLMODE_TDC_* flag is set then
* TDC must be set and vice-versa
*/
if (!!tdc_flags != !!data[IFLA_CAN_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 (data[IFLA_CAN_TDC]) {
struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX,
data[IFLA_CAN_TDC],
can_tdc_policy, extack);
if (err)
return err;
if (tb_tdc[IFLA_CAN_TDC_TDCV]) {
if (tdc_flags & CAN_CTRLMODE_TDC_AUTO)
return -EOPNOTSUPP;
} else {
if (tdc_flags & CAN_CTRLMODE_TDC_MANUAL)
return -EOPNOTSUPP;
}
if (!tb_tdc[IFLA_CAN_TDC_TDCO])
return -EOPNOTSUPP;
}
flags = cm->flags & cm->mask;
}
if (data[IFLA_CAN_BITTIMING]) {
struct can_bittiming bt;
err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);
if (err)
return err;
memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
err = can_validate_bittiming(&bt, extack);
if (err)
return err;
}
if (is_can_fd) {
if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
return -EOPNOTSUPP;
}
if (data[IFLA_CAN_DATA_BITTIMING] || data[IFLA_CAN_TDC]) {
if (!is_can_fd)
return -EOPNOTSUPP;
}
if (data[IFLA_CAN_DATA_BITTIMING]) {
struct can_bittiming bt;
memcpy(&bt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), sizeof(bt));
err = can_validate_bittiming(&bt, extack);
if (err)
return err;
}
err = can_validate_databittiming(data, extack,
IFLA_CAN_DATA_BITTIMING, flags);
if (err)
return err;
return 0;
}
static int can_tdc_changelink(struct can_priv *priv, const struct nlattr *nla,
static int can_ctrlmode_changelink(struct net_device *dev,
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct can_priv *priv = netdev_priv(dev);
struct can_ctrlmode *cm;
u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
if (!data[IFLA_CAN_CTRLMODE])
return 0;
/* Do not allow changing controller mode while running */
if (dev->flags & IFF_UP)
return -EBUSY;
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
ctrlstatic = can_get_static_ctrlmode(priv);
maskedflags = cm->flags & cm->mask;
notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;
if (notsupp) {
NL_SET_ERR_MSG_FMT(extack,
"requested control mode %s not supported",
can_get_ctrlmode_str(notsupp));
return -EOPNOTSUPP;
}
/* do not check for static fd-non-iso if 'fd' is disabled */
if (!(maskedflags & CAN_CTRLMODE_FD))
ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
if (ctrlstatic_missing) {
NL_SET_ERR_MSG_FMT(extack,
"missing required %s static control mode",
can_get_ctrlmode_str(ctrlstatic_missing));
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;
/* 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 */
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));
}
can_set_default_mtu(dev);
return 0;
}
static int can_tdc_changelink(struct data_bittiming_params *dbt_params,
const struct nlattr *nla,
struct netlink_ext_ack *extack)
{
struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
struct can_tdc tdc = { 0 };
const struct can_tdc_const *tdc_const = priv->fd.tdc_const;
const struct can_tdc_const *tdc_const = dbt_params->tdc_const;
int err;
if (!tdc_const || !can_fd_tdc_is_enabled(priv))
if (!tdc_const) {
NL_SET_ERR_MSG(extack, "The device does not support TDC");
return -EOPNOTSUPP;
}
err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, nla,
can_tdc_policy, extack);
@@ -179,7 +308,91 @@ static int can_tdc_changelink(struct can_priv *priv, const struct nlattr *nla,
tdc.tdcf = tdcf;
}
priv->fd.tdc = tdc;
dbt_params->tdc = tdc;
return 0;
}
static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
bool fd, struct netlink_ext_ack *extack)
{
struct nlattr *data_bittiming, *data_tdc;
struct can_priv *priv = netdev_priv(dev);
struct data_bittiming_params *dbt_params;
struct can_bittiming dbt;
bool need_tdc_calc = false;
u32 tdc_mask;
int err;
if (fd) {
data_bittiming = data[IFLA_CAN_DATA_BITTIMING];
data_tdc = data[IFLA_CAN_TDC];
dbt_params = &priv->fd;
tdc_mask = CAN_CTRLMODE_FD_TDC_MASK;
} else {
return -EOPNOTSUPP; /* Place holder for CAN XL */
}
if (!data_bittiming)
return 0;
/* Do not allow changing bittiming while running */
if (dev->flags & IFF_UP)
return -EBUSY;
/* Calculate bittiming parameters based on data_bittiming_const
* if set, otherwise pass bitrate directly via do_set_bitrate().
* Bail out if neither is given.
*/
if (!dbt_params->data_bittiming_const && !dbt_params->do_set_data_bittiming &&
!dbt_params->data_bitrate_const)
return -EOPNOTSUPP;
memcpy(&dbt, nla_data(data_bittiming), sizeof(dbt));
err = can_get_bittiming(dev, &dbt, dbt_params->data_bittiming_const,
dbt_params->data_bitrate_const,
dbt_params->data_bitrate_const_cnt, extack);
if (err)
return err;
if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
NL_SET_ERR_MSG_FMT(extack,
"CAN data bitrate %u bps surpasses transceiver capabilities of %u bps",
dbt.bitrate, priv->bitrate_max);
return -EINVAL;
}
memset(&dbt_params->tdc, 0, sizeof(dbt_params->tdc));
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
need_tdc_calc = !(cm->mask & tdc_mask);
}
if (data_tdc) {
/* TDC parameters are provided: use them */
err = can_tdc_changelink(dbt_params, data_tdc, extack);
if (err) {
priv->ctrlmode &= ~tdc_mask;
return err;
}
} else if (need_tdc_calc) {
/* Neither of TDC parameters nor TDC flags are provided:
* do calculation
*/
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
* turned off. TDC is disabled: do nothing
*/
memcpy(&dbt_params->data_bittiming, &dbt, sizeof(dbt));
if (dbt_params->do_set_data_bittiming) {
/* Finally, set the bit-timing registers */
err = dbt_params->do_set_data_bittiming(dev);
if (err)
return err;
}
return 0;
}
@@ -189,57 +402,12 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct can_priv *priv = netdev_priv(dev);
bool fd_tdc_flag_provided = false;
int err;
/* We need synchronization with dev->stop() */
ASSERT_RTNL();
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm;
u32 ctrlstatic;
u32 maskedflags;
/* Do not allow changing controller mode while running */
if (dev->flags & IFF_UP)
return -EBUSY;
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
ctrlstatic = can_get_static_ctrlmode(priv);
maskedflags = cm->flags & cm->mask;
/* check whether provided bits are allowed to be passed */
if (maskedflags & ~(priv->ctrlmode_supported | ctrlstatic))
return -EOPNOTSUPP;
/* do not check for static fd-non-iso if 'fd' is disabled */
if (!(maskedflags & CAN_CTRLMODE_FD))
ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
/* make sure static options are provided by configuration */
if ((maskedflags & ctrlstatic) != ctrlstatic)
return -EOPNOTSUPP;
/* 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 */
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));
}
can_set_default_mtu(dev);
fd_tdc_flag_provided = cm->mask & CAN_CTRLMODE_FD_TDC_MASK;
/* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually
* exclusive: make sure to turn the other one off
*/
if (fd_tdc_flag_provided)
priv->ctrlmode &= cm->flags | ~CAN_CTRLMODE_FD_TDC_MASK;
}
can_ctrlmode_changelink(dev, data, extack);
if (data[IFLA_CAN_BITTIMING]) {
struct can_bittiming bt;
@@ -311,75 +479,21 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
return err;
}
if (data[IFLA_CAN_DATA_BITTIMING]) {
struct can_bittiming dbt;
/* Do not allow changing bittiming while running */
if (dev->flags & IFF_UP)
return -EBUSY;
/* Calculate bittiming parameters based on
* data_bittiming_const if set, otherwise pass bitrate
* directly via do_set_bitrate(). Bail out if neither
* is given.
*/
if (!priv->fd.data_bittiming_const && !priv->fd.do_set_data_bittiming &&
!priv->fd.data_bitrate_const)
return -EOPNOTSUPP;
memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]),
sizeof(dbt));
err = can_get_bittiming(dev, &dbt,
priv->fd.data_bittiming_const,
priv->fd.data_bitrate_const,
priv->fd.data_bitrate_const_cnt,
extack);
if (err)
return err;
if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
NL_SET_ERR_MSG_FMT(extack,
"CANFD data bitrate %u bps surpasses transceiver capabilities of %u bps",
dbt.bitrate, priv->bitrate_max);
return -EINVAL;
}
memset(&priv->fd.tdc, 0, sizeof(priv->fd.tdc));
if (data[IFLA_CAN_TDC]) {
/* TDC parameters are provided: use them */
err = can_tdc_changelink(priv, data[IFLA_CAN_TDC],
extack);
if (err) {
priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
return err;
}
} else if (!fd_tdc_flag_provided) {
/* Neither of TDC parameters nor TDC flags are
* provided: do calculation
*/
can_calc_tdco(&priv->fd.tdc, priv->fd.tdc_const, &dbt,
&priv->ctrlmode, priv->ctrlmode_supported);
} /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly
* turned off. TDC is disabled: do nothing
*/
memcpy(&priv->fd.data_bittiming, &dbt, sizeof(dbt));
if (priv->fd.do_set_data_bittiming) {
/* Finally, set the bit-timing registers */
err = priv->fd.do_set_data_bittiming(dev);
if (err)
return err;
}
}
/* CAN FD */
err = can_dbt_changelink(dev, data, true, 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;
unsigned int i;
if (!priv->do_set_termination)
if (!priv->do_set_termination) {
NL_SET_ERR_MSG(extack,
"Termination is not configurable on this device");
return -EOPNOTSUPP;
}
/* check whether given value is supported by the interface */
for (i = 0; i < num_term; i++) {
@@ -400,38 +514,55 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
return 0;
}
static size_t can_tdc_get_size(const struct net_device *dev)
static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params,
u32 tdc_flags)
{
struct can_priv *priv = netdev_priv(dev);
bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK;
size_t size;
if (!priv->fd.tdc_const)
if (!dbt_params->tdc_const)
return 0;
size = nla_total_size(0); /* nest IFLA_CAN_TDC */
if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL) {
if (tdc_manual) {
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MAX */
}
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MAX */
if (priv->fd.tdc_const->tdcf_max) {
if (dbt_params->tdc_const->tdcf_max) {
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MIN */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MAX */
}
if (can_fd_tdc_is_enabled(priv)) {
if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL ||
priv->fd.do_get_auto_tdcv)
if (tdc_flags) {
if (tdc_manual || dbt_params->do_get_auto_tdcv)
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO */
if (priv->fd.tdc_const->tdcf_max)
if (dbt_params->tdc_const->tdcf_max)
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF */
}
return size;
}
static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_params,
u32 tdc_flags)
{
size_t size = 0;
if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
size += nla_total_size(sizeof(dbt_params->data_bittiming));
if (dbt_params->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
size += nla_total_size(sizeof(*dbt_params->data_bittiming_const));
if (dbt_params->data_bitrate_const) /* IFLA_CAN_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 */
return size;
}
static size_t can_ctrlmode_ext_get_size(void)
{
return nla_total_size(0) + /* nest IFLA_CAN_CTRLMODE_EXT */
@@ -453,10 +584,6 @@ static size_t can_get_size(const struct net_device *dev)
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */
if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */
size += nla_total_size(sizeof(struct can_berr_counter));
if (priv->fd.data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
size += nla_total_size(sizeof(struct can_bittiming));
if (priv->fd.data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
size += nla_total_size(sizeof(struct can_bittiming_const));
if (priv->termination_const) {
size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */
size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */
@@ -465,31 +592,69 @@ static size_t can_get_size(const struct net_device *dev)
if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */
size += nla_total_size(sizeof(*priv->bitrate_const) *
priv->bitrate_const_cnt);
if (priv->fd.data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
size += nla_total_size(sizeof(*priv->fd.data_bitrate_const) *
priv->fd.data_bitrate_const_cnt);
size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */
size += can_tdc_get_size(dev); /* IFLA_CAN_TDC */
size += can_ctrlmode_ext_get_size(); /* IFLA_CAN_CTRLMODE_EXT */
size += can_data_bittiming_get_size(&priv->fd,
priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
return size;
}
static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev)
static int can_bittiming_fill_info(struct sk_buff *skb, int ifla_can_bittiming,
struct can_bittiming *bittiming)
{
return bittiming->bitrate != CAN_BITRATE_UNSET &&
bittiming->bitrate != CAN_BITRATE_UNKNOWN &&
nla_put(skb, ifla_can_bittiming, sizeof(*bittiming), bittiming);
}
static int can_bittiming_const_fill_info(struct sk_buff *skb,
int ifla_can_bittiming_const,
const struct can_bittiming_const *bittiming_const)
{
return bittiming_const &&
nla_put(skb, ifla_can_bittiming_const,
sizeof(*bittiming_const), bittiming_const);
}
static int can_bitrate_const_fill_info(struct sk_buff *skb,
int ifla_can_bitrate_const,
const u32 *bitrate_const, unsigned int cnt)
{
return bitrate_const &&
nla_put(skb, ifla_can_bitrate_const,
sizeof(*bitrate_const) * cnt, bitrate_const);
}
static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
int ifla_can_tdc)
{
struct nlattr *nest;
struct can_priv *priv = netdev_priv(dev);
struct can_tdc *tdc = &priv->fd.tdc;
const struct can_tdc_const *tdc_const = priv->fd.tdc_const;
struct data_bittiming_params *dbt_params;
const struct can_tdc_const *tdc_const;
struct can_tdc *tdc;
struct nlattr *nest;
bool tdc_is_enabled, tdc_manual;
if (ifla_can_tdc == IFLA_CAN_TDC) {
dbt_params = &priv->fd;
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 */
}
tdc_const = dbt_params->tdc_const;
tdc = &dbt_params->tdc;
if (!tdc_const)
return 0;
nest = nla_nest_start(skb, IFLA_CAN_TDC);
nest = nla_nest_start(skb, ifla_can_tdc);
if (!nest)
return -EMSGSIZE;
if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL &&
if (tdc_manual &&
(nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MIN, tdc_const->tdcv_min) ||
nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MAX, tdc_const->tdcv_max)))
goto err_cancel;
@@ -501,15 +666,15 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MAX, tdc_const->tdcf_max)))
goto err_cancel;
if (can_fd_tdc_is_enabled(priv)) {
if (tdc_is_enabled) {
u32 tdcv;
int err = -EINVAL;
if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL) {
if (tdc_manual) {
tdcv = tdc->tdcv;
err = 0;
} else if (priv->fd.do_get_auto_tdcv) {
err = priv->fd.do_get_auto_tdcv(dev, &tdcv);
} else if (dbt_params->do_get_auto_tdcv) {
err = dbt_params->do_get_auto_tdcv(dev, &tdcv);
}
if (!err && nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdcv))
goto err_cancel;
@@ -557,14 +722,11 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (priv->do_get_state)
priv->do_get_state(dev, &state);
if ((priv->bittiming.bitrate != CAN_BITRATE_UNSET &&
priv->bittiming.bitrate != CAN_BITRATE_UNKNOWN &&
nla_put(skb, IFLA_CAN_BITTIMING,
sizeof(priv->bittiming), &priv->bittiming)) ||
if (can_bittiming_fill_info(skb, IFLA_CAN_BITTIMING,
&priv->bittiming) ||
(priv->bittiming_const &&
nla_put(skb, IFLA_CAN_BITTIMING_CONST,
sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
can_bittiming_const_fill_info(skb, IFLA_CAN_BITTIMING_CONST,
priv->bittiming_const) ||
nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
nla_put_u32(skb, IFLA_CAN_STATE, state) ||
@@ -575,14 +737,11 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
!priv->do_get_berr_counter(dev, &bec) &&
nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
(priv->fd.data_bittiming.bitrate &&
nla_put(skb, IFLA_CAN_DATA_BITTIMING,
sizeof(priv->fd.data_bittiming), &priv->fd.data_bittiming)) ||
can_bittiming_fill_info(skb, IFLA_CAN_DATA_BITTIMING,
&priv->fd.data_bittiming) ||
(priv->fd.data_bittiming_const &&
nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
sizeof(*priv->fd.data_bittiming_const),
priv->fd.data_bittiming_const)) ||
can_bittiming_const_fill_info(skb, IFLA_CAN_DATA_BITTIMING_CONST,
priv->fd.data_bittiming_const) ||
(priv->termination_const &&
(nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
@@ -591,23 +750,19 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
priv->termination_const_cnt,
priv->termination_const))) ||
(priv->bitrate_const &&
nla_put(skb, IFLA_CAN_BITRATE_CONST,
sizeof(*priv->bitrate_const) *
priv->bitrate_const_cnt,
priv->bitrate_const)) ||
can_bitrate_const_fill_info(skb, IFLA_CAN_BITRATE_CONST,
priv->bitrate_const,
priv->bitrate_const_cnt) ||
(priv->fd.data_bitrate_const &&
nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
sizeof(*priv->fd.data_bitrate_const) *
priv->fd.data_bitrate_const_cnt,
priv->fd.data_bitrate_const)) ||
can_bitrate_const_fill_info(skb, IFLA_CAN_DATA_BITRATE_CONST,
priv->fd.data_bitrate_const,
priv->fd.data_bitrate_const_cnt) ||
(nla_put(skb, IFLA_CAN_BITRATE_MAX,
sizeof(priv->bitrate_max),
&priv->bitrate_max)) ||
can_tdc_fill_info(skb, dev) ||
can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) ||
can_ctrlmode_ext_fill_info(skb, priv)
)
+46 -2
View File
@@ -16,6 +16,10 @@
#define CAN_CTRLMODE_FD_TDC_MASK \
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_TDC_MANUAL)
#define CAN_CTRLMODE_TDC_AUTO_MASK \
(CAN_CTRLMODE_TDC_AUTO)
#define CAN_CTRLMODE_TDC_MANUAL_MASK \
(CAN_CTRLMODE_TDC_MANUAL)
/*
* struct can_tdc - CAN FD Transmission Delay Compensation parameters
@@ -114,13 +118,24 @@ struct can_tdc_const {
u32 tdcf_max;
};
struct data_bittiming_params {
const struct can_bittiming_const *data_bittiming_const;
struct can_bittiming data_bittiming;
const struct can_tdc_const *tdc_const;
struct can_tdc tdc;
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
int (*do_set_data_bittiming)(struct net_device *dev);
int (*do_get_auto_tdcv)(const struct net_device *dev, u32 *tdcv);
};
#ifdef CONFIG_CAN_CALC_BITTIMING
int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc, struct netlink_ext_ack *extack);
void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
const struct can_bittiming *dbt,
u32 *ctrlmode, u32 ctrlmode_supported);
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported);
#else /* !CONFIG_CAN_CALC_BITTIMING */
static inline int
can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
@@ -133,7 +148,7 @@ can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
static inline void
can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
const struct can_bittiming *dbt,
u32 *ctrlmode, u32 ctrlmode_supported)
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported)
{
}
#endif /* CONFIG_CAN_CALC_BITTIMING */
@@ -149,6 +164,35 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const unsigned int bitrate_const_cnt,
struct netlink_ext_ack *extack);
/*
* can_get_relative_tdco() - TDCO relative to the sample point
*
* struct can_tdc::tdco represents the absolute offset from TDCV. Some
* controllers use instead an offset relative to the Sample Point (SP)
* such that:
*
* SSP = TDCV + absolute TDCO
* = TDCV + SP + relative TDCO
*
* -+----------- one bit ----------+-- TX pin
* |<--- Sample Point --->|
*
* --+----------- one bit ----------+-- RX pin
* |<-------- TDCV -------->|
* |<------------------------>| absolute TDCO
* |<--- Sample Point --->|
* | |<->| relative TDCO
* |<------------- Secondary Sample Point ------------>|
*/
static inline s32 can_get_relative_tdco(const struct data_bittiming_params *dbt_params)
{
const struct can_bittiming *dbt = &dbt_params->data_bittiming;
s32 sample_point_in_tc = (CAN_SYNC_SEG + dbt->prop_seg +
dbt->phase_seg1) * dbt->brp;
return (s32)dbt_params->tdc.tdco - sample_point_in_tc;
}
/*
* can_bit_time() - Duration of one bit
*
+2 -40
View File
@@ -38,17 +38,6 @@ enum can_termination_gpio {
CAN_TERMINATION_GPIO_MAX,
};
struct data_bittiming_params {
const struct can_bittiming_const *data_bittiming_const;
struct can_bittiming data_bittiming;
const struct can_tdc_const *tdc_const;
struct can_tdc tdc;
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
int (*do_set_data_bittiming)(struct net_device *dev);
int (*do_get_auto_tdcv)(const struct net_device *dev, u32 *tdcv);
};
/*
* CAN common private data
*/
@@ -96,35 +85,6 @@ static inline bool can_fd_tdc_is_enabled(const struct can_priv *priv)
return !!(priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
}
/*
* can_get_relative_tdco() - TDCO relative to the sample point
*
* struct can_tdc::tdco represents the absolute offset from TDCV. Some
* controllers use instead an offset relative to the Sample Point (SP)
* such that:
*
* SSP = TDCV + absolute TDCO
* = TDCV + SP + relative TDCO
*
* -+----------- one bit ----------+-- TX pin
* |<--- Sample Point --->|
*
* --+----------- one bit ----------+-- RX pin
* |<-------- TDCV -------->|
* |<------------------------>| absolute TDCO
* |<--- Sample Point --->|
* | |<->| relative TDCO
* |<------------- Secondary Sample Point ------------>|
*/
static inline s32 can_get_relative_tdco(const struct can_priv *priv)
{
const struct can_bittiming *dbt = &priv->fd.data_bittiming;
s32 sample_point_in_tc = (CAN_SYNC_SEG + dbt->prop_seg +
dbt->phase_seg1) * dbt->brp;
return (s32)priv->fd.tdc.tdco - sample_point_in_tc;
}
static inline u32 can_get_static_ctrlmode(struct can_priv *priv)
{
return priv->ctrlmode & ~priv->ctrlmode_supported;
@@ -181,6 +141,8 @@ int can_restart_now(struct net_device *dev);
void can_bus_off(struct net_device *dev);
const char *can_get_state_str(const enum can_state state);
const char *can_get_ctrlmode_str(u32 ctrlmode);
void can_state_get_by_berr_counter(const struct net_device *dev,
const struct can_berr_counter *bec,
enum can_state *tx_state,
+7 -7
View File
@@ -101,8 +101,8 @@ struct can_ctrlmode {
#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */
#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */
#define CAN_CTRLMODE_TDC_AUTO 0x200 /* FD transceiver automatically calculates TDCV */
#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* FD TDCV is manually set up by user */
/*
* CAN device statistics
@@ -129,14 +129,14 @@ enum {
IFLA_CAN_RESTART_MS,
IFLA_CAN_RESTART,
IFLA_CAN_BERR_COUNTER,
IFLA_CAN_DATA_BITTIMING,
IFLA_CAN_DATA_BITTIMING_CONST,
IFLA_CAN_DATA_BITTIMING, /* FD */
IFLA_CAN_DATA_BITTIMING_CONST, /* FD */
IFLA_CAN_TERMINATION,
IFLA_CAN_TERMINATION_CONST,
IFLA_CAN_BITRATE_CONST,
IFLA_CAN_DATA_BITRATE_CONST,
IFLA_CAN_DATA_BITRATE_CONST, /* FD */
IFLA_CAN_BITRATE_MAX,
IFLA_CAN_TDC,
IFLA_CAN_TDC, /* FD */
IFLA_CAN_CTRLMODE_EXT,
/* add new constants above here */
@@ -145,7 +145,7 @@ enum {
};
/*
* CAN FD Transmitter Delay Compensation (TDC)
* CAN FD/XL Transmitter Delay Compensation (TDC)
*
* Please refer to struct can_tdc_const and can_tdc in
* include/linux/can/bittiming.h for further details.