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