[RFC PATCH v3 8/9] can: slcan: add support to set bit time register (btr)

From: Dario Binacchi
Date: Tue Jul 26 2022 - 17:04:13 EST


It allows to set the bit time register with tunable values.
The setting can only be changed if the interface is down:

ip link set dev can0 down
ethtool --set-tunable can0 can-btr 0x31c
ip link set dev can0 up

Suggested-by: Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx>
Signed-off-by: Dario Binacchi <dario.binacchi@xxxxxxxxxxxxxxxxxxxx>
---

(no changes since v1)

drivers/net/can/slcan/slcan-core.c | 58 ++++++++++++++++++++-------
drivers/net/can/slcan/slcan-ethtool.c | 13 ++++++
drivers/net/can/slcan/slcan.h | 1 +
3 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/drivers/net/can/slcan/slcan-core.c b/drivers/net/can/slcan/slcan-core.c
index 45e521910236..3905f21e7788 100644
--- a/drivers/net/can/slcan/slcan-core.c
+++ b/drivers/net/can/slcan/slcan-core.c
@@ -99,6 +99,7 @@ struct slcan {
#define CF_ERR_RST 0 /* Reset errors on open */
wait_queue_head_t xcmd_wait; /* Wait queue for commands */
/* transmission */
+ u32 btr; /* bit timing register */
};

static const u32 slcan_bitrate_const[] = {
@@ -128,6 +129,17 @@ int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on)
return 0;
}

+int slcan_set_btr(struct net_device *ndev, u32 btr)
+{
+ struct slcan *sl = netdev_priv(ndev);
+
+ if (netif_running(ndev))
+ return -EBUSY;
+
+ sl->btr = btr;
+ return 0;
+}
+
/*************************************************************************
* SLCAN ENCAPSULATION FORMAT *
*************************************************************************/
@@ -699,22 +711,40 @@ static int slcan_netdev_open(struct net_device *dev)
return err;
}

- if (sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN) {
- for (s = 0; s < ARRAY_SIZE(slcan_bitrate_const); s++) {
- if (sl->can.bittiming.bitrate == slcan_bitrate_const[s])
- break;
+ if (sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN || sl->btr) {
+ if (sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN) {
+ for (s = 0; s < ARRAY_SIZE(slcan_bitrate_const); s++) {
+ if (sl->can.bittiming.bitrate ==
+ slcan_bitrate_const[s])
+ break;
+ }
+
+ /* The CAN framework has already validate the bitrate
+ * value, so we can avoid to check if `s' has been
+ * properly set.
+ */
+ snprintf(cmd, sizeof(cmd), "C\rS%d\r", s);
+ err = slcan_transmit_cmd(sl, cmd);
+ if (err) {
+ netdev_err(dev,
+ "failed to send bitrate command 'C\\rS%d\\r'\n",
+ s);
+ goto cmd_transmit_failed;
+ }
}

- /* The CAN framework has already validate the bitrate value,
- * so we can avoid to check if `s' has been properly set.
- */
- snprintf(cmd, sizeof(cmd), "C\rS%d\r", s);
- err = slcan_transmit_cmd(sl, cmd);
- if (err) {
- netdev_err(dev,
- "failed to send bitrate command 'C\\rS%d\\r'\n",
- s);
- goto cmd_transmit_failed;
+ if (sl->btr) {
+ u32 btr = sl->btr & GENMASK(15, 0);
+
+ snprintf(cmd, sizeof(cmd), "C\rs%04x\r", btr);
+ err = slcan_transmit_cmd(sl, cmd);
+ if (err) {
+ netdev_err(dev,
+ "failed to send bit timing command 'C\\rs%04x\\r'\n",
+ btr);
+ goto cmd_transmit_failed;
+ }
+
}

if (test_bit(CF_ERR_RST, &sl->cmd_flags)) {
diff --git a/drivers/net/can/slcan/slcan-ethtool.c b/drivers/net/can/slcan/slcan-ethtool.c
index bf0afdc4e49d..8e2e77bbffda 100644
--- a/drivers/net/can/slcan/slcan-ethtool.c
+++ b/drivers/net/can/slcan/slcan-ethtool.c
@@ -52,11 +52,24 @@ static int slcan_get_sset_count(struct net_device *netdev, int sset)
}
}

+static int slcan_set_tunable(struct net_device *netdev,
+ const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ switch (tuna->id) {
+ case ETHTOOL_CAN_BTR:
+ return slcan_set_btr(netdev, *(u32 *)data);
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct ethtool_ops slcan_ethtool_ops = {
.get_strings = slcan_get_strings,
.get_priv_flags = slcan_get_priv_flags,
.set_priv_flags = slcan_set_priv_flags,
.get_sset_count = slcan_get_sset_count,
+ .set_tunable = slcan_set_tunable,
};

void slcan_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/can/slcan/slcan.h b/drivers/net/can/slcan/slcan.h
index d463c8d99e22..1ac412fe8c95 100644
--- a/drivers/net/can/slcan/slcan.h
+++ b/drivers/net/can/slcan/slcan.h
@@ -13,6 +13,7 @@

bool slcan_err_rst_on_open(struct net_device *ndev);
int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on);
+int slcan_set_btr(struct net_device *ndev, u32 btr);
void slcan_set_ethtool_ops(struct net_device *ndev);

#endif /* _SLCAN_H */
--
2.32.0