Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7cd606f authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by David S. Miller
Browse files

can: Fix data length code handling in rx path



A valid CAN dataframe can have a data length code (DLC) of 0 .. 8 data bytes.

When reading the CAN controllers register the 4-bit value may contain values
from 0 .. 15 which may exceed the reserved space in the socket buffer!

The ISO 11898-1 Chapter 8.4.2.3 (DLC field) says that register values > 8
should be reduced to 8 without any error reporting or frame drop.

This patch introduces a new helper macro to cast a given 4-bit data length
code (dlc) to __u8 and ensure the DLC value to be max. 8 bytes.

The different handlings in the rx path of the CAN netdevice drivers are fixed.

Signed-off-by: default avatarOliver Hartkopp <oliver@hartkopp.net>
Signed-off-by: default avatarWolfgang Grandegger <wg@grandegger.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d90a909e
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -474,7 +474,7 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb,
	reg_msr = at91_read(priv, AT91_MSR(mb));
	reg_msr = at91_read(priv, AT91_MSR(mb));
	if (reg_msr & AT91_MSR_MRTR)
	if (reg_msr & AT91_MSR_MRTR)
		cf->can_id |= CAN_RTR_FLAG;
		cf->can_id |= CAN_RTR_FLAG;
	cf->can_dlc = min_t(__u8, (reg_msr >> 16) & 0xf, 8);
	cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf);


	*(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb));
	*(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb));
	*(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb));
	*(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb));
+1 −1
Original line number Original line Diff line number Diff line
@@ -392,7 +392,7 @@ static void bfin_can_rx(struct net_device *dev, u16 isrc)
		cf->can_id |= CAN_RTR_FLAG;
		cf->can_id |= CAN_RTR_FLAG;


	/* get data length code */
	/* get data length code */
	cf->can_dlc = bfin_read16(&reg->chl[obj].dlc);
	cf->can_dlc = get_can_dlc(bfin_read16(&reg->chl[obj].dlc) & 0xF);


	/* get payload */
	/* get payload */
	for (i = 0; i < 8; i += 2) {
	for (i = 0; i < 8; i += 2) {
+3 −10
Original line number Original line Diff line number Diff line
@@ -403,9 +403,8 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,


		for (i = 1; i < RXBDAT_OFF; i++)
		for (i = 1; i < RXBDAT_OFF; i++)
			buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
			buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
		len = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK;

		if (len > 8)
		len = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK);
			len = 8;
		for (; i < (RXBDAT_OFF + len); i++)
		for (; i < (RXBDAT_OFF + len); i++)
			buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
			buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
	} else {
	} else {
@@ -455,13 +454,7 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
			(buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT);
			(buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT);
	}
	}
	/* Data length */
	/* Data length */
	frame->can_dlc = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK;
	frame->can_dlc = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK);
	if (frame->can_dlc > 8) {
		dev_warn(&spi->dev, "invalid frame recevied\n");
		priv->net->stats.rx_errors++;
		dev_kfree_skb(skb);
		return;
	}
	memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc);
	memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc);


	priv->net->stats.rx_packets++;
	priv->net->stats.rx_packets++;
+2 −1
Original line number Original line Diff line number Diff line
@@ -297,7 +297,8 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
	frame->can_id |= can_id >> 1;
	frame->can_id |= can_id >> 1;
	if (can_id & 1)
	if (can_id & 1)
		frame->can_id |= CAN_RTR_FLAG;
		frame->can_id |= CAN_RTR_FLAG;
	frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;

	frame->can_dlc = get_can_dlc(in_8(&regs->rx.dlr) & 0xf);


	if (!(frame->can_id & CAN_RTR_FLAG)) {
	if (!(frame->can_id & CAN_RTR_FLAG)) {
		void __iomem *data = &regs->rx.dsr1_0;
		void __iomem *data = &regs->rx.dsr1_0;
+8 −10
Original line number Original line Diff line number Diff line
@@ -293,15 +293,14 @@ static void sja1000_rx(struct net_device *dev)
	uint8_t fi;
	uint8_t fi;
	uint8_t dreg;
	uint8_t dreg;
	canid_t id;
	canid_t id;
	uint8_t dlc;
	int i;
	int i;


	/* create zero'ed CAN frame buffer */
	skb = alloc_can_skb(dev, &cf);
	skb = alloc_can_skb(dev, &cf);
	if (skb == NULL)
	if (skb == NULL)
		return;
		return;


	fi = priv->read_reg(priv, REG_FI);
	fi = priv->read_reg(priv, REG_FI);
	dlc = fi & 0x0F;


	if (fi & FI_FF) {
	if (fi & FI_FF) {
		/* extended frame format (EFF) */
		/* extended frame format (EFF) */
@@ -318,16 +317,15 @@ static void sja1000_rx(struct net_device *dev)
		    | (priv->read_reg(priv, REG_ID2) >> 5);
		    | (priv->read_reg(priv, REG_ID2) >> 5);
	}
	}


	if (fi & FI_RTR)
	if (fi & FI_RTR) {
		id |= CAN_RTR_FLAG;
		id |= CAN_RTR_FLAG;

	} else {
	cf->can_id = id;
		cf->can_dlc = get_can_dlc(fi & 0x0F);
	cf->can_dlc = dlc;
		for (i = 0; i < cf->can_dlc; i++)
	for (i = 0; i < dlc; i++)
			cf->data[i] = priv->read_reg(priv, dreg++);
			cf->data[i] = priv->read_reg(priv, dreg++);
	}


	while (i < 8)
	cf->can_id = id;
		cf->data[i++] = 0;


	/* release receive buffer */
	/* release receive buffer */
	priv->write_reg(priv, REG_CMR, CMD_RRB);
	priv->write_reg(priv, REG_CMR, CMD_RRB);
@@ -335,7 +333,7 @@ static void sja1000_rx(struct net_device *dev)
	netif_rx(skb);
	netif_rx(skb);


	stats->rx_packets++;
	stats->rx_packets++;
	stats->rx_bytes += dlc;
	stats->rx_bytes += cf->can_dlc;
}
}


static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
Loading