mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'linux-can-next-for-6.19-20251126' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next
Marc Kleine-Budde says:
====================
pull-request: can-next 2025-11-26
this is a pull request of 27 patches for net-next/main.
The first 17 patches are by Vincent Mailhol and Oliver Hartkopp and
add CAN XL support to the CAN netlink interface.
Geert Uytterhoeven and Biju Das provide 7 patches for the rcar_canfd
driver to add suspend/resume support.
The next 2 patches are by Markus Schneider-Pargmann and add them as
the m_can maintainer.
Conor Dooley's patch updates the mpfs-can DT bindungs.
linux-can-next-for-6.19-20251126
* tag 'linux-can-next-for-6.19-20251126' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next: (27 commits)
dt-bindings: can: mpfs: document resets
MAINTAINERS: Simplify m_can section
MAINTAINERS: Add myself as m_can maintainer
can: rcar_canfd: Add suspend/resume support
can: rcar_canfd: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
can: rcar_canfd: Invert CAN clock and close_candev() order
can: rcar_canfd: Extract rcar_canfd_global_{,de}init()
can: rcar_canfd: Use devm_clk_get_optional() for RAM clk
can: rcar_canfd: Invert global vs. channel teardown
can: rcar_canfd: Invert reset assert order
can: dev: print bitrate error with two decimal digits
can: raw: instantly reject unsupported CAN frames
can: add dummy_can driver
can: calc_bittiming: add can_calc_sample_point_pwm()
can: calc_bittiming: add can_calc_sample_point_nrz()
can: calc_bittiming: replace misleading "nominal" by "reference"
can: netlink: add PWM netlink interface
can: calc_bittiming: add PWM calculation
can: bittiming: add PWM validation
can: bittiming: add PWM parameters
...
====================
Link: https://patch.msgid.link/20251126120106.154635-1-mkl@pengutronix.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -32,11 +32,15 @@ properties:
|
||||
- description: AHB peripheral clock
|
||||
- description: CAN bus clock
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@@ -46,6 +50,7 @@ examples:
|
||||
compatible = "microchip,mpfs-can";
|
||||
reg = <0x2010c000 0x1000>;
|
||||
clocks = <&clkcfg 17>, <&clkcfg 37>;
|
||||
resets = <&clkcfg 17>;
|
||||
interrupt-parent = <&plic>;
|
||||
interrupts = <56>;
|
||||
};
|
||||
|
||||
@@ -15412,14 +15412,12 @@ S: Supported
|
||||
F: drivers/net/phy/mxl-86110.c
|
||||
F: drivers/net/phy/mxl-gpy.c
|
||||
|
||||
MCAN MMIO DEVICE DRIVER
|
||||
M: Chandrasekar Ramakrishnan <rcsekar@samsung.com>
|
||||
MCAN DEVICE DRIVER
|
||||
M: Markus Schneider-Pargmann <msp@baylibre.com>
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
|
||||
F: drivers/net/can/m_can/m_can.c
|
||||
F: drivers/net/can/m_can/m_can.h
|
||||
F: drivers/net/can/m_can/m_can_platform.c
|
||||
F: drivers/net/can/m_can/
|
||||
|
||||
MCBA MICROCHIP CAN BUS ANALYZER TOOL DRIVER
|
||||
R: Yasushi SHOJI <yashi@spacecubics.com>
|
||||
|
||||
@@ -124,6 +124,23 @@ config CAN_CAN327
|
||||
|
||||
If this driver is built as a module, it will be called can327.
|
||||
|
||||
config CAN_DUMMY
|
||||
tristate "Dummy CAN"
|
||||
help
|
||||
A dummy CAN module supporting Classical CAN, CAN FD and CAN XL. It
|
||||
exposes bittiming values which can be configured through the netlink
|
||||
interface.
|
||||
|
||||
The module will simply echo any frame sent to it. If debug messages
|
||||
are activated, it prints all the CAN bittiming information in the
|
||||
kernel log. Aside from that it does nothing.
|
||||
|
||||
This is convenient for testing the CAN netlink interface. Most of the
|
||||
users will never need this. If unsure, say NO.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called dummy-can.
|
||||
|
||||
config CAN_FLEXCAN
|
||||
tristate "Support for Freescale FLEXCAN based chips"
|
||||
depends on OF || COLDFIRE || COMPILE_TEST
|
||||
|
||||
@@ -21,6 +21,7 @@ obj-$(CONFIG_CAN_CAN327) += can327.o
|
||||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||
obj-$(CONFIG_CAN_CTUCANFD) += ctucanfd/
|
||||
obj-$(CONFIG_CAN_DUMMY) += dummy_can.o
|
||||
obj-$(CONFIG_CAN_FLEXCAN) += flexcan/
|
||||
obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
||||
obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/* 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) 2025 Vincent Mailhol <mailhol@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
@@ -151,3 +152,65 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int can_validate_pwm_bittiming(const struct net_device *dev,
|
||||
const struct can_pwm *pwm,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct can_priv *priv = netdev_priv(dev);
|
||||
u32 xl_bit_time_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
|
||||
u32 nom_bit_time_tqmin = can_bit_time_tqmin(&priv->bittiming);
|
||||
u32 pwms_ns = can_tqmin_to_ns(pwm->pwms, priv->clock.freq);
|
||||
u32 pwml_ns = can_tqmin_to_ns(pwm->pwml, priv->clock.freq);
|
||||
|
||||
if (pwms_ns + pwml_ns > CAN_PWM_NS_MAX) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"The PWM symbol duration: %u ns may not exceed %u ns",
|
||||
pwms_ns + pwml_ns, CAN_PWM_NS_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pwms_ns < CAN_PWM_DECODE_NS) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWMS: %u ns shall be at least %u ns",
|
||||
pwms_ns, CAN_PWM_DECODE_NS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pwm->pwms >= pwm->pwml) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWMS: %u tqmin shall be smaller than PWML: %u tqmin",
|
||||
pwm->pwms, pwm->pwml);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pwml_ns - pwms_ns < 2 * CAN_PWM_DECODE_NS) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"At least %u ns shall separate PWMS: %u ns from PMWL: %u ns",
|
||||
2 * CAN_PWM_DECODE_NS, pwms_ns, pwml_ns);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xl_bit_time_tqmin % (pwm->pwms + pwm->pwml) != 0) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWM duration: %u tqmin does not divide XL's bit time: %u tqmin",
|
||||
pwm->pwms + pwm->pwml, xl_bit_time_tqmin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pwm->pwmo >= pwm->pwms + pwm->pwml) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWMO: %u tqmin can not be greater than PWMS + PWML: %u tqmin",
|
||||
pwm->pwmo, pwm->pwms + pwm->pwml);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nom_bit_time_tqmin % (pwm->pwms + pwm->pwml) != pwm->pwmo) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"Can not assemble nominal bit time: %u tqmin out of PWMS + PMWL and PWMO",
|
||||
nom_bit_time_tqmin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/* 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/units.h>
|
||||
@@ -9,6 +10,33 @@
|
||||
|
||||
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
|
||||
|
||||
/* CiA recommended sample points for Non Return to Zero encoding. */
|
||||
static int can_calc_sample_point_nrz(const struct can_bittiming *bt)
|
||||
{
|
||||
if (bt->bitrate > 800 * KILO /* BPS */)
|
||||
return 750;
|
||||
|
||||
if (bt->bitrate > 500 * KILO /* BPS */)
|
||||
return 800;
|
||||
|
||||
return 875;
|
||||
}
|
||||
|
||||
/* Sample points for Pulse-Width Modulation encoding. */
|
||||
static int can_calc_sample_point_pwm(const struct can_bittiming *bt)
|
||||
{
|
||||
if (bt->bitrate > 15 * MEGA /* BPS */)
|
||||
return 625;
|
||||
|
||||
if (bt->bitrate > 9 * MEGA /* BPS */)
|
||||
return 600;
|
||||
|
||||
if (bt->bitrate > 4 * MEGA /* BPS */)
|
||||
return 560;
|
||||
|
||||
return 520;
|
||||
}
|
||||
|
||||
/* Bit-timing calculation derived from:
|
||||
*
|
||||
* Code based on LinCAN sources and H8S2638 project
|
||||
@@ -23,7 +51,7 @@
|
||||
*/
|
||||
static int
|
||||
can_update_sample_point(const struct can_bittiming_const *btc,
|
||||
const unsigned int sample_point_nominal, const unsigned int tseg,
|
||||
const unsigned int sample_point_reference, const unsigned int tseg,
|
||||
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
|
||||
unsigned int *sample_point_error_ptr)
|
||||
{
|
||||
@@ -34,7 +62,7 @@ can_update_sample_point(const struct can_bittiming_const *btc,
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
tseg2 = tseg + CAN_SYNC_SEG -
|
||||
(sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
|
||||
(sample_point_reference * (tseg + CAN_SYNC_SEG)) /
|
||||
1000 - i;
|
||||
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
|
||||
tseg1 = tseg - tseg2;
|
||||
@@ -45,9 +73,9 @@ can_update_sample_point(const struct can_bittiming_const *btc,
|
||||
|
||||
sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
|
||||
(tseg + CAN_SYNC_SEG);
|
||||
sample_point_error = abs(sample_point_nominal - sample_point);
|
||||
sample_point_error = abs(sample_point_reference - sample_point);
|
||||
|
||||
if (sample_point <= sample_point_nominal &&
|
||||
if (sample_point <= sample_point_reference &&
|
||||
sample_point_error < best_sample_point_error) {
|
||||
best_sample_point = sample_point;
|
||||
best_sample_point_error = sample_point_error;
|
||||
@@ -67,28 +95,24 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
unsigned int bitrate; /* current bitrate */
|
||||
unsigned int bitrate_error; /* difference between current and nominal value */
|
||||
unsigned int bitrate_error; /* diff between calculated and reference value */
|
||||
unsigned int best_bitrate_error = UINT_MAX;
|
||||
unsigned int sample_point_error; /* difference between current and nominal value */
|
||||
unsigned int sample_point_error; /* diff between calculated and reference value */
|
||||
unsigned int best_sample_point_error = UINT_MAX;
|
||||
unsigned int sample_point_nominal; /* nominal sample point */
|
||||
unsigned int sample_point_reference; /* reference sample point */
|
||||
unsigned int best_tseg = 0; /* current best value for tseg */
|
||||
unsigned int best_brp = 0; /* current best value for brp */
|
||||
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
|
||||
u64 v64;
|
||||
int err;
|
||||
|
||||
/* Use CiA recommended sample points */
|
||||
if (bt->sample_point) {
|
||||
sample_point_nominal = bt->sample_point;
|
||||
} else {
|
||||
if (bt->bitrate > 800 * KILO /* BPS */)
|
||||
sample_point_nominal = 750;
|
||||
else if (bt->bitrate > 500 * KILO /* BPS */)
|
||||
sample_point_nominal = 800;
|
||||
else
|
||||
sample_point_nominal = 875;
|
||||
}
|
||||
if (bt->sample_point)
|
||||
sample_point_reference = bt->sample_point;
|
||||
else if (btc == priv->xl.data_bittiming_const &&
|
||||
(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
|
||||
sample_point_reference = can_calc_sample_point_pwm(bt);
|
||||
else
|
||||
sample_point_reference = can_calc_sample_point_nrz(bt);
|
||||
|
||||
/* tseg even = round down, odd = round up */
|
||||
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
|
||||
@@ -114,7 +138,7 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
if (bitrate_error < best_bitrate_error)
|
||||
best_sample_point_error = UINT_MAX;
|
||||
|
||||
can_update_sample_point(btc, sample_point_nominal, tseg / 2,
|
||||
can_update_sample_point(btc, sample_point_reference, tseg / 2,
|
||||
&tseg1, &tseg2, &sample_point_error);
|
||||
if (sample_point_error >= best_sample_point_error)
|
||||
continue;
|
||||
@@ -129,23 +153,26 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
}
|
||||
|
||||
if (best_bitrate_error) {
|
||||
/* Error in one-tenth of a percent */
|
||||
v64 = (u64)best_bitrate_error * 1000;
|
||||
/* Error in one-hundredth of a percent */
|
||||
v64 = (u64)best_bitrate_error * 10000;
|
||||
do_div(v64, bt->bitrate);
|
||||
bitrate_error = (u32)v64;
|
||||
/* print at least 0.01% if the error is smaller */
|
||||
bitrate_error = max(bitrate_error, 1U);
|
||||
if (bitrate_error > CAN_CALC_MAX_ERROR) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"bitrate error: %u.%u%% too high",
|
||||
bitrate_error / 10, bitrate_error % 10);
|
||||
"bitrate error: %u.%02u%% too high",
|
||||
bitrate_error / 100,
|
||||
bitrate_error % 100);
|
||||
return -EINVAL;
|
||||
}
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"bitrate error: %u.%u%%",
|
||||
bitrate_error / 10, bitrate_error % 10);
|
||||
"bitrate error: %u.%02u%%",
|
||||
bitrate_error / 100, bitrate_error % 100);
|
||||
}
|
||||
|
||||
/* real sample point */
|
||||
bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
|
||||
bt->sample_point = can_update_sample_point(btc, sample_point_reference,
|
||||
best_tseg, &tseg1, &tseg2,
|
||||
NULL);
|
||||
|
||||
@@ -198,3 +225,38 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
|
||||
*ctrlmode |= tdc_auto;
|
||||
}
|
||||
}
|
||||
|
||||
int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
|
||||
struct can_pwm *pwm = &priv->xl.pwm;
|
||||
u32 xl_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
|
||||
u32 xl_ns = can_tqmin_to_ns(xl_tqmin, priv->clock.freq);
|
||||
u32 nom_tqmin = can_bit_time_tqmin(&priv->bittiming);
|
||||
int pwm_per_bit_max = xl_tqmin / (pwm_const->pwms_min + pwm_const->pwml_min);
|
||||
int pwm_per_bit;
|
||||
u32 pwm_tqmin;
|
||||
|
||||
/* For 5 MB/s databitrate or greater, xl_ns < CAN_PWM_NS_MAX
|
||||
* giving us a pwm_per_bit of 1 and the loop immediately breaks
|
||||
*/
|
||||
for (pwm_per_bit = DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX);
|
||||
pwm_per_bit <= pwm_per_bit_max; pwm_per_bit++)
|
||||
if (xl_tqmin % pwm_per_bit == 0)
|
||||
break;
|
||||
|
||||
if (pwm_per_bit > pwm_per_bit_max) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"Can not divide the XL data phase's bit time: %u tqmin into multiple PWM symbols",
|
||||
xl_tqmin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pwm_tqmin = xl_tqmin / pwm_per_bit;
|
||||
pwm->pwms = DIV_ROUND_UP_POW2(pwm_tqmin, 4);
|
||||
pwm->pwml = pwm_tqmin - pwm->pwms;
|
||||
pwm->pwmo = nom_tqmin % pwm_tqmin;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -92,29 +92,39 @@ const char *can_get_ctrlmode_str(u32 ctrlmode)
|
||||
{
|
||||
switch (ctrlmode & ~(ctrlmode - 1)) {
|
||||
case 0:
|
||||
return "none";
|
||||
return "(none)";
|
||||
case CAN_CTRLMODE_LOOPBACK:
|
||||
return "loopback";
|
||||
return "LOOPBACK";
|
||||
case CAN_CTRLMODE_LISTENONLY:
|
||||
return "listen-only";
|
||||
return "LISTEN-ONLY";
|
||||
case CAN_CTRLMODE_3_SAMPLES:
|
||||
return "triple-sampling";
|
||||
return "TRIPLE-SAMPLING";
|
||||
case CAN_CTRLMODE_ONE_SHOT:
|
||||
return "one-shot";
|
||||
return "ONE-SHOT";
|
||||
case CAN_CTRLMODE_BERR_REPORTING:
|
||||
return "berr-reporting";
|
||||
return "BERR-REPORTING";
|
||||
case CAN_CTRLMODE_FD:
|
||||
return "fd";
|
||||
return "FD";
|
||||
case CAN_CTRLMODE_PRESUME_ACK:
|
||||
return "presume-ack";
|
||||
return "PRESUME-ACK";
|
||||
case CAN_CTRLMODE_FD_NON_ISO:
|
||||
return "fd-non-iso";
|
||||
return "FD-NON-ISO";
|
||||
case CAN_CTRLMODE_CC_LEN8_DLC:
|
||||
return "cc-len8-dlc";
|
||||
return "CC-LEN8-DLC";
|
||||
case CAN_CTRLMODE_TDC_AUTO:
|
||||
return "fd-tdc-auto";
|
||||
return "TDC-AUTO";
|
||||
case CAN_CTRLMODE_TDC_MANUAL:
|
||||
return "fd-tdc-manual";
|
||||
return "TDC-MANUAL";
|
||||
case CAN_CTRLMODE_RESTRICTED:
|
||||
return "RESTRICTED";
|
||||
case CAN_CTRLMODE_XL:
|
||||
return "XL";
|
||||
case CAN_CTRLMODE_XL_TDC_AUTO:
|
||||
return "XL-TDC-AUTO";
|
||||
case CAN_CTRLMODE_XL_TDC_MANUAL:
|
||||
return "XL-TDC-MANUAL";
|
||||
case CAN_CTRLMODE_XL_TMS:
|
||||
return "TMS";
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
@@ -348,7 +358,13 @@ void can_set_default_mtu(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->ctrlmode & CAN_CTRLMODE_FD) {
|
||||
if (priv->ctrlmode & CAN_CTRLMODE_XL) {
|
||||
if (can_is_canxl_dev_mtu(dev->mtu))
|
||||
return;
|
||||
dev->mtu = CANXL_MTU;
|
||||
dev->min_mtu = CANXL_MIN_MTU;
|
||||
dev->max_mtu = CANXL_MAX_MTU;
|
||||
} else if (priv->ctrlmode & CAN_CTRLMODE_FD) {
|
||||
dev->mtu = CANFD_MTU;
|
||||
dev->min_mtu = CANFD_MTU;
|
||||
dev->max_mtu = CANFD_MTU;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* 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 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
@@ -22,6 +22,10 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
|
||||
[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] = {
|
||||
@@ -36,6 +40,18 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
|
||||
[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)
|
||||
@@ -70,7 +86,7 @@ static int can_validate_tdc(struct nlattr *data_tdc,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
|
||||
/* 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) {
|
||||
@@ -82,8 +98,8 @@ static int can_validate_tdc(struct nlattr *data_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 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];
|
||||
@@ -116,6 +132,40 @@ static int can_validate_tdc(struct nlattr *data_tdc,
|
||||
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)
|
||||
@@ -126,10 +176,10 @@ static int can_validate_databittiming(struct nlattr *data[],
|
||||
bool is_on;
|
||||
int err;
|
||||
|
||||
/* Make sure that valid CAN FD configurations always consist of
|
||||
/* Make sure that valid CAN FD/XL configurations always consist of
|
||||
* - nominal/arbitration bittiming
|
||||
* - data bittiming
|
||||
* - control mode with CAN_CTRLMODE_FD set
|
||||
* - control mode with CAN_CTRLMODE_{FD,XL} set
|
||||
* - TDC parameters are coherent (details in can_validate_tdc())
|
||||
*/
|
||||
|
||||
@@ -139,7 +189,10 @@ static int can_validate_databittiming(struct nlattr *data[],
|
||||
is_on = flags & CAN_CTRLMODE_FD;
|
||||
type = "FD";
|
||||
} else {
|
||||
return -EOPNOTSUPP; /* Place holder for CAN XL */
|
||||
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) {
|
||||
@@ -175,6 +228,32 @@ static int can_validate_databittiming(struct nlattr *data[],
|
||||
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)
|
||||
{
|
||||
@@ -188,6 +267,17 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
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);
|
||||
@@ -199,6 +289,15 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -208,7 +307,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
struct can_ctrlmode *cm;
|
||||
u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
|
||||
u32 ctrlstatic, maskedflags, deactivated, notsupp, ctrlstatic_missing;
|
||||
|
||||
if (!data[IFLA_CAN_CTRLMODE])
|
||||
return 0;
|
||||
@@ -220,6 +319,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
|
||||
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;
|
||||
|
||||
@@ -241,21 +341,40 @@ static int can_ctrlmode_changelink(struct net_device *dev,
|
||||
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 config */
|
||||
/* 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);
|
||||
|
||||
@@ -330,7 +449,10 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
|
||||
dbt_params = &priv->fd;
|
||||
tdc_mask = CAN_CTRLMODE_FD_TDC_MASK;
|
||||
} else {
|
||||
return -EOPNOTSUPP; /* Place holder for CAN XL */
|
||||
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)
|
||||
@@ -366,7 +488,8 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
|
||||
if (data[IFLA_CAN_CTRLMODE]) {
|
||||
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
|
||||
|
||||
need_tdc_calc = !(cm->mask & tdc_mask);
|
||||
if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
|
||||
need_tdc_calc = !(cm->mask & tdc_mask);
|
||||
}
|
||||
if (data_tdc) {
|
||||
/* TDC parameters are provided: use them */
|
||||
@@ -381,7 +504,7 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
|
||||
*/
|
||||
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
|
||||
} /* else: both CAN_CTRLMODE_{,XL}_TDC_{AUTO,MANUAL} are explicitly
|
||||
* turned off. TDC is disabled: do nothing
|
||||
*/
|
||||
|
||||
@@ -397,6 +520,76 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_pwm_changelink(struct net_device *dev,
|
||||
const struct nlattr *pwm_nla,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
|
||||
struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
|
||||
struct can_pwm pwm = { 0 };
|
||||
int err;
|
||||
|
||||
if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
|
||||
return 0;
|
||||
|
||||
if (!pwm_const) {
|
||||
NL_SET_ERR_MSG(extack, "The device does not support PWM");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!pwm_nla)
|
||||
return can_calc_pwm(dev, extack);
|
||||
|
||||
err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla,
|
||||
can_pwm_policy, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tb_pwm[IFLA_CAN_PWM_PWMS]) {
|
||||
pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]);
|
||||
if (pwm.pwms < pwm_const->pwms_min ||
|
||||
pwm.pwms > pwm_const->pwms_max) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWMS: %u tqmin is out of range: %u...%u",
|
||||
pwm.pwms, pwm_const->pwms_min,
|
||||
pwm_const->pwms_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tb_pwm[IFLA_CAN_PWM_PWML]) {
|
||||
pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]);
|
||||
if (pwm.pwml < pwm_const->pwml_min ||
|
||||
pwm.pwml > pwm_const->pwml_max) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWML: %u tqmin is out of range: %u...%u",
|
||||
pwm.pwml, pwm_const->pwml_min,
|
||||
pwm_const->pwml_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tb_pwm[IFLA_CAN_PWM_PWMO]) {
|
||||
pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]);
|
||||
if (pwm.pwmo < pwm_const->pwmo_min ||
|
||||
pwm.pwmo > pwm_const->pwmo_max) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"PWMO: %u tqmin is out of range: %u...%u",
|
||||
pwm.pwmo, pwm_const->pwmo_min,
|
||||
pwm_const->pwmo_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
err = can_validate_pwm_bittiming(dev, &pwm, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->xl.pwm = pwm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -486,6 +679,14 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* CAN XL */
|
||||
err = can_dbt_changelink(dev, data, false, extack);
|
||||
if (err)
|
||||
return err;
|
||||
err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], 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;
|
||||
@@ -553,14 +754,14 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
|
||||
if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_{,XL}_DATA_BITTIMING */
|
||||
size += nla_total_size(sizeof(dbt_params->data_bittiming));
|
||||
if (dbt_params->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
|
||||
if (dbt_params->data_bittiming_const) /* IFLA_CAN_{,XL}_DATA_BITTIMING_CONST */
|
||||
size += nla_total_size(sizeof(*dbt_params->data_bittiming_const));
|
||||
if (dbt_params->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
|
||||
if (dbt_params->data_bitrate_const) /* IFLA_CAN_{,XL}_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 */
|
||||
size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -571,6 +772,30 @@ static size_t can_ctrlmode_ext_get_size(void)
|
||||
nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */
|
||||
}
|
||||
|
||||
static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const,
|
||||
bool pwm_on)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (!pwm_const || !pwm_on)
|
||||
return 0;
|
||||
|
||||
size = nla_total_size(0); /* nest IFLA_CAN_PWM */
|
||||
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MIN */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MAX */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MIN */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MAX */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MIN */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MAX */
|
||||
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO */
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t can_get_size(const struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
@@ -600,6 +825,11 @@ static size_t can_get_size(const struct net_device *dev)
|
||||
size += can_data_bittiming_get_size(&priv->fd,
|
||||
priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
|
||||
|
||||
size += can_data_bittiming_get_size(&priv->xl,
|
||||
priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
|
||||
size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */
|
||||
priv->ctrlmode & CAN_CTRLMODE_XL_TMS);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -644,7 +874,9 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
|
||||
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 */
|
||||
dbt_params = &priv->xl;
|
||||
tdc_is_enabled = can_xl_tdc_is_enabled(priv);
|
||||
tdc_manual = priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MANUAL;
|
||||
}
|
||||
tdc_const = dbt_params->tdc_const;
|
||||
tdc = &dbt_params->tdc;
|
||||
@@ -695,6 +927,42 @@ err_cancel:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv)
|
||||
{
|
||||
const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
|
||||
const struct can_pwm *pwm = &priv->xl.pwm;
|
||||
struct nlattr *nest;
|
||||
|
||||
if (!pwm_const)
|
||||
return 0;
|
||||
|
||||
nest = nla_nest_start(skb, IFLA_CAN_XL_PWM);
|
||||
if (!nest)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max))
|
||||
goto err_cancel;
|
||||
|
||||
if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
|
||||
if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) ||
|
||||
nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo))
|
||||
goto err_cancel;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
return 0;
|
||||
|
||||
err_cancel:
|
||||
nla_nest_cancel(skb, nest);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,
|
||||
const struct can_priv *priv)
|
||||
{
|
||||
@@ -766,9 +1034,22 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
|
||||
can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) ||
|
||||
|
||||
can_ctrlmode_ext_fill_info(skb, priv)
|
||||
)
|
||||
can_ctrlmode_ext_fill_info(skb, priv) ||
|
||||
|
||||
can_bittiming_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING,
|
||||
&priv->xl.data_bittiming) ||
|
||||
|
||||
can_bittiming_const_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING_CONST,
|
||||
priv->xl.data_bittiming_const) ||
|
||||
|
||||
can_bitrate_const_fill_info(skb, IFLA_CAN_XL_DATA_BITRATE_CONST,
|
||||
priv->xl.data_bitrate_const,
|
||||
priv->xl.data_bitrate_const_cnt) ||
|
||||
|
||||
can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) ||
|
||||
|
||||
can_pwm_fill_info(skb, priv)
|
||||
)
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
|
||||
285
drivers/net/can/dummy_can.c
Normal file
285
drivers/net/can/dummy_can.c
Normal file
@@ -0,0 +1,285 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/* Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org> */
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/bittiming.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/skb.h>
|
||||
|
||||
struct dummy_can {
|
||||
struct can_priv can;
|
||||
struct net_device *dev;
|
||||
};
|
||||
|
||||
static struct dummy_can *dummy_can;
|
||||
|
||||
static const struct can_bittiming_const dummy_can_bittiming_const = {
|
||||
.name = "dummy_can CC",
|
||||
.tseg1_min = 2,
|
||||
.tseg1_max = 256,
|
||||
.tseg2_min = 2,
|
||||
.tseg2_max = 128,
|
||||
.sjw_max = 128,
|
||||
.brp_min = 1,
|
||||
.brp_max = 512,
|
||||
.brp_inc = 1
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const dummy_can_fd_databittiming_const = {
|
||||
.name = "dummy_can FD",
|
||||
.tseg1_min = 2,
|
||||
.tseg1_max = 256,
|
||||
.tseg2_min = 2,
|
||||
.tseg2_max = 128,
|
||||
.sjw_max = 128,
|
||||
.brp_min = 1,
|
||||
.brp_max = 512,
|
||||
.brp_inc = 1
|
||||
};
|
||||
|
||||
static const struct can_tdc_const dummy_can_fd_tdc_const = {
|
||||
.tdcv_min = 0,
|
||||
.tdcv_max = 0, /* Manual mode not supported. */
|
||||
.tdco_min = 0,
|
||||
.tdco_max = 127,
|
||||
.tdcf_min = 0,
|
||||
.tdcf_max = 127
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const dummy_can_xl_databittiming_const = {
|
||||
.name = "dummy_can XL",
|
||||
.tseg1_min = 2,
|
||||
.tseg1_max = 256,
|
||||
.tseg2_min = 2,
|
||||
.tseg2_max = 128,
|
||||
.sjw_max = 128,
|
||||
.brp_min = 1,
|
||||
.brp_max = 512,
|
||||
.brp_inc = 1
|
||||
};
|
||||
|
||||
static const struct can_tdc_const dummy_can_xl_tdc_const = {
|
||||
.tdcv_min = 0,
|
||||
.tdcv_max = 0, /* Manual mode not supported. */
|
||||
.tdco_min = 0,
|
||||
.tdco_max = 127,
|
||||
.tdcf_min = 0,
|
||||
.tdcf_max = 127
|
||||
};
|
||||
|
||||
static const struct can_pwm_const dummy_can_pwm_const = {
|
||||
.pwms_min = 1,
|
||||
.pwms_max = 8,
|
||||
.pwml_min = 2,
|
||||
.pwml_max = 24,
|
||||
.pwmo_min = 0,
|
||||
.pwmo_max = 16,
|
||||
};
|
||||
|
||||
static void dummy_can_print_bittiming(struct net_device *dev,
|
||||
struct can_bittiming *bt)
|
||||
{
|
||||
netdev_dbg(dev, "\tbitrate: %u\n", bt->bitrate);
|
||||
netdev_dbg(dev, "\tsample_point: %u\n", bt->sample_point);
|
||||
netdev_dbg(dev, "\ttq: %u\n", bt->tq);
|
||||
netdev_dbg(dev, "\tprop_seg: %u\n", bt->prop_seg);
|
||||
netdev_dbg(dev, "\tphase_seg1: %u\n", bt->phase_seg1);
|
||||
netdev_dbg(dev, "\tphase_seg2: %u\n", bt->phase_seg2);
|
||||
netdev_dbg(dev, "\tsjw: %u\n", bt->sjw);
|
||||
netdev_dbg(dev, "\tbrp: %u\n", bt->brp);
|
||||
}
|
||||
|
||||
static void dummy_can_print_tdc(struct net_device *dev, struct can_tdc *tdc)
|
||||
{
|
||||
netdev_dbg(dev, "\t\ttdcv: %u\n", tdc->tdcv);
|
||||
netdev_dbg(dev, "\t\ttdco: %u\n", tdc->tdco);
|
||||
netdev_dbg(dev, "\t\ttdcf: %u\n", tdc->tdcf);
|
||||
}
|
||||
|
||||
static void dummy_can_print_pwm(struct net_device *dev, struct can_pwm *pwm,
|
||||
struct can_bittiming *dbt)
|
||||
{
|
||||
netdev_dbg(dev, "\t\tpwms: %u\n", pwm->pwms);
|
||||
netdev_dbg(dev, "\t\tpwml: %u\n", pwm->pwml);
|
||||
netdev_dbg(dev, "\t\tpwmo: %u\n", pwm->pwmo);
|
||||
}
|
||||
|
||||
static void dummy_can_print_ctrlmode(struct net_device *dev)
|
||||
{
|
||||
struct dummy_can *priv = netdev_priv(dev);
|
||||
struct can_priv *can_priv = &priv->can;
|
||||
unsigned long supported = can_priv->ctrlmode_supported;
|
||||
u32 enabled = can_priv->ctrlmode;
|
||||
|
||||
netdev_dbg(dev, "Control modes:\n");
|
||||
netdev_dbg(dev, "\tsupported: 0x%08x\n", (u32)supported);
|
||||
netdev_dbg(dev, "\tenabled: 0x%08x\n", enabled);
|
||||
|
||||
if (supported) {
|
||||
int idx;
|
||||
|
||||
netdev_dbg(dev, "\tlist:");
|
||||
for_each_set_bit(idx, &supported, BITS_PER_TYPE(u32))
|
||||
netdev_dbg(dev, "\t\t%s: %s\n",
|
||||
can_get_ctrlmode_str(BIT(idx)),
|
||||
enabled & BIT(idx) ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
static void dummy_can_print_bittiming_info(struct net_device *dev)
|
||||
{
|
||||
struct dummy_can *priv = netdev_priv(dev);
|
||||
struct can_priv *can_priv = &priv->can;
|
||||
|
||||
netdev_dbg(dev, "Clock frequency: %u\n", can_priv->clock.freq);
|
||||
netdev_dbg(dev, "Maximum bitrate: %u\n", can_priv->bitrate_max);
|
||||
netdev_dbg(dev, "MTU: %u\n", dev->mtu);
|
||||
netdev_dbg(dev, "\n");
|
||||
|
||||
dummy_can_print_ctrlmode(dev);
|
||||
netdev_dbg(dev, "\n");
|
||||
|
||||
netdev_dbg(dev, "Classical CAN nominal bittiming:\n");
|
||||
dummy_can_print_bittiming(dev, &can_priv->bittiming);
|
||||
netdev_dbg(dev, "\n");
|
||||
|
||||
if (can_priv->ctrlmode & CAN_CTRLMODE_FD) {
|
||||
netdev_dbg(dev, "CAN FD databittiming:\n");
|
||||
dummy_can_print_bittiming(dev, &can_priv->fd.data_bittiming);
|
||||
if (can_fd_tdc_is_enabled(can_priv)) {
|
||||
netdev_dbg(dev, "\tCAN FD TDC:\n");
|
||||
dummy_can_print_tdc(dev, &can_priv->fd.tdc);
|
||||
}
|
||||
}
|
||||
netdev_dbg(dev, "\n");
|
||||
|
||||
if (can_priv->ctrlmode & CAN_CTRLMODE_XL) {
|
||||
netdev_dbg(dev, "CAN XL databittiming:\n");
|
||||
dummy_can_print_bittiming(dev, &can_priv->xl.data_bittiming);
|
||||
if (can_xl_tdc_is_enabled(can_priv)) {
|
||||
netdev_dbg(dev, "\tCAN XL TDC:\n");
|
||||
dummy_can_print_tdc(dev, &can_priv->xl.tdc);
|
||||
}
|
||||
if (can_priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
|
||||
netdev_dbg(dev, "\tCAN XL PWM:\n");
|
||||
dummy_can_print_pwm(dev, &can_priv->xl.pwm,
|
||||
&can_priv->xl.data_bittiming);
|
||||
}
|
||||
}
|
||||
netdev_dbg(dev, "\n");
|
||||
}
|
||||
|
||||
static int dummy_can_netdev_open(struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
dummy_can_print_bittiming_info(dev);
|
||||
netdev_dbg(dev, "error-signalling is %s\n",
|
||||
str_enabled_disabled(!can_dev_in_xl_only_mode(priv)));
|
||||
|
||||
ret = open_candev(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
netif_start_queue(dev);
|
||||
netdev_dbg(dev, "dummy-can is up\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_can_netdev_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
close_candev(dev);
|
||||
netdev_dbg(dev, "dummy-can is down\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_tx_t dummy_can_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
if (can_dev_dropped_skb(dev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
can_put_echo_skb(skb, dev, 0, 0);
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += can_get_echo_skb(dev, 0, NULL);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static const struct net_device_ops dummy_can_netdev_ops = {
|
||||
.ndo_open = dummy_can_netdev_open,
|
||||
.ndo_stop = dummy_can_netdev_close,
|
||||
.ndo_start_xmit = dummy_can_start_xmit,
|
||||
};
|
||||
|
||||
static const struct ethtool_ops dummy_can_ethtool_ops = {
|
||||
.get_ts_info = ethtool_op_get_ts_info,
|
||||
};
|
||||
|
||||
static int __init dummy_can_init(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct dummy_can *priv;
|
||||
int ret;
|
||||
|
||||
dev = alloc_candev(sizeof(*priv), 1);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->netdev_ops = &dummy_can_netdev_ops;
|
||||
dev->ethtool_ops = &dummy_can_ethtool_ops;
|
||||
priv = netdev_priv(dev);
|
||||
priv->can.bittiming_const = &dummy_can_bittiming_const;
|
||||
priv->can.bitrate_max = 20 * MEGA /* BPS */;
|
||||
priv->can.clock.freq = 160 * MEGA /* Hz */;
|
||||
priv->can.fd.data_bittiming_const = &dummy_can_fd_databittiming_const;
|
||||
priv->can.fd.tdc_const = &dummy_can_fd_tdc_const;
|
||||
priv->can.xl.data_bittiming_const = &dummy_can_xl_databittiming_const;
|
||||
priv->can.xl.tdc_const = &dummy_can_xl_tdc_const;
|
||||
priv->can.xl.pwm_const = &dummy_can_pwm_const;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
|
||||
CAN_CTRLMODE_FD | CAN_CTRLMODE_TDC_AUTO |
|
||||
CAN_CTRLMODE_RESTRICTED | CAN_CTRLMODE_XL |
|
||||
CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TMS;
|
||||
priv->dev = dev;
|
||||
|
||||
ret = register_candev(priv->dev);
|
||||
if (ret) {
|
||||
free_candev(priv->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dummy_can = priv;
|
||||
netdev_dbg(dev, "dummy-can ready\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dummy_can_exit(void)
|
||||
{
|
||||
struct net_device *dev = dummy_can->dev;
|
||||
|
||||
netdev_dbg(dev, "dummy-can bye bye\n");
|
||||
unregister_candev(dev);
|
||||
free_candev(dev);
|
||||
}
|
||||
|
||||
module_init(dummy_can_init);
|
||||
module_exit(dummy_can_exit);
|
||||
|
||||
MODULE_DESCRIPTION("A dummy CAN driver, mainly to test the netlink interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vincent Mailhol <mailhol@kernel.org>");
|
||||
@@ -468,6 +468,7 @@ struct rcar_canfd_global {
|
||||
struct platform_device *pdev; /* Respective platform device */
|
||||
struct clk *clkp; /* Peripheral clock */
|
||||
struct clk *can_clk; /* fCAN clock */
|
||||
struct clk *clk_ram; /* Clock RAM */
|
||||
unsigned long channels_mask; /* Enabled channels mask */
|
||||
bool extclk; /* CANFD or Ext clock */
|
||||
bool fdmode; /* CAN FD or Classical CAN only mode */
|
||||
@@ -1569,8 +1570,8 @@ static int rcar_canfd_close(struct net_device *ndev)
|
||||
netif_stop_queue(ndev);
|
||||
rcar_canfd_stop(ndev);
|
||||
napi_disable(&priv->napi);
|
||||
clk_disable_unprepare(gpriv->can_clk);
|
||||
close_candev(ndev);
|
||||
clk_disable_unprepare(gpriv->can_clk);
|
||||
phy_power_off(priv->transceiver);
|
||||
return 0;
|
||||
}
|
||||
@@ -1960,22 +1961,120 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch)
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
|
||||
{
|
||||
struct device *dev = &gpriv->pdev->dev;
|
||||
u32 rule_entry = 0;
|
||||
u32 ch, sts;
|
||||
int err;
|
||||
|
||||
err = reset_control_reset(gpriv->rstc1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_reset(gpriv->rstc2);
|
||||
if (err)
|
||||
goto fail_reset1;
|
||||
|
||||
/* Enable peripheral clock for register access */
|
||||
err = clk_prepare_enable(gpriv->clkp);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable peripheral clock: %pe\n",
|
||||
ERR_PTR(err));
|
||||
goto fail_reset2;
|
||||
}
|
||||
|
||||
/* Enable RAM clock */
|
||||
err = clk_prepare_enable(gpriv->clk_ram);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"failed to enable RAM clock, error %d\n", err);
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
err = rcar_canfd_reset_controller(gpriv);
|
||||
if (err) {
|
||||
dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
|
||||
goto fail_ram_clk;
|
||||
}
|
||||
|
||||
/* Controller in Global reset & Channel reset mode */
|
||||
rcar_canfd_configure_controller(gpriv);
|
||||
|
||||
/* Configure per channel attributes */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
/* Configure Channel's Rx fifo */
|
||||
rcar_canfd_configure_rx(gpriv, ch);
|
||||
|
||||
/* Configure Channel's Tx (Common) fifo */
|
||||
rcar_canfd_configure_tx(gpriv, ch);
|
||||
|
||||
/* Configure receive rules */
|
||||
rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
|
||||
rule_entry += RCANFD_CHANNEL_NUMRULES;
|
||||
}
|
||||
|
||||
/* Configure common interrupts */
|
||||
rcar_canfd_enable_global_interrupts(gpriv);
|
||||
|
||||
/* Start Global operation mode */
|
||||
rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
|
||||
RCANFD_GCTR_GMDC_GOPM);
|
||||
|
||||
/* Verify mode change */
|
||||
err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
|
||||
!(sts & RCANFD_GSTS_GNOPM), 2, 500000);
|
||||
if (err) {
|
||||
dev_err(dev, "global operational mode failed\n");
|
||||
goto fail_mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_mode:
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
fail_ram_clk:
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
fail_clk:
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
fail_reset2:
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
fail_reset1:
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full)
|
||||
{
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
|
||||
if (full) {
|
||||
rcar_canfd_reset_controller(gpriv);
|
||||
|
||||
/* Enter global sleep mode */
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
}
|
||||
|
||||
static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy *transceivers[RCANFD_NUM_CHANNELS] = { NULL, };
|
||||
const struct rcar_canfd_hw_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *addr;
|
||||
u32 sts, ch, fcan_freq;
|
||||
struct rcar_canfd_global *gpriv;
|
||||
struct device_node *of_child;
|
||||
unsigned long channels_mask = 0;
|
||||
int err, ch_irq, g_irq;
|
||||
int g_err_irq, g_recc_irq;
|
||||
u32 rule_entry = 0;
|
||||
bool fdmode = true; /* CAN FD only mode - default */
|
||||
char name[9] = "channelX";
|
||||
struct clk *clk_ram;
|
||||
u32 ch, fcan_freq;
|
||||
int i;
|
||||
|
||||
info = of_device_get_match_data(dev);
|
||||
@@ -2065,10 +2164,10 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
gpriv->extclk = gpriv->info->external_clk;
|
||||
}
|
||||
|
||||
clk_ram = devm_clk_get_optional_enabled(dev, "ram_clk");
|
||||
if (IS_ERR(clk_ram))
|
||||
return dev_err_probe(dev, PTR_ERR(clk_ram),
|
||||
"cannot get enabled ram clock\n");
|
||||
gpriv->clk_ram = devm_clk_get_optional(dev, "ram_clk");
|
||||
if (IS_ERR(gpriv->clk_ram))
|
||||
return dev_err_probe(dev, PTR_ERR(gpriv->clk_ram),
|
||||
"cannot get ram clock\n");
|
||||
|
||||
addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(addr)) {
|
||||
@@ -2117,59 +2216,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
err = reset_control_reset(gpriv->rstc1);
|
||||
err = rcar_canfd_global_init(gpriv);
|
||||
if (err)
|
||||
goto fail_dev;
|
||||
err = reset_control_reset(gpriv->rstc2);
|
||||
if (err) {
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
goto fail_dev;
|
||||
}
|
||||
|
||||
/* Enable peripheral clock for register access */
|
||||
err = clk_prepare_enable(gpriv->clkp);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable peripheral clock: %pe\n",
|
||||
ERR_PTR(err));
|
||||
goto fail_reset;
|
||||
}
|
||||
|
||||
err = rcar_canfd_reset_controller(gpriv);
|
||||
if (err) {
|
||||
dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
/* Controller in Global reset & Channel reset mode */
|
||||
rcar_canfd_configure_controller(gpriv);
|
||||
|
||||
/* Configure per channel attributes */
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
|
||||
/* Configure Channel's Rx fifo */
|
||||
rcar_canfd_configure_rx(gpriv, ch);
|
||||
|
||||
/* Configure Channel's Tx (Common) fifo */
|
||||
rcar_canfd_configure_tx(gpriv, ch);
|
||||
|
||||
/* Configure receive rules */
|
||||
rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
|
||||
rule_entry += RCANFD_CHANNEL_NUMRULES;
|
||||
}
|
||||
|
||||
/* Configure common interrupts */
|
||||
rcar_canfd_enable_global_interrupts(gpriv);
|
||||
|
||||
/* Start Global operation mode */
|
||||
rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
|
||||
RCANFD_GCTR_GMDC_GOPM);
|
||||
|
||||
/* Verify mode change */
|
||||
err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
|
||||
!(sts & RCANFD_GSTS_GNOPM), 2, 500000);
|
||||
if (err) {
|
||||
dev_err(dev, "global operational mode failed\n");
|
||||
goto fail_mode;
|
||||
}
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
|
||||
err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq,
|
||||
@@ -2188,12 +2237,7 @@ fail_channel:
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels)
|
||||
rcar_canfd_channel_remove(gpriv, ch);
|
||||
fail_mode:
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
fail_clk:
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
fail_reset:
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
rcar_canfd_global_deinit(gpriv, false);
|
||||
fail_dev:
|
||||
return err;
|
||||
}
|
||||
@@ -2203,33 +2247,79 @@ static void rcar_canfd_remove(struct platform_device *pdev)
|
||||
struct rcar_canfd_global *gpriv = platform_get_drvdata(pdev);
|
||||
u32 ch;
|
||||
|
||||
rcar_canfd_reset_controller(gpriv);
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]);
|
||||
rcar_canfd_channel_remove(gpriv, ch);
|
||||
}
|
||||
|
||||
/* Enter global sleep mode */
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
rcar_canfd_global_deinit(gpriv, true);
|
||||
}
|
||||
|
||||
static int __maybe_unused rcar_canfd_suspend(struct device *dev)
|
||||
static int rcar_canfd_suspend(struct device *dev)
|
||||
{
|
||||
struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u32 ch;
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
struct rcar_canfd_channel *priv = gpriv->ch[ch];
|
||||
struct net_device *ndev = priv->ndev;
|
||||
|
||||
if (!netif_running(ndev))
|
||||
continue;
|
||||
|
||||
netif_device_detach(ndev);
|
||||
|
||||
err = rcar_canfd_close(ndev);
|
||||
if (err) {
|
||||
netdev_err(ndev, "rcar_canfd_close() failed %pe\n",
|
||||
ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->can.state = CAN_STATE_SLEEPING;
|
||||
}
|
||||
|
||||
/* TODO Skip if wake-up (which is not yet supported) is enabled */
|
||||
rcar_canfd_global_deinit(gpriv, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rcar_canfd_resume(struct device *dev)
|
||||
static int rcar_canfd_resume(struct device *dev)
|
||||
{
|
||||
struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u32 ch;
|
||||
|
||||
err = rcar_canfd_global_init(gpriv);
|
||||
if (err) {
|
||||
dev_err(dev, "rcar_canfd_global_init() failed %pe\n", ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
|
||||
struct rcar_canfd_channel *priv = gpriv->ch[ch];
|
||||
struct net_device *ndev = priv->ndev;
|
||||
|
||||
if (!netif_running(ndev))
|
||||
continue;
|
||||
|
||||
err = rcar_canfd_open(ndev);
|
||||
if (err) {
|
||||
netdev_err(ndev, "rcar_canfd_open() failed %pe\n",
|
||||
ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
netif_device_attach(ndev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
|
||||
rcar_canfd_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
|
||||
rcar_canfd_resume);
|
||||
|
||||
static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = {
|
||||
{ .compatible = "renesas,r8a779a0-canfd", .data = &rcar_gen4_hw_info },
|
||||
@@ -2246,7 +2336,7 @@ static struct platform_driver rcar_canfd_driver = {
|
||||
.driver = {
|
||||
.name = RCANFD_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(rcar_canfd_of_table),
|
||||
.pm = &rcar_canfd_pm_ops,
|
||||
.pm = pm_sleep_ptr(&rcar_canfd_pm_ops),
|
||||
},
|
||||
.probe = rcar_canfd_probe,
|
||||
.remove = rcar_canfd_remove,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2020 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
* Copyright (c) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
* Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
|
||||
*/
|
||||
|
||||
#ifndef _CAN_BITTIMING_H
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
#define CAN_CTRLMODE_FD_TDC_MASK \
|
||||
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_TDC_MANUAL)
|
||||
#define CAN_CTRLMODE_XL_TDC_MASK \
|
||||
(CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TDC_MANUAL)
|
||||
#define CAN_CTRLMODE_TDC_AUTO_MASK \
|
||||
(CAN_CTRLMODE_TDC_AUTO)
|
||||
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_XL_TDC_AUTO)
|
||||
#define CAN_CTRLMODE_TDC_MANUAL_MASK \
|
||||
(CAN_CTRLMODE_TDC_MANUAL)
|
||||
(CAN_CTRLMODE_TDC_MANUAL | CAN_CTRLMODE_XL_TDC_MANUAL)
|
||||
|
||||
/*
|
||||
* struct can_tdc - CAN FD Transmission Delay Compensation parameters
|
||||
@@ -85,6 +87,11 @@ struct can_tdc {
|
||||
u32 tdcf;
|
||||
};
|
||||
|
||||
/* The transceiver decoding margin corresponds to t_Decode in ISO 11898-2 */
|
||||
#define CAN_PWM_DECODE_NS 5
|
||||
/* Maximum PWM symbol duration. Corresponds to t_SymbolNom_MAX - t_Decode */
|
||||
#define CAN_PWM_NS_MAX (205 - CAN_PWM_DECODE_NS)
|
||||
|
||||
/*
|
||||
* struct can_tdc_const - CAN hardware-dependent constant for
|
||||
* Transmission Delay Compensation
|
||||
@@ -118,11 +125,48 @@ struct can_tdc_const {
|
||||
u32 tdcf_max;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct can_pwm - CAN Pulse-Width Modulation (PWM) parameters
|
||||
*
|
||||
* @pwms: pulse width modulation short phase
|
||||
* @pwml: pulse width modulation long phase
|
||||
* @pwmo: pulse width modulation offset
|
||||
*/
|
||||
struct can_pwm {
|
||||
u32 pwms;
|
||||
u32 pwml;
|
||||
u32 pwmo;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct can_pwm - CAN hardware-dependent constants for Pulse-Width
|
||||
* Modulation (PWM)
|
||||
*
|
||||
* @pwms_min: PWM short phase minimum value. Must be at least 1.
|
||||
* @pwms_max: PWM short phase maximum value
|
||||
* @pwml_min: PWM long phase minimum value. Must be at least 1.
|
||||
* @pwml_max: PWM long phase maximum value
|
||||
* @pwmo_min: PWM offset phase minimum value
|
||||
* @pwmo_max: PWM offset phase maximum value
|
||||
*/
|
||||
struct can_pwm_const {
|
||||
u32 pwms_min;
|
||||
u32 pwms_max;
|
||||
u32 pwml_min;
|
||||
u32 pwml_max;
|
||||
u32 pwmo_min;
|
||||
u32 pwmo_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 struct can_pwm_const *pwm_const;
|
||||
union {
|
||||
struct can_tdc tdc;
|
||||
struct can_pwm pwm;
|
||||
};
|
||||
const u32 *data_bitrate_const;
|
||||
unsigned int data_bitrate_const_cnt;
|
||||
int (*do_set_data_bittiming)(struct net_device *dev);
|
||||
@@ -136,12 +180,14 @@ 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 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported);
|
||||
|
||||
int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack);
|
||||
#else /* !CONFIG_CAN_CALC_BITTIMING */
|
||||
static inline int
|
||||
can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
const struct can_bittiming_const *btc, struct netlink_ext_ack *extack)
|
||||
{
|
||||
netdev_err(dev, "bit-timing calculation not available\n");
|
||||
NL_SET_ERR_MSG(extack, "bit-timing calculation not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -151,6 +197,14 @@ can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
|
||||
u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int
|
||||
can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
|
||||
{
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"bit-timing calculation not available: manually provide PWML and PWMS\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_CAN_CALC_BITTIMING */
|
||||
|
||||
void can_sjw_set_default(struct can_bittiming *bt);
|
||||
@@ -164,6 +218,10 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
|
||||
const unsigned int bitrate_const_cnt,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
int can_validate_pwm_bittiming(const struct net_device *dev,
|
||||
const struct can_pwm *pwm,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
/*
|
||||
* can_get_relative_tdco() - TDCO relative to the sample point
|
||||
*
|
||||
@@ -206,4 +264,17 @@ static inline unsigned int can_bit_time(const struct can_bittiming *bt)
|
||||
return CAN_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
|
||||
}
|
||||
|
||||
/* Duration of one bit in minimum time quantum */
|
||||
static inline unsigned int can_bit_time_tqmin(const struct can_bittiming *bt)
|
||||
{
|
||||
return can_bit_time(bt) * bt->brp;
|
||||
}
|
||||
|
||||
/* Convert a duration from minimum a minimum time quantum to nano seconds */
|
||||
static inline u32 can_tqmin_to_ns(u32 tqmin, u32 clock_freq)
|
||||
{
|
||||
return DIV_U64_ROUND_CLOSEST(mul_u32_u32(tqmin, NSEC_PER_SEC),
|
||||
clock_freq);
|
||||
}
|
||||
|
||||
#endif /* !_CAN_BITTIMING_H */
|
||||
|
||||
@@ -47,7 +47,7 @@ struct can_priv {
|
||||
|
||||
const struct can_bittiming_const *bittiming_const;
|
||||
struct can_bittiming bittiming;
|
||||
struct data_bittiming_params fd;
|
||||
struct data_bittiming_params fd, xl;
|
||||
unsigned int bitrate_const_cnt;
|
||||
const u32 *bitrate_const;
|
||||
u32 bitrate_max;
|
||||
@@ -85,6 +85,11 @@ static inline bool can_fd_tdc_is_enabled(const struct can_priv *priv)
|
||||
return !!(priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
|
||||
}
|
||||
|
||||
static inline bool can_xl_tdc_is_enabled(const struct can_priv *priv)
|
||||
{
|
||||
return !!(priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
|
||||
}
|
||||
|
||||
static inline u32 can_get_static_ctrlmode(struct can_priv *priv)
|
||||
{
|
||||
return priv->ctrlmode & ~priv->ctrlmode_supported;
|
||||
@@ -95,22 +100,6 @@ static inline bool can_is_canxl_dev_mtu(unsigned int mtu)
|
||||
return (mtu >= CANXL_MIN_MTU && mtu <= CANXL_MAX_MTU);
|
||||
}
|
||||
|
||||
/* drop skb if it does not contain a valid CAN frame for sending */
|
||||
static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->ctrlmode & CAN_CTRLMODE_LISTENONLY) {
|
||||
netdev_info_once(dev,
|
||||
"interface in listen only mode, dropping skb\n");
|
||||
kfree_skb(skb);
|
||||
dev->stats.tx_dropped++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return can_dropped_invalid_skb(dev, skb);
|
||||
}
|
||||
|
||||
void can_setup(struct net_device *dev);
|
||||
|
||||
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
|
||||
@@ -146,6 +135,51 @@ 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);
|
||||
|
||||
static inline bool can_dev_in_xl_only_mode(struct can_priv *priv)
|
||||
{
|
||||
const u32 mixed_mode = CAN_CTRLMODE_FD | CAN_CTRLMODE_XL;
|
||||
|
||||
/* When CAN XL is enabled but FD is disabled we are running in
|
||||
* the so-called 'CANXL-only mode' where the error signalling is
|
||||
* disabled. This helper function determines the required value
|
||||
* to disable error signalling in the CAN XL controller.
|
||||
* The so-called CC/FD/XL 'mixed mode' requires error signalling.
|
||||
*/
|
||||
return ((priv->ctrlmode & mixed_mode) == CAN_CTRLMODE_XL);
|
||||
}
|
||||
|
||||
/* drop skb if it does not contain a valid CAN frame for sending */
|
||||
static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
u32 silent_mode = priv->ctrlmode & (CAN_CTRLMODE_LISTENONLY |
|
||||
CAN_CTRLMODE_RESTRICTED);
|
||||
|
||||
if (silent_mode) {
|
||||
netdev_info_once(dev, "interface in %s mode, dropping skb\n",
|
||||
can_get_ctrlmode_str(silent_mode));
|
||||
goto invalid_skb;
|
||||
}
|
||||
|
||||
if (!(priv->ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
|
||||
netdev_info_once(dev, "CAN FD is disabled, dropping skb\n");
|
||||
goto invalid_skb;
|
||||
}
|
||||
|
||||
if (can_dev_in_xl_only_mode(priv) && !can_is_canxl_skb(skb)) {
|
||||
netdev_info_once(dev,
|
||||
"Error signaling is disabled, dropping skb\n");
|
||||
goto invalid_skb;
|
||||
}
|
||||
|
||||
return can_dropped_invalid_skb(dev, skb);
|
||||
|
||||
invalid_skb:
|
||||
kfree_skb(skb);
|
||||
dev->stats.tx_dropped++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void can_state_get_by_berr_counter(const struct net_device *dev,
|
||||
const struct can_berr_counter *bec,
|
||||
enum can_state *tx_state,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Definitions for the CAN netlink interface
|
||||
*
|
||||
* Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
* Copyright (c) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
@@ -103,6 +104,11 @@ struct can_ctrlmode {
|
||||
#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
|
||||
#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 */
|
||||
#define CAN_CTRLMODE_RESTRICTED 0x800 /* Restricted operation mode */
|
||||
#define CAN_CTRLMODE_XL 0x1000 /* CAN XL mode */
|
||||
#define CAN_CTRLMODE_XL_TDC_AUTO 0x2000 /* XL transceiver automatically calculates TDCV */
|
||||
#define CAN_CTRLMODE_XL_TDC_MANUAL 0x4000 /* XL TDCV is manually set up by user */
|
||||
#define CAN_CTRLMODE_XL_TMS 0x8000 /* Transceiver Mode Switching */
|
||||
|
||||
/*
|
||||
* CAN device statistics
|
||||
@@ -138,6 +144,11 @@ enum {
|
||||
IFLA_CAN_BITRATE_MAX,
|
||||
IFLA_CAN_TDC, /* FD */
|
||||
IFLA_CAN_CTRLMODE_EXT,
|
||||
IFLA_CAN_XL_DATA_BITTIMING,
|
||||
IFLA_CAN_XL_DATA_BITTIMING_CONST,
|
||||
IFLA_CAN_XL_DATA_BITRATE_CONST,
|
||||
IFLA_CAN_XL_TDC,
|
||||
IFLA_CAN_XL_PWM,
|
||||
|
||||
/* add new constants above here */
|
||||
__IFLA_CAN_MAX,
|
||||
@@ -179,6 +190,29 @@ enum {
|
||||
IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1
|
||||
};
|
||||
|
||||
/*
|
||||
* CAN FD/XL Pulse-Width Modulation (PWM)
|
||||
*
|
||||
* Please refer to struct can_pwm_const and can_pwm in
|
||||
* include/linux/can/bittiming.h for further details.
|
||||
*/
|
||||
enum {
|
||||
IFLA_CAN_PWM_UNSPEC,
|
||||
IFLA_CAN_PWM_PWMS_MIN, /* u32 */
|
||||
IFLA_CAN_PWM_PWMS_MAX, /* u32 */
|
||||
IFLA_CAN_PWM_PWML_MIN, /* u32 */
|
||||
IFLA_CAN_PWM_PWML_MAX, /* u32 */
|
||||
IFLA_CAN_PWM_PWMO_MIN, /* u32 */
|
||||
IFLA_CAN_PWM_PWMO_MAX, /* u32 */
|
||||
IFLA_CAN_PWM_PWMS, /* u32 */
|
||||
IFLA_CAN_PWM_PWML, /* u32 */
|
||||
IFLA_CAN_PWM_PWMO, /* u32 */
|
||||
|
||||
/* add new constants above here */
|
||||
__IFLA_CAN_PWM,
|
||||
IFLA_CAN_PWM_MAX = __IFLA_CAN_PWM - 1
|
||||
};
|
||||
|
||||
/* u16 termination range: 1..65535 Ohms */
|
||||
#define CAN_TERMINATION_DISABLED 0
|
||||
|
||||
|
||||
@@ -892,20 +892,58 @@ static void raw_put_canxl_vcid(struct raw_sock *ro, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
|
||||
static inline bool raw_dev_cc_enabled(struct net_device *dev,
|
||||
struct can_priv *priv)
|
||||
{
|
||||
/* Classical CAN -> no checks for flags and device capabilities */
|
||||
if (can_is_can_skb(skb))
|
||||
/* The CANXL-only mode disables error-signalling on the CAN bus
|
||||
* which is needed to send CAN CC/FD frames
|
||||
*/
|
||||
if (priv)
|
||||
return !can_dev_in_xl_only_mode(priv);
|
||||
|
||||
/* virtual CAN interfaces always support CAN CC */
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool raw_dev_fd_enabled(struct net_device *dev,
|
||||
struct can_priv *priv)
|
||||
{
|
||||
/* check FD ctrlmode on real CAN interfaces */
|
||||
if (priv)
|
||||
return (priv->ctrlmode & CAN_CTRLMODE_FD);
|
||||
|
||||
/* check MTU for virtual CAN FD interfaces */
|
||||
return (READ_ONCE(dev->mtu) >= CANFD_MTU);
|
||||
}
|
||||
|
||||
static inline bool raw_dev_xl_enabled(struct net_device *dev,
|
||||
struct can_priv *priv)
|
||||
{
|
||||
/* check XL ctrlmode on real CAN interfaces */
|
||||
if (priv)
|
||||
return (priv->ctrlmode & CAN_CTRLMODE_XL);
|
||||
|
||||
/* check MTU for virtual CAN XL interfaces */
|
||||
return can_is_canxl_dev_mtu(READ_ONCE(dev->mtu));
|
||||
}
|
||||
|
||||
static unsigned int raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = safe_candev_priv(dev);
|
||||
|
||||
/* Classical CAN */
|
||||
if (can_is_can_skb(skb) && raw_dev_cc_enabled(dev, priv))
|
||||
return CAN_MTU;
|
||||
|
||||
/* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
|
||||
/* CAN FD */
|
||||
if (ro->fd_frames && can_is_canfd_skb(skb) &&
|
||||
(mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
|
||||
raw_dev_fd_enabled(dev, priv))
|
||||
return CANFD_MTU;
|
||||
|
||||
/* CAN XL -> needs to be enabled and a CAN XL device */
|
||||
/* CAN XL */
|
||||
if (ro->xl_frames && can_is_canxl_skb(skb) &&
|
||||
can_is_canxl_dev_mtu(mtu))
|
||||
raw_dev_xl_enabled(dev, priv))
|
||||
return CANXL_MTU;
|
||||
|
||||
return 0;
|
||||
@@ -961,7 +999,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
err = -EINVAL;
|
||||
|
||||
/* check for valid CAN (CC/FD/XL) frame content */
|
||||
txmtu = raw_check_txframe(ro, skb, READ_ONCE(dev->mtu));
|
||||
txmtu = raw_check_txframe(ro, skb, dev);
|
||||
if (!txmtu)
|
||||
goto free_skb;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user