aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/bfin_mac.c
diff options
context:
space:
mode:
authorSonic Zhang <sonic.zhang@analog.com>2010-05-10 01:39:09 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-17 20:20:58 -0400
commitad2864d88718714d8b347b6209b07abb2ecd3a49 (patch)
treeb6381e4fb49436c3032d7e13f02416db047a67fb /drivers/net/bfin_mac.c
parentf6e1e4f3e511589dd0c47d42b870501659e7195f (diff)
netdev: bfin_mac: deduce Ethernet FCS from hardware IP payload checksum
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>
Diffstat (limited to 'drivers/net/bfin_mac.c')
-rw-r--r--drivers/net/bfin_mac.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index 2b364ba6b62e..9e010d69f34a 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -985,6 +985,7 @@ out:
985 return NETDEV_TX_OK; 985 return NETDEV_TX_OK;
986} 986}
987 987
988#define IP_HEADER_OFF 0
988#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \ 989#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
989 RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE) 990 RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
990 991
@@ -993,6 +994,10 @@ static void bfin_mac_rx(struct net_device *dev)
993 struct sk_buff *skb, *new_skb; 994 struct sk_buff *skb, *new_skb;
994 unsigned short len; 995 unsigned short len;
995 struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev); 996 struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
997#if defined(BFIN_MAC_CSUM_OFFLOAD)
998 unsigned int i;
999 unsigned char fcs[ETH_FCS_LEN + 1];
1000#endif
996 1001
997 /* check if frame status word reports an error condition 1002 /* check if frame status word reports an error condition
998 * we which case we simply drop the packet 1003 * we which case we simply drop the packet
@@ -1026,6 +1031,8 @@ static void bfin_mac_rx(struct net_device *dev)
1026 current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2; 1031 current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
1027 1032
1028 len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN); 1033 len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
1034 /* Deduce Ethernet FCS length from Ethernet payload length */
1035 len -= ETH_FCS_LEN;
1029 skb_put(skb, len); 1036 skb_put(skb, len);
1030 1037
1031 skb->protocol = eth_type_trans(skb, dev); 1038 skb->protocol = eth_type_trans(skb, dev);
@@ -1033,8 +1040,32 @@ static void bfin_mac_rx(struct net_device *dev)
1033 bfin_rx_hwtstamp(dev, skb); 1040 bfin_rx_hwtstamp(dev, skb);
1034 1041
1035#if defined(BFIN_MAC_CSUM_OFFLOAD) 1042#if defined(BFIN_MAC_CSUM_OFFLOAD)
1036 skb->csum = current_rx_ptr->status.ip_payload_csum; 1043 /* Checksum offloading only works for IPv4 packets with the standard IP header
1037 skb->ip_summed = CHECKSUM_COMPLETE; 1044 * length of 20 bytes, because the blackfin MAC checksum calculation is
1045 * based on that assumption. We must NOT use the calculated checksum if our
1046 * IP version or header break that assumption.
1047 */
1048 if (skb->data[IP_HEADER_OFF] == 0x45) {
1049 skb->csum = current_rx_ptr->status.ip_payload_csum;
1050 /*
1051 * Deduce Ethernet FCS from hardware generated IP payload checksum.
1052 * IP checksum is based on 16-bit one's complement algorithm.
1053 * To deduce a value from checksum is equal to add its inversion.
1054 * If the IP payload len is odd, the inversed FCS should also
1055 * begin from odd address and leave first byte zero.
1056 */
1057 if (skb->len % 2) {
1058 fcs[0] = 0;
1059 for (i = 0; i < ETH_FCS_LEN; i++)
1060 fcs[i + 1] = ~skb->data[skb->len + i];
1061 skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum);
1062 } else {
1063 for (i = 0; i < ETH_FCS_LEN; i++)
1064 fcs[i] = ~skb->data[skb->len + i];
1065 skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum);
1066 }
1067 skb->ip_summed = CHECKSUM_COMPLETE;
1068 }
1038#endif 1069#endif
1039 1070
1040 netif_rx(skb); 1071 netif_rx(skb);