162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix 362306a36Sopenharmony_ci * Copyright (C) 2006 Andrey Volkov, Varma Electronics 462306a36Sopenharmony_ci * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> 562306a36Sopenharmony_ci * Copyright (C) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/can/dev.h> 962306a36Sopenharmony_ci#include <net/rtnetlink.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { 1262306a36Sopenharmony_ci [IFLA_CAN_STATE] = { .type = NLA_U32 }, 1362306a36Sopenharmony_ci [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) }, 1462306a36Sopenharmony_ci [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, 1562306a36Sopenharmony_ci [IFLA_CAN_RESTART] = { .type = NLA_U32 }, 1662306a36Sopenharmony_ci [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) }, 1762306a36Sopenharmony_ci [IFLA_CAN_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) }, 1862306a36Sopenharmony_ci [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) }, 1962306a36Sopenharmony_ci [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) }, 2062306a36Sopenharmony_ci [IFLA_CAN_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) }, 2162306a36Sopenharmony_ci [IFLA_CAN_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) }, 2262306a36Sopenharmony_ci [IFLA_CAN_TERMINATION] = { .type = NLA_U16 }, 2362306a36Sopenharmony_ci [IFLA_CAN_TDC] = { .type = NLA_NESTED }, 2462306a36Sopenharmony_ci [IFLA_CAN_CTRLMODE_EXT] = { .type = NLA_NESTED }, 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = { 2862306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCV_MIN] = { .type = NLA_U32 }, 2962306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCV_MAX] = { .type = NLA_U32 }, 3062306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCO_MIN] = { .type = NLA_U32 }, 3162306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCO_MAX] = { .type = NLA_U32 }, 3262306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCF_MIN] = { .type = NLA_U32 }, 3362306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCF_MAX] = { .type = NLA_U32 }, 3462306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCV] = { .type = NLA_U32 }, 3562306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCO] = { .type = NLA_U32 }, 3662306a36Sopenharmony_ci [IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 }, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int can_validate_bittiming(const struct can_bittiming *bt, 4062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci /* sample point is in one-tenth of a percent */ 4362306a36Sopenharmony_ci if (bt->sample_point >= 1000) { 4462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "sample point must be between 0 and 100%"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int can_validate(struct nlattr *tb[], struct nlattr *data[], 5362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci bool is_can_fd = false; 5662306a36Sopenharmony_ci int err; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Make sure that valid CAN FD configurations always consist of 5962306a36Sopenharmony_ci * - nominal/arbitration bittiming 6062306a36Sopenharmony_ci * - data bittiming 6162306a36Sopenharmony_ci * - control mode with CAN_CTRLMODE_FD set 6262306a36Sopenharmony_ci * - TDC parameters are coherent (details below) 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!data) 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (data[IFLA_CAN_BITTIMING]) { 6962306a36Sopenharmony_ci struct can_bittiming bt; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); 7262306a36Sopenharmony_ci err = can_validate_bittiming(&bt, extack); 7362306a36Sopenharmony_ci if (err) 7462306a36Sopenharmony_ci return err; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (data[IFLA_CAN_CTRLMODE]) { 7862306a36Sopenharmony_ci struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); 7962306a36Sopenharmony_ci u32 tdc_flags = cm->flags & CAN_CTRLMODE_TDC_MASK; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually exclusive */ 8462306a36Sopenharmony_ci if (tdc_flags == CAN_CTRLMODE_TDC_MASK) 8562306a36Sopenharmony_ci return -EOPNOTSUPP; 8662306a36Sopenharmony_ci /* If one of the CAN_CTRLMODE_TDC_* flag is set then 8762306a36Sopenharmony_ci * TDC must be set and vice-versa 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (!!tdc_flags != !!data[IFLA_CAN_TDC]) 9062306a36Sopenharmony_ci return -EOPNOTSUPP; 9162306a36Sopenharmony_ci /* If providing TDC parameters, at least TDCO is 9262306a36Sopenharmony_ci * needed. TDCV is needed if and only if 9362306a36Sopenharmony_ci * CAN_CTRLMODE_TDC_MANUAL is set 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (data[IFLA_CAN_TDC]) { 9662306a36Sopenharmony_ci struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1]; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, 9962306a36Sopenharmony_ci data[IFLA_CAN_TDC], 10062306a36Sopenharmony_ci can_tdc_policy, extack); 10162306a36Sopenharmony_ci if (err) 10262306a36Sopenharmony_ci return err; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (tb_tdc[IFLA_CAN_TDC_TDCV]) { 10562306a36Sopenharmony_ci if (tdc_flags & CAN_CTRLMODE_TDC_AUTO) 10662306a36Sopenharmony_ci return -EOPNOTSUPP; 10762306a36Sopenharmony_ci } else { 10862306a36Sopenharmony_ci if (tdc_flags & CAN_CTRLMODE_TDC_MANUAL) 10962306a36Sopenharmony_ci return -EOPNOTSUPP; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!tb_tdc[IFLA_CAN_TDC_TDCO]) 11362306a36Sopenharmony_ci return -EOPNOTSUPP; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (is_can_fd) { 11862306a36Sopenharmony_ci if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) 11962306a36Sopenharmony_ci return -EOPNOTSUPP; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (data[IFLA_CAN_DATA_BITTIMING] || data[IFLA_CAN_TDC]) { 12362306a36Sopenharmony_ci if (!is_can_fd) 12462306a36Sopenharmony_ci return -EOPNOTSUPP; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (data[IFLA_CAN_DATA_BITTIMING]) { 12862306a36Sopenharmony_ci struct can_bittiming bt; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci memcpy(&bt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), sizeof(bt)); 13162306a36Sopenharmony_ci err = can_validate_bittiming(&bt, extack); 13262306a36Sopenharmony_ci if (err) 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int can_tdc_changelink(struct can_priv *priv, const struct nlattr *nla, 14062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1]; 14362306a36Sopenharmony_ci struct can_tdc tdc = { 0 }; 14462306a36Sopenharmony_ci const struct can_tdc_const *tdc_const = priv->tdc_const; 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!tdc_const || !can_tdc_is_enabled(priv)) 14862306a36Sopenharmony_ci return -EOPNOTSUPP; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, nla, 15162306a36Sopenharmony_ci can_tdc_policy, extack); 15262306a36Sopenharmony_ci if (err) 15362306a36Sopenharmony_ci return err; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (tb_tdc[IFLA_CAN_TDC_TDCV]) { 15662306a36Sopenharmony_ci u32 tdcv = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCV]); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (tdcv < tdc_const->tdcv_min || tdcv > tdc_const->tdcv_max) 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci tdc.tdcv = tdcv; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (tb_tdc[IFLA_CAN_TDC_TDCO]) { 16562306a36Sopenharmony_ci u32 tdco = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCO]); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (tdco < tdc_const->tdco_min || tdco > tdc_const->tdco_max) 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci tdc.tdco = tdco; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (tb_tdc[IFLA_CAN_TDC_TDCF]) { 17462306a36Sopenharmony_ci u32 tdcf = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCF]); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (tdcf < tdc_const->tdcf_min || tdcf > tdc_const->tdcf_max) 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci tdc.tdcf = tdcf; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci priv->tdc = tdc; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int can_changelink(struct net_device *dev, struct nlattr *tb[], 18862306a36Sopenharmony_ci struct nlattr *data[], 18962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 19262306a36Sopenharmony_ci u32 tdc_mask = 0; 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* We need synchronization with dev->stop() */ 19662306a36Sopenharmony_ci ASSERT_RTNL(); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (data[IFLA_CAN_BITTIMING]) { 19962306a36Sopenharmony_ci struct can_bittiming bt; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Do not allow changing bittiming while running */ 20262306a36Sopenharmony_ci if (dev->flags & IFF_UP) 20362306a36Sopenharmony_ci return -EBUSY; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Calculate bittiming parameters based on 20662306a36Sopenharmony_ci * bittiming_const if set, otherwise pass bitrate 20762306a36Sopenharmony_ci * directly via do_set_bitrate(). Bail out if neither 20862306a36Sopenharmony_ci * is given. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (!priv->bittiming_const && !priv->do_set_bittiming && 21162306a36Sopenharmony_ci !priv->bitrate_const) 21262306a36Sopenharmony_ci return -EOPNOTSUPP; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); 21562306a36Sopenharmony_ci err = can_get_bittiming(dev, &bt, 21662306a36Sopenharmony_ci priv->bittiming_const, 21762306a36Sopenharmony_ci priv->bitrate_const, 21862306a36Sopenharmony_ci priv->bitrate_const_cnt, 21962306a36Sopenharmony_ci extack); 22062306a36Sopenharmony_ci if (err) 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) { 22462306a36Sopenharmony_ci NL_SET_ERR_MSG_FMT(extack, 22562306a36Sopenharmony_ci "arbitration bitrate %u bps surpasses transceiver capabilities of %u bps", 22662306a36Sopenharmony_ci bt.bitrate, priv->bitrate_max); 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci memcpy(&priv->bittiming, &bt, sizeof(bt)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (priv->do_set_bittiming) { 23362306a36Sopenharmony_ci /* Finally, set the bit-timing registers */ 23462306a36Sopenharmony_ci err = priv->do_set_bittiming(dev); 23562306a36Sopenharmony_ci if (err) 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (data[IFLA_CAN_CTRLMODE]) { 24162306a36Sopenharmony_ci struct can_ctrlmode *cm; 24262306a36Sopenharmony_ci u32 ctrlstatic; 24362306a36Sopenharmony_ci u32 maskedflags; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Do not allow changing controller mode while running */ 24662306a36Sopenharmony_ci if (dev->flags & IFF_UP) 24762306a36Sopenharmony_ci return -EBUSY; 24862306a36Sopenharmony_ci cm = nla_data(data[IFLA_CAN_CTRLMODE]); 24962306a36Sopenharmony_ci ctrlstatic = can_get_static_ctrlmode(priv); 25062306a36Sopenharmony_ci maskedflags = cm->flags & cm->mask; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* check whether provided bits are allowed to be passed */ 25362306a36Sopenharmony_ci if (maskedflags & ~(priv->ctrlmode_supported | ctrlstatic)) 25462306a36Sopenharmony_ci return -EOPNOTSUPP; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* do not check for static fd-non-iso if 'fd' is disabled */ 25762306a36Sopenharmony_ci if (!(maskedflags & CAN_CTRLMODE_FD)) 25862306a36Sopenharmony_ci ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* make sure static options are provided by configuration */ 26162306a36Sopenharmony_ci if ((maskedflags & ctrlstatic) != ctrlstatic) 26262306a36Sopenharmony_ci return -EOPNOTSUPP; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* clear bits to be modified and copy the flag values */ 26562306a36Sopenharmony_ci priv->ctrlmode &= ~cm->mask; 26662306a36Sopenharmony_ci priv->ctrlmode |= maskedflags; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* CAN_CTRLMODE_FD can only be set when driver supports FD */ 26962306a36Sopenharmony_ci if (priv->ctrlmode & CAN_CTRLMODE_FD) { 27062306a36Sopenharmony_ci dev->mtu = CANFD_MTU; 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci dev->mtu = CAN_MTU; 27362306a36Sopenharmony_ci memset(&priv->data_bittiming, 0, 27462306a36Sopenharmony_ci sizeof(priv->data_bittiming)); 27562306a36Sopenharmony_ci priv->ctrlmode &= ~CAN_CTRLMODE_TDC_MASK; 27662306a36Sopenharmony_ci memset(&priv->tdc, 0, sizeof(priv->tdc)); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci tdc_mask = cm->mask & CAN_CTRLMODE_TDC_MASK; 28062306a36Sopenharmony_ci /* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually 28162306a36Sopenharmony_ci * exclusive: make sure to turn the other one off 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (tdc_mask) 28462306a36Sopenharmony_ci priv->ctrlmode &= cm->flags | ~CAN_CTRLMODE_TDC_MASK; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (data[IFLA_CAN_RESTART_MS]) { 28862306a36Sopenharmony_ci /* Do not allow changing restart delay while running */ 28962306a36Sopenharmony_ci if (dev->flags & IFF_UP) 29062306a36Sopenharmony_ci return -EBUSY; 29162306a36Sopenharmony_ci priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (data[IFLA_CAN_RESTART]) { 29562306a36Sopenharmony_ci /* Do not allow a restart while not running */ 29662306a36Sopenharmony_ci if (!(dev->flags & IFF_UP)) 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci err = can_restart_now(dev); 29962306a36Sopenharmony_ci if (err) 30062306a36Sopenharmony_ci return err; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (data[IFLA_CAN_DATA_BITTIMING]) { 30462306a36Sopenharmony_ci struct can_bittiming dbt; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Do not allow changing bittiming while running */ 30762306a36Sopenharmony_ci if (dev->flags & IFF_UP) 30862306a36Sopenharmony_ci return -EBUSY; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Calculate bittiming parameters based on 31162306a36Sopenharmony_ci * data_bittiming_const if set, otherwise pass bitrate 31262306a36Sopenharmony_ci * directly via do_set_bitrate(). Bail out if neither 31362306a36Sopenharmony_ci * is given. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (!priv->data_bittiming_const && !priv->do_set_data_bittiming && 31662306a36Sopenharmony_ci !priv->data_bitrate_const) 31762306a36Sopenharmony_ci return -EOPNOTSUPP; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), 32062306a36Sopenharmony_ci sizeof(dbt)); 32162306a36Sopenharmony_ci err = can_get_bittiming(dev, &dbt, 32262306a36Sopenharmony_ci priv->data_bittiming_const, 32362306a36Sopenharmony_ci priv->data_bitrate_const, 32462306a36Sopenharmony_ci priv->data_bitrate_const_cnt, 32562306a36Sopenharmony_ci extack); 32662306a36Sopenharmony_ci if (err) 32762306a36Sopenharmony_ci return err; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) { 33062306a36Sopenharmony_ci NL_SET_ERR_MSG_FMT(extack, 33162306a36Sopenharmony_ci "CANFD data bitrate %u bps surpasses transceiver capabilities of %u bps", 33262306a36Sopenharmony_ci dbt.bitrate, priv->bitrate_max); 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci memset(&priv->tdc, 0, sizeof(priv->tdc)); 33762306a36Sopenharmony_ci if (data[IFLA_CAN_TDC]) { 33862306a36Sopenharmony_ci /* TDC parameters are provided: use them */ 33962306a36Sopenharmony_ci err = can_tdc_changelink(priv, data[IFLA_CAN_TDC], 34062306a36Sopenharmony_ci extack); 34162306a36Sopenharmony_ci if (err) { 34262306a36Sopenharmony_ci priv->ctrlmode &= ~CAN_CTRLMODE_TDC_MASK; 34362306a36Sopenharmony_ci return err; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } else if (!tdc_mask) { 34662306a36Sopenharmony_ci /* Neither of TDC parameters nor TDC flags are 34762306a36Sopenharmony_ci * provided: do calculation 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci can_calc_tdco(&priv->tdc, priv->tdc_const, &dbt, 35062306a36Sopenharmony_ci &priv->ctrlmode, priv->ctrlmode_supported); 35162306a36Sopenharmony_ci } /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly 35262306a36Sopenharmony_ci * turned off. TDC is disabled: do nothing 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (priv->do_set_data_bittiming) { 35862306a36Sopenharmony_ci /* Finally, set the bit-timing registers */ 35962306a36Sopenharmony_ci err = priv->do_set_data_bittiming(dev); 36062306a36Sopenharmony_ci if (err) 36162306a36Sopenharmony_ci return err; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (data[IFLA_CAN_TERMINATION]) { 36662306a36Sopenharmony_ci const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]); 36762306a36Sopenharmony_ci const unsigned int num_term = priv->termination_const_cnt; 36862306a36Sopenharmony_ci unsigned int i; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!priv->do_set_termination) 37162306a36Sopenharmony_ci return -EOPNOTSUPP; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* check whether given value is supported by the interface */ 37462306a36Sopenharmony_ci for (i = 0; i < num_term; i++) { 37562306a36Sopenharmony_ci if (termval == priv->termination_const[i]) 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (i >= num_term) 37962306a36Sopenharmony_ci return -EINVAL; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Finally, set the termination value */ 38262306a36Sopenharmony_ci err = priv->do_set_termination(dev, termval); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci return err; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci priv->termination = termval; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic size_t can_tdc_get_size(const struct net_device *dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 39562306a36Sopenharmony_ci size_t size; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!priv->tdc_const) 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci size = nla_total_size(0); /* nest IFLA_CAN_TDC */ 40162306a36Sopenharmony_ci if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL) { 40262306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MIN */ 40362306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV_MAX */ 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MIN */ 40662306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO_MAX */ 40762306a36Sopenharmony_ci if (priv->tdc_const->tdcf_max) { 40862306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MIN */ 40962306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF_MAX */ 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (can_tdc_is_enabled(priv)) { 41362306a36Sopenharmony_ci if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL || 41462306a36Sopenharmony_ci priv->do_get_auto_tdcv) 41562306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV */ 41662306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO */ 41762306a36Sopenharmony_ci if (priv->tdc_const->tdcf_max) 41862306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCF */ 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return size; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic size_t can_ctrlmode_ext_get_size(void) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return nla_total_size(0) + /* nest IFLA_CAN_CTRLMODE_EXT */ 42762306a36Sopenharmony_ci nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */ 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic size_t can_get_size(const struct net_device *dev) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 43362306a36Sopenharmony_ci size_t size = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */ 43662306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_bittiming)); 43762306a36Sopenharmony_ci if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ 43862306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_bittiming_const)); 43962306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ 44062306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ 44162306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ 44262306a36Sopenharmony_ci size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ 44362306a36Sopenharmony_ci if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ 44462306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_berr_counter)); 44562306a36Sopenharmony_ci if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */ 44662306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_bittiming)); 44762306a36Sopenharmony_ci if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */ 44862306a36Sopenharmony_ci size += nla_total_size(sizeof(struct can_bittiming_const)); 44962306a36Sopenharmony_ci if (priv->termination_const) { 45062306a36Sopenharmony_ci size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */ 45162306a36Sopenharmony_ci size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */ 45262306a36Sopenharmony_ci priv->termination_const_cnt); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */ 45562306a36Sopenharmony_ci size += nla_total_size(sizeof(*priv->bitrate_const) * 45662306a36Sopenharmony_ci priv->bitrate_const_cnt); 45762306a36Sopenharmony_ci if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */ 45862306a36Sopenharmony_ci size += nla_total_size(sizeof(*priv->data_bitrate_const) * 45962306a36Sopenharmony_ci priv->data_bitrate_const_cnt); 46062306a36Sopenharmony_ci size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ 46162306a36Sopenharmony_ci size += can_tdc_get_size(dev); /* IFLA_CAN_TDC */ 46262306a36Sopenharmony_ci size += can_ctrlmode_ext_get_size(); /* IFLA_CAN_CTRLMODE_EXT */ 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return size; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct nlattr *nest; 47062306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 47162306a36Sopenharmony_ci struct can_tdc *tdc = &priv->tdc; 47262306a36Sopenharmony_ci const struct can_tdc_const *tdc_const = priv->tdc_const; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!tdc_const) 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci nest = nla_nest_start(skb, IFLA_CAN_TDC); 47862306a36Sopenharmony_ci if (!nest) 47962306a36Sopenharmony_ci return -EMSGSIZE; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (priv->ctrlmode_supported & CAN_CTRLMODE_TDC_MANUAL && 48262306a36Sopenharmony_ci (nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MIN, tdc_const->tdcv_min) || 48362306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_TDC_TDCV_MAX, tdc_const->tdcv_max))) 48462306a36Sopenharmony_ci goto err_cancel; 48562306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MIN, tdc_const->tdco_min) || 48662306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_TDC_TDCO_MAX, tdc_const->tdco_max)) 48762306a36Sopenharmony_ci goto err_cancel; 48862306a36Sopenharmony_ci if (tdc_const->tdcf_max && 48962306a36Sopenharmony_ci (nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MIN, tdc_const->tdcf_min) || 49062306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_TDC_TDCF_MAX, tdc_const->tdcf_max))) 49162306a36Sopenharmony_ci goto err_cancel; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (can_tdc_is_enabled(priv)) { 49462306a36Sopenharmony_ci u32 tdcv; 49562306a36Sopenharmony_ci int err = -EINVAL; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL) { 49862306a36Sopenharmony_ci tdcv = tdc->tdcv; 49962306a36Sopenharmony_ci err = 0; 50062306a36Sopenharmony_ci } else if (priv->do_get_auto_tdcv) { 50162306a36Sopenharmony_ci err = priv->do_get_auto_tdcv(dev, &tdcv); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci if (!err && nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdcv)) 50462306a36Sopenharmony_ci goto err_cancel; 50562306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_CAN_TDC_TDCO, tdc->tdco)) 50662306a36Sopenharmony_ci goto err_cancel; 50762306a36Sopenharmony_ci if (tdc_const->tdcf_max && 50862306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_TDC_TDCF, tdc->tdcf)) 50962306a36Sopenharmony_ci goto err_cancel; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci nla_nest_end(skb, nest); 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cierr_cancel: 51662306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 51762306a36Sopenharmony_ci return -EMSGSIZE; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int can_ctrlmode_ext_fill_info(struct sk_buff *skb, 52162306a36Sopenharmony_ci const struct can_priv *priv) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct nlattr *nest; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci nest = nla_nest_start(skb, IFLA_CAN_CTRLMODE_EXT); 52662306a36Sopenharmony_ci if (!nest) 52762306a36Sopenharmony_ci return -EMSGSIZE; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_CAN_CTRLMODE_SUPPORTED, 53062306a36Sopenharmony_ci priv->ctrlmode_supported)) { 53162306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 53262306a36Sopenharmony_ci return -EMSGSIZE; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci nla_nest_end(skb, nest); 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int can_fill_info(struct sk_buff *skb, const struct net_device *dev) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 54262306a36Sopenharmony_ci struct can_ctrlmode cm = {.flags = priv->ctrlmode}; 54362306a36Sopenharmony_ci struct can_berr_counter bec = { }; 54462306a36Sopenharmony_ci enum can_state state = priv->state; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (priv->do_get_state) 54762306a36Sopenharmony_ci priv->do_get_state(dev, &state); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if ((priv->bittiming.bitrate != CAN_BITRATE_UNSET && 55062306a36Sopenharmony_ci priv->bittiming.bitrate != CAN_BITRATE_UNKNOWN && 55162306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_BITTIMING, 55262306a36Sopenharmony_ci sizeof(priv->bittiming), &priv->bittiming)) || 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci (priv->bittiming_const && 55562306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_BITTIMING_CONST, 55662306a36Sopenharmony_ci sizeof(*priv->bittiming_const), priv->bittiming_const)) || 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) || 55962306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_STATE, state) || 56062306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || 56162306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci (priv->do_get_berr_counter && 56462306a36Sopenharmony_ci !priv->do_get_berr_counter(dev, &bec) && 56562306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci (priv->data_bittiming.bitrate && 56862306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_DATA_BITTIMING, 56962306a36Sopenharmony_ci sizeof(priv->data_bittiming), &priv->data_bittiming)) || 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci (priv->data_bittiming_const && 57262306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST, 57362306a36Sopenharmony_ci sizeof(*priv->data_bittiming_const), 57462306a36Sopenharmony_ci priv->data_bittiming_const)) || 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci (priv->termination_const && 57762306a36Sopenharmony_ci (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) || 57862306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_TERMINATION_CONST, 57962306a36Sopenharmony_ci sizeof(*priv->termination_const) * 58062306a36Sopenharmony_ci priv->termination_const_cnt, 58162306a36Sopenharmony_ci priv->termination_const))) || 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci (priv->bitrate_const && 58462306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_BITRATE_CONST, 58562306a36Sopenharmony_ci sizeof(*priv->bitrate_const) * 58662306a36Sopenharmony_ci priv->bitrate_const_cnt, 58762306a36Sopenharmony_ci priv->bitrate_const)) || 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci (priv->data_bitrate_const && 59062306a36Sopenharmony_ci nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST, 59162306a36Sopenharmony_ci sizeof(*priv->data_bitrate_const) * 59262306a36Sopenharmony_ci priv->data_bitrate_const_cnt, 59362306a36Sopenharmony_ci priv->data_bitrate_const)) || 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci (nla_put(skb, IFLA_CAN_BITRATE_MAX, 59662306a36Sopenharmony_ci sizeof(priv->bitrate_max), 59762306a36Sopenharmony_ci &priv->bitrate_max)) || 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci can_tdc_fill_info(skb, dev) || 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci can_ctrlmode_ext_fill_info(skb, priv) 60262306a36Sopenharmony_ci ) 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return -EMSGSIZE; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic size_t can_get_xstats_size(const struct net_device *dev) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci return sizeof(struct can_device_stats); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct can_priv *priv = netdev_priv(dev); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (nla_put(skb, IFLA_INFO_XSTATS, 61962306a36Sopenharmony_ci sizeof(priv->can_stats), &priv->can_stats)) 62062306a36Sopenharmony_ci goto nla_put_failure; 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cinla_put_failure: 62462306a36Sopenharmony_ci return -EMSGSIZE; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int can_newlink(struct net *src_net, struct net_device *dev, 62862306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 62962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci return -EOPNOTSUPP; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic void can_dellink(struct net_device *dev, struct list_head *head) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistruct rtnl_link_ops can_link_ops __read_mostly = { 63962306a36Sopenharmony_ci .kind = "can", 64062306a36Sopenharmony_ci .netns_refund = true, 64162306a36Sopenharmony_ci .maxtype = IFLA_CAN_MAX, 64262306a36Sopenharmony_ci .policy = can_policy, 64362306a36Sopenharmony_ci .setup = can_setup, 64462306a36Sopenharmony_ci .validate = can_validate, 64562306a36Sopenharmony_ci .newlink = can_newlink, 64662306a36Sopenharmony_ci .changelink = can_changelink, 64762306a36Sopenharmony_ci .dellink = can_dellink, 64862306a36Sopenharmony_ci .get_size = can_get_size, 64962306a36Sopenharmony_ci .fill_info = can_fill_info, 65062306a36Sopenharmony_ci .get_xstats_size = can_get_xstats_size, 65162306a36Sopenharmony_ci .fill_xstats = can_fill_xstats, 65262306a36Sopenharmony_ci}; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ciint can_netlink_register(void) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci return rtnl_link_register(&can_link_ops); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_civoid can_netlink_unregister(void) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci rtnl_link_unregister(&can_link_ops); 66262306a36Sopenharmony_ci} 663