* are bundled into this buffer and where we can find an array of
         * per-packet metadata (which contains elements encoded into u16).
         */
+
+       /* SKB contents for current firmware:
+        *   <packet 1> <padding>
+        *   ...
+        *   <packet N> <padding>
+        *   <per-packet metadata entry 1> <dummy header>
+        *   ...
+        *   <per-packet metadata entry N> <dummy header>
+        *   <padding2> <rx_hdr>
+        *
+        * where:
+        *   <packet N> contains pkt_len bytes:
+        *              2 bytes of IP alignment pseudo header
+        *              packet received
+        *   <per-packet metadata entry N> contains 4 bytes:
+        *              pkt_len and fields AX_RXHDR_*
+        *   <padding>  0-7 bytes to terminate at
+        *              8 bytes boundary (64-bit).
+        *   <padding2> 4 bytes to make rx_hdr terminate at
+        *              8 bytes boundary (64-bit)
+        *   <dummy-header> contains 4 bytes:
+        *              pkt_len=0 and AX_RXHDR_DROP_ERR
+        *   <rx-hdr>   contains 4 bytes:
+        *              pkt_cnt and hdr_off (offset of
+        *                <per-packet metadata entry 1>)
+        *
+        * pkt_cnt is number of entrys in the per-packet metadata.
+        * In current firmware there is 2 entrys per packet.
+        * The first points to the packet and the
+        *  second is a dummy header.
+        * This was done probably to align fields in 64-bit and
+        *  maintain compatibility with old firmware.
+        * This code assumes that <dummy header> and <padding2> are
+        *  optional.
+        */
+
        if (skb->len < 4)
                return 0;
        skb_trim(skb, skb->len - 4);
        /* Make sure that the bounds of the metadata array are inside the SKB
         * (and in front of the counter at the end).
         */
-       if (pkt_cnt * 2 + hdr_off > skb->len)
+       if (pkt_cnt * 4 + hdr_off > skb->len)
                return 0;
        pkt_hdr = (u32 *)(skb->data + hdr_off);
 
        /* Packets must not overlap the metadata array */
        skb_trim(skb, hdr_off);
 
-       for (; ; pkt_cnt--, pkt_hdr++) {
+       for (; pkt_cnt > 0; pkt_cnt--, pkt_hdr++) {
+               u16 pkt_len_plus_padd;
                u16 pkt_len;
 
                le32_to_cpus(pkt_hdr);
                pkt_len = (*pkt_hdr >> 16) & 0x1fff;
+               pkt_len_plus_padd = (pkt_len + 7) & 0xfff8;
 
-               if (pkt_len > skb->len)
+               /* Skip dummy header used for alignment
+                */
+               if (pkt_len == 0)
+                       continue;
+
+               if (pkt_len_plus_padd > skb->len)
                        return 0;
 
                /* Check CRC or runt packet */
-               if (((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) == 0) &&
-                   pkt_len >= 2 + ETH_HLEN) {
-                       bool last = (pkt_cnt == 0);
-
-                       if (last) {
-                               ax_skb = skb;
-                       } else {
-                               ax_skb = skb_clone(skb, GFP_ATOMIC);
-                               if (!ax_skb)
-                                       return 0;
-                       }
-                       ax_skb->len = pkt_len;
-                       /* Skip IP alignment pseudo header */
-                       skb_pull(ax_skb, 2);
-                       skb_set_tail_pointer(ax_skb, ax_skb->len);
-                       ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
-                       ax88179_rx_checksum(ax_skb, pkt_hdr);
+               if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) ||
+                   pkt_len < 2 + ETH_HLEN) {
+                       dev->net->stats.rx_errors++;
+                       skb_pull(skb, pkt_len_plus_padd);
+                       continue;
+               }
 
-                       if (last)
-                               return 1;
+               /* last packet */
+               if (pkt_len_plus_padd == skb->len) {
+                       skb_trim(skb, pkt_len);
 
-                       usbnet_skb_return(dev, ax_skb);
+                       /* Skip IP alignment pseudo header */
+                       skb_pull(skb, 2);
+
+                       skb->truesize = SKB_TRUESIZE(pkt_len_plus_padd);
+                       ax88179_rx_checksum(skb, pkt_hdr);
+                       return 1;
                }
 
-               /* Trim this packet away from the SKB */
-               if (!skb_pull(skb, (pkt_len + 7) & 0xFFF8))
+               ax_skb = skb_clone(skb, GFP_ATOMIC);
+               if (!ax_skb)
                        return 0;
+               skb_trim(ax_skb, pkt_len);
+
+               /* Skip IP alignment pseudo header */
+               skb_pull(ax_skb, 2);
+
+               skb->truesize = pkt_len_plus_padd +
+                               SKB_DATA_ALIGN(sizeof(struct sk_buff));
+               ax88179_rx_checksum(ax_skb, pkt_hdr);
+               usbnet_skb_return(dev, ax_skb);
+
+               skb_pull(skb, pkt_len_plus_padd);
        }
+
+       return 0;
 }
 
 static struct sk_buff *