1114 lines · 30.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix * Copyright (C) 2006 Andrey Volkov, Varma Electronics * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org> */ #include <linux/can/dev.h>#include <net/rtnetlink.h> static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {	[IFLA_CAN_STATE] = { .type = NLA_U32 },	[IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) },	[IFLA_CAN_RESTART_MS] = { .type = NLA_U32 },	[IFLA_CAN_RESTART] = { .type = NLA_U32 },	[IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) },	[IFLA_CAN_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },	[IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) },	[IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },	[IFLA_CAN_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },	[IFLA_CAN_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },	[IFLA_CAN_TERMINATION] = { .type = NLA_U16 },	[IFLA_CAN_TDC] = { .type = NLA_NESTED },	[IFLA_CAN_CTRLMODE_EXT] = { .type = NLA_NESTED },	[IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },	[IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },	[IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },	[IFLA_CAN_XL_PWM] = { .type = NLA_NESTED },}; static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {	[IFLA_CAN_TDC_TDCV_MIN] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCV_MAX] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCO_MIN] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCO_MAX] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCF_MIN] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCF_MAX] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCV] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCO] = { .type = NLA_U32 },	[IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },}; static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = {	[IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWML] = { .type = NLA_U32 },	[IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 },}; 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_{,XL}_TDC_* flags 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_{,XL}_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_pwm(struct nlattr *data[],			    struct netlink_ext_ack *extack, u32 flags){	struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];	int err; 	if (!data[IFLA_CAN_XL_PWM])		return 0; 	if (!(flags & CAN_CTRLMODE_XL_TMS)) {		NL_SET_ERR_MSG(extack, "PWM requires TMS");		return -EOPNOTSUPP;	} 	err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM],			       can_pwm_policy, extack);	if (err)		return err; 	if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) {		NL_SET_ERR_MSG(extack,			       "Provide either both PWMS and PWML, or none for automatic calculation");		return -EOPNOTSUPP;	} 	if (tb_pwm[IFLA_CAN_PWM_PWMO] &&	    (!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) {		NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML");		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/XL configurations always consist of	 * - nominal/arbitration bittiming	 * - data bittiming	 * - control mode with CAN_CTRLMODE_{FD,XL} 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 {		data_tdc = data[IFLA_CAN_XL_TDC];		tdc_flags = flags & CAN_CTRLMODE_XL_TDC_MASK;		is_on = flags & CAN_CTRLMODE_XL;		type = "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_xl_flags(struct netlink_ext_ack *extack,				 u32 masked_flags, u32 mask){	if (masked_flags & CAN_CTRLMODE_XL) {		if (masked_flags & CAN_CTRLMODE_XL_TMS) {			const u32 tms_conflicts_mask = CAN_CTRLMODE_FD |				CAN_CTRLMODE_XL_TDC_MASK;			u32 tms_conflicts = masked_flags & tms_conflicts_mask; 			if (tms_conflicts) {				NL_SET_ERR_MSG_FMT(extack,						   "TMS and %s are mutually exclusive",						   can_get_ctrlmode_str(tms_conflicts));				return -EOPNOTSUPP;			}		}	} else {		if (mask & CAN_CTRLMODE_XL_TMS) {			NL_SET_ERR_MSG(extack, "TMS requires CAN XL");			return -EOPNOTSUPP;		}	} 	return 0;} static int can_validate(struct nlattr *tb[], struct nlattr *data[],			struct netlink_ext_ack *extack){	u32 flags = 0;	int err; 	if (!data)		return 0; 	if (data[IFLA_CAN_CTRLMODE]) {		struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); 		flags = cm->flags & cm->mask; 		if ((flags & CAN_CTRLMODE_LISTENONLY) &&		    (flags & CAN_CTRLMODE_RESTRICTED)) {			NL_SET_ERR_MSG(extack,				       "LISTEN-ONLY and RESTRICTED modes are mutually exclusive");			return -EOPNOTSUPP;		} 		err = can_validate_xl_flags(extack, flags, cm->mask);		if (err)			return err;	} 	err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);	if (err)		return err; 	err = can_validate_databittiming(data, extack,					 IFLA_CAN_DATA_BITTIMING, flags);	if (err)		return err; 	err = can_validate_databittiming(data, extack,					 IFLA_CAN_XL_DATA_BITTIMING, flags);	if (err)		return err; 	err = can_validate_pwm(data, extack, flags);	if (err)		return err; 	return 0;} 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, deactivated, 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;	deactivated = ~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 FD was active and is not turned off, check for XL conflicts */	if (priv->ctrlmode & CAN_CTRLMODE_FD & ~deactivated) {		if (maskedflags & CAN_CTRLMODE_XL_TMS) {			NL_SET_ERR_MSG(extack,				       "TMS can not be activated while CAN FD is on");			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;	if (cm->mask & CAN_CTRLMODE_XL)		priv->ctrlmode &= ~(CAN_CTRLMODE_XL_TDC_MASK |				    CAN_CTRLMODE_XL_TMS); 	/* clear bits to be modified and copy the flag values */	priv->ctrlmode &= ~cm->mask;	priv->ctrlmode |= maskedflags; 	/* Wipe potential leftovers from previous CAN FD/XL 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));	}	if (!(priv->ctrlmode & CAN_CTRLMODE_XL)) {		memset(&priv->xl.data_bittiming, 0,		       sizeof(priv->fd.data_bittiming));		priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;		memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));		memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm));	} 	can_set_default_mtu(dev);	can_set_cap_info(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 = dbt_params->tdc_const;	int err; 	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);	if (err)		return err; 	if (tb_tdc[IFLA_CAN_TDC_TDCV]) {		u32 tdcv = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCV]); 		if (tdcv < tdc_const->tdcv_min || tdcv > tdc_const->tdcv_max)			return -EINVAL; 		tdc.tdcv = tdcv;	} 	if (tb_tdc[IFLA_CAN_TDC_TDCO]) {		u32 tdco = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCO]); 		if (tdco < tdc_const->tdco_min || tdco > tdc_const->tdco_max)			return -EINVAL; 		tdc.tdco = tdco;	} 	if (tb_tdc[IFLA_CAN_TDC_TDCF]) {		u32 tdcf = nla_get_u32(tb_tdc[IFLA_CAN_TDC_TDCF]); 		if (tdcf < tdc_const->tdcf_min || tdcf > tdc_const->tdcf_max)			return -EINVAL; 		tdc.tdcf = tdcf;	} 	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 {		data_bittiming = data[IFLA_CAN_XL_DATA_BITTIMING];		data_tdc = data[IFLA_CAN_XL_TDC];		dbt_params = &priv->xl;		tdc_mask = CAN_CTRLMODE_XL_TDC_MASK;	} 	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]); 		if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))			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;

Lines 1–500 of 1114.