|
@@ -985,6 +985,7 @@ out:
|
|
|
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)
|
|
|
|
|
@@ -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
|
|
@@ -1026,6 +1031,8 @@ 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);
|
|
@@ -1033,8 +1040,32 @@ static void bfin_mac_rx(struct net_device *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);
|