Commit 8ba09bfa authored by Vincent Mailhol's avatar Vincent Mailhol Committed by Marc Kleine-Budde
Browse files

can: peak_canfd: advertise timestamping capabilities and add ioctl support

Currently, userland has no method to query which timestamping features
are supported by the peak_canfd driver (aside maybe of getting RX
messages and observe whether or not hardware timestamps stay at zero).

The canonical way to add hardware timestamp support is to implement
ethtool_ops::get_ts_info() in order to advertise the timestamping
capabilities and to implement net_device_ops::ndo_eth_ioctl() as
requested in [1]. Currently, the driver only supports hardware RX
timestamps [2] but not hardware TX. For this reason, the generic
function can_ethtool_op_get_ts_info_hwts() and can_eth_ioctl_hwts()
can not be reused and instead this patch adds peak_get_ts_info() and
peak_eth_ioctl().

[1] kernel doc Timestamping, section 3.1: "Hardware Timestamping
Implementation: Device Drivers"
Link: https://docs.kernel.org/networking/timestamping.html#hardware-timestamping-implementation-device-drivers

[2] https://lore.kernel.org/linux-can/20220727084257.brcbbf7lksoeekbr@pengutronix.de/



CC: Stephane Grosjean <s.grosjean@peak-system.com>
Signed-off-by: default avatarVincent Mailhol <mailhol.vincent@wanadoo.fr>
Link: https://lore.kernel.org/all/20220727101641.198847-14-mailhol.vincent@wanadoo.fr


Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 1d5eeda2
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/ethtool.h>

#include "peak_canfd_user.h"

@@ -742,13 +743,59 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
	return NETDEV_TX_OK;
}

static int peak_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
	struct hwtstamp_config hwts_cfg = { 0 };

	switch (cmd) {
	case SIOCSHWTSTAMP: /* set */
		if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg)))
			return -EFAULT;
		if (hwts_cfg.tx_type == HWTSTAMP_TX_OFF &&
		    hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL)
			return 0;
		return -ERANGE;

	case SIOCGHWTSTAMP: /* get */
		hwts_cfg.tx_type = HWTSTAMP_TX_OFF;
		hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
		if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg)))
			return -EFAULT;
		return 0;

	default:
		return -EOPNOTSUPP;
	}
}

static const struct net_device_ops peak_canfd_netdev_ops = {
	.ndo_open = peak_canfd_open,
	.ndo_stop = peak_canfd_close,
	.ndo_eth_ioctl = peak_eth_ioctl,
	.ndo_start_xmit = peak_canfd_start_xmit,
	.ndo_change_mtu = can_change_mtu,
};

static int peak_get_ts_info(struct net_device *dev,
			    struct ethtool_ts_info *info)
{
	info->so_timestamping =
		SOF_TIMESTAMPING_TX_SOFTWARE |
		SOF_TIMESTAMPING_RX_SOFTWARE |
		SOF_TIMESTAMPING_SOFTWARE |
		SOF_TIMESTAMPING_RX_HARDWARE |
		SOF_TIMESTAMPING_RAW_HARDWARE;
	info->phc_index = -1;
	info->tx_types = BIT(HWTSTAMP_TX_OFF);
	info->rx_filters = BIT(HWTSTAMP_FILTER_ALL);

	return 0;
}

static const struct ethtool_ops peak_canfd_ethtool_ops = {
	.get_ts_info = peak_get_ts_info,
};

struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index,
					int echo_skb_max)
{
@@ -789,6 +836,7 @@ struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index,

	ndev->flags |= IFF_ECHO;
	ndev->netdev_ops = &peak_canfd_netdev_ops;
	ndev->ethtool_ops = &peak_canfd_ethtool_ops;
	ndev->dev_id = index;

	return ndev;