Skip to content

Commit

Permalink
netdev: bfin_mac: deduce Ethernet FCS from hardware IP payload checksum
Browse files Browse the repository at this point in the history
IP checksum is based on 16-bit one's complement algorithm, so to deduce a
value from checksum is equal to add its complement.

Unfortunately, the Blackfin on-chip MAC checksum logic only works when the
IP packet has a header length of 20 bytes.  This is true for most IPv4
packets, but not for IPv6 packets or IPv4 packets which use header options.
So only use the hardware checksum when appropriate.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Jon Kowal <jon.kowal@dspecialists.de>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Sonic Zhang authored and David S. Miller committed May 18, 2010
1 parent f6e1e4f commit ad2864d
Showing 1 changed file with 33 additions and 2 deletions.
35 changes: 33 additions & 2 deletions drivers/net/bfin_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}

#define IP_HEADER_OFF 0
#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)

Expand All @@ -993,6 +994,10 @@ static void bfin_mac_rx(struct net_device *dev)
struct sk_buff *skb, *new_skb;
unsigned short len;
struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
#if defined(BFIN_MAC_CSUM_OFFLOAD)
unsigned int i;
unsigned char fcs[ETH_FCS_LEN + 1];
#endif

/* check if frame status word reports an error condition
* we which case we simply drop the packet
Expand Down Expand Up @@ -1026,15 +1031,41 @@ static void bfin_mac_rx(struct net_device *dev)
current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;

len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
/* Deduce Ethernet FCS length from Ethernet payload length */
len -= ETH_FCS_LEN;
skb_put(skb, len);

skb->protocol = eth_type_trans(skb, dev);

bfin_rx_hwtstamp(dev, skb);

#if defined(BFIN_MAC_CSUM_OFFLOAD)
skb->csum = current_rx_ptr->status.ip_payload_csum;
skb->ip_summed = CHECKSUM_COMPLETE;
/* Checksum offloading only works for IPv4 packets with the standard IP header
* length of 20 bytes, because the blackfin MAC checksum calculation is
* based on that assumption. We must NOT use the calculated checksum if our
* IP version or header break that assumption.
*/
if (skb->data[IP_HEADER_OFF] == 0x45) {
skb->csum = current_rx_ptr->status.ip_payload_csum;
/*
* Deduce Ethernet FCS from hardware generated IP payload checksum.
* IP checksum is based on 16-bit one's complement algorithm.
* To deduce a value from checksum is equal to add its inversion.
* If the IP payload len is odd, the inversed FCS should also
* begin from odd address and leave first byte zero.
*/
if (skb->len % 2) {
fcs[0] = 0;
for (i = 0; i < ETH_FCS_LEN; i++)
fcs[i + 1] = ~skb->data[skb->len + i];
skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum);
} else {
for (i = 0; i < ETH_FCS_LEN; i++)
fcs[i] = ~skb->data[skb->len + i];
skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum);
}
skb->ip_summed = CHECKSUM_COMPLETE;
}
#endif

netif_rx(skb);
Expand Down

0 comments on commit ad2864d

Please sign in to comment.