Commit c337f103 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde
Browse files

Merge patch series "can: support CAN XL"

Oliver Hartkopp <socketcan@hartkopp.net> says:

The CAN with eXtended data Length (CAN XL) is a new CAN protocol with
a 10Mbit/s data transfer with a new physical layer transceiver (for
this data section). CAN XL allows up to 2048 byte of payload and
shares the arbitration principle (11 bit priority) known from
Classical CAN and CAN FD. RTR and 29 bit identifiers are not
implemented in CAN XL.

A short introduction to CAN XL can be found here:
https://www.can-cia.org/can-knowledge/can/can-xl/
https://github.com/linux-can/can-doc/blob/master/presentations/CAN-XL-Intro.pdf

V1: https://lore.kernel.org/all/20220711183426.96446-1-socketcan@hartkopp.net

V2: Major rework after discussion and feedback on Linux-CAN ML
    https://lore.kernel.org/all/20220714160541.2071-1-socketcan@hartkopp.net

- rework of struct canxl_frame
- CANXL_XLF flag is now the switch between CAN XL and CAN/CANFD
- variable length in r/w operations for CAN XL frames
- write CAN XL frame to raw socket enforces size <-> canxl_frame.len sync

V3: Fix length for CAN XL frames inside the sk_buff
    https://lore.kernel.org/all/20220717132730.30295-1-socketcan@hartkopp.net

- extend the CAN_RAW sockopt to handle fixed/truncated read/write operations

V4: Fix patch 5 (can: raw: add CAN XL support)
    https://lore.kernel.org/all/20220719054204.29061-1-socketcan@hartkopp.net

- fix return value (move 'err = -EINVAL' in raw_sendmsg())
- add CAN XL frame handling in can_rcv()
- change comment for CAN_RAW_XL_[RT]X_DYN definition (allow -> enable)

V5: Remove CAN_RAW_XL_[RT]X_DYN definition again
    https://lore.kernel.org/all/20220719112748.3281-1-socketcan@hartkopp.net

- CAN_RAW_XL_[RT]X_DYN (truncated data) feature is now enabled by default
- use CANXL_MIN_DLEN instead of '1' in canxl_frame definition
- add missing 'err = -EINVAL' initialization in raw_sendmsg())

V6: https://lore.kernel.org/all/20220724074402.117394-1-socketcan@hartkopp.net

- rework an separate skb identification and length helpers
- add CANFD_FDF flag in all CAN FD frame structures
- simplify patches for infrastructure and raw sockets
- add vxcan support in virtual CAN interface patch

V7: https://lore.kernel.org/all/20220729154107.1875-1-socketcan@hartkopp.net

- fixed indention as remarked by Marc
- set CANFD_FDF flag when detecting CAN FD frames generated by PF_PACKET
- Allow to use variable CAN XL MTU sizes to enforce real time requirements
  on CAN XL segments (e.g. to support of CAN CiA segmentation concept)

V8: https://lore.kernel.org/all/20220801190010.3344-1-socketcan@hartkopp.net

- fixed typo as remarked by Vincent
- rebased to latest can-next/net-next tree

V9: https://lore.kernel.org/all/20220912170725.120748-1-socketcan@hartkopp.net

- rebased to latest can-next/net-next tree
- updated and extended public available CAN XL documenatation in cover letter
- renamed struct canxl_frame variable cfx to cxl as suggested by Vincent
- Added Acked-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr> tag

Link: https://lore.kernel.org/all/20220912170725.120748-1-socketcan@hartkopp.net


Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parents 1c679f91 62633269
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -657,7 +657,6 @@ static void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *c
		cf->can_id = (idw >> 18) & CAN_SFF_MASK;

	/* BRS, ESI, RTR Flags */
	cf->flags = 0;
	if (FIELD_GET(REG_FRAME_FORMAT_W_FDF, ffw)) {
		if (FIELD_GET(REG_FRAME_FORMAT_W_BRS, ffw))
			cf->flags |= CANFD_BRS;
+1 −1
Original line number Diff line number Diff line
@@ -247,7 +247,7 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
	struct net_device *dev = offload->dev;
	struct net_device_stats *stats = &dev->stats;
	struct sk_buff *skb;
	u8 len;
	unsigned int len;
	int err;

	skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
+79 −34
Original line number Diff line number Diff line
@@ -91,8 +91,8 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
EXPORT_SYMBOL_GPL(can_put_echo_skb);

struct sk_buff *
__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr,
		   unsigned int *frame_len_ptr)
__can_get_echo_skb(struct net_device *dev, unsigned int idx,
		   unsigned int *len_ptr, unsigned int *frame_len_ptr)
{
	struct can_priv *priv = netdev_priv(dev);

@@ -108,16 +108,12 @@ __can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr,
		 */
		struct sk_buff *skb = priv->echo_skb[idx];
		struct can_skb_priv *can_skb_priv = can_skb_prv(skb);
		struct canfd_frame *cf = (struct canfd_frame *)skb->data;

		if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)
			skb_tstamp_tx(skb, skb_hwtstamps(skb));

		/* get the real payload length for netdev statistics */
		if (cf->can_id & CAN_RTR_FLAG)
			*len_ptr = 0;
		else
			*len_ptr = cf->len;
		*len_ptr = can_skb_get_data_len(skb);

		if (frame_len_ptr)
			*frame_len_ptr = can_skb_priv->frame_len;
@@ -147,7 +143,7 @@ unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx,
			      unsigned int *frame_len_ptr)
{
	struct sk_buff *skb;
	u8 len;
	unsigned int len;

	skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
	if (!skb)
@@ -191,6 +187,20 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx,
}
EXPORT_SYMBOL_GPL(can_free_echo_skb);

/* fill common values for CAN sk_buffs */
static void init_can_skb_reserve(struct sk_buff *skb)
{
	skb->pkt_type = PACKET_BROADCAST;
	skb->ip_summed = CHECKSUM_UNNECESSARY;

	skb_reset_mac_header(skb);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);

	can_skb_reserve(skb);
	can_skb_prv(skb)->skbcnt = 0;
}

struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
{
	struct sk_buff *skb;
@@ -204,16 +214,8 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
	}

	skb->protocol = htons(ETH_P_CAN);
	skb->pkt_type = PACKET_BROADCAST;
	skb->ip_summed = CHECKSUM_UNNECESSARY;

	skb_reset_mac_header(skb);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);

	can_skb_reserve(skb);
	init_can_skb_reserve(skb);
	can_skb_prv(skb)->ifindex = dev->ifindex;
	can_skb_prv(skb)->skbcnt = 0;

	*cf = skb_put_zero(skb, sizeof(struct can_frame));

@@ -235,23 +237,51 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
	}

	skb->protocol = htons(ETH_P_CANFD);
	skb->pkt_type = PACKET_BROADCAST;
	skb->ip_summed = CHECKSUM_UNNECESSARY;

	skb_reset_mac_header(skb);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);

	can_skb_reserve(skb);
	init_can_skb_reserve(skb);
	can_skb_prv(skb)->ifindex = dev->ifindex;
	can_skb_prv(skb)->skbcnt = 0;

	*cfd = skb_put_zero(skb, sizeof(struct canfd_frame));

	/* set CAN FD flag by default */
	(*cfd)->flags = CANFD_FDF;

	return skb;
}
EXPORT_SYMBOL_GPL(alloc_canfd_skb);

struct sk_buff *alloc_canxl_skb(struct net_device *dev,
				struct canxl_frame **cxl,
				unsigned int data_len)
{
	struct sk_buff *skb;

	if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN)
		goto out_error;

	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
			       CANXL_HDR_SIZE + data_len);
	if (unlikely(!skb))
		goto out_error;

	skb->protocol = htons(ETH_P_CANXL);
	init_can_skb_reserve(skb);
	can_skb_prv(skb)->ifindex = dev->ifindex;

	*cxl = skb_put_zero(skb, CANXL_HDR_SIZE + data_len);

	/* set CAN XL flag and length information by default */
	(*cxl)->flags = CANXL_XLF;
	(*cxl)->len = data_len;

	return skb;

out_error:
	*cxl = NULL;

	return NULL;
}
EXPORT_SYMBOL_GPL(alloc_canxl_skb);

struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
{
	struct sk_buff *skb;
@@ -291,6 +321,14 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
		skb_reset_mac_header(skb);
		skb_reset_network_header(skb);
		skb_reset_transport_header(skb);

		/* set CANFD_FDF flag for CAN FD frames */
		if (can_is_canfd_skb(skb)) {
			struct canfd_frame *cfd;

			cfd = (struct canfd_frame *)skb->data;
			cfd->flags |= CANFD_FDF;
		}
	}

	return true;
@@ -299,18 +337,25 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
/* Drop a given socketbuffer if it does not contain a valid CAN frame. */
bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
{
	const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
	struct can_priv *priv = netdev_priv(dev);

	if (skb->protocol == htons(ETH_P_CAN)) {
		if (unlikely(skb->len != CAN_MTU ||
			     cfd->len > CAN_MAX_DLEN))
	switch (ntohs(skb->protocol)) {
	case ETH_P_CAN:
		if (!can_is_can_skb(skb))
			goto inval_skb;
	} else if (skb->protocol == htons(ETH_P_CANFD)) {
		if (unlikely(skb->len != CANFD_MTU ||
			     cfd->len > CANFD_MAX_DLEN))
		break;

	case ETH_P_CANFD:
		if (!can_is_canfd_skb(skb))
			goto inval_skb;
	} else {
		break;

	case ETH_P_CANXL:
		if (!can_is_canxl_skb(skb))
			goto inval_skb;
		break;

	default:
		goto inval_skb;
	}

+6 −6
Original line number Diff line number Diff line
@@ -71,11 +71,10 @@ MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");

static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
{
	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
	struct net_device_stats *stats = &dev->stats;

	stats->rx_packets++;
	stats->rx_bytes += cfd->len;
	stats->rx_bytes += can_skb_get_data_len(skb);

	skb->pkt_type  = PACKET_BROADCAST;
	skb->dev       = dev;
@@ -86,14 +85,14 @@ static void vcan_rx(struct sk_buff *skb, struct net_device *dev)

static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev)
{
	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
	struct net_device_stats *stats = &dev->stats;
	int loop, len;
	unsigned int len;
	int loop;

	if (can_dropped_invalid_skb(dev, skb))
		return NETDEV_TX_OK;

	len = cfd->can_id & CAN_RTR_FLAG ? 0 : cfd->len;
	len = can_skb_get_data_len(skb);
	stats->tx_packets++;
	stats->tx_bytes += len;

@@ -137,7 +136,8 @@ static int vcan_change_mtu(struct net_device *dev, int new_mtu)
	if (dev->flags & IFF_UP)
		return -EBUSY;

	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU)
	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU &&
	    !can_is_canxl_dev_mtu(new_mtu))
		return -EINVAL;

	dev->mtu = new_mtu;
+4 −4
Original line number Diff line number Diff line
@@ -38,10 +38,9 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
{
	struct vxcan_priv *priv = netdev_priv(dev);
	struct net_device *peer;
	struct canfd_frame *cfd = (struct canfd_frame *)oskb->data;
	struct net_device_stats *peerstats, *srcstats = &dev->stats;
	struct sk_buff *skb;
	u8 len;
	unsigned int len;

	if (can_dropped_invalid_skb(dev, oskb))
		return NETDEV_TX_OK;
@@ -70,7 +69,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
	skb->dev        = peer;
	skb->ip_summed  = CHECKSUM_UNNECESSARY;

	len = cfd->can_id & CAN_RTR_FLAG ? 0 : cfd->len;
	len = can_skb_get_data_len(skb);
	if (netif_rx(skb) == NET_RX_SUCCESS) {
		srcstats->tx_packets++;
		srcstats->tx_bytes += len;
@@ -132,7 +131,8 @@ static int vxcan_change_mtu(struct net_device *dev, int new_mtu)
	if (dev->flags & IFF_UP)
		return -EBUSY;

	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU)
	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU &&
	    !can_is_canxl_dev_mtu(new_mtu))
		return -EINVAL;

	dev->mtu = new_mtu;
Loading