Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
[cascardo/linux.git] / drivers / net / usb / asix.c
index 8e7d237..31b7331 100644 (file)
@@ -224,10 +224,9 @@ static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                   cmd, value, index, size);
 
        if (data) {
-               buf = kmalloc(size, GFP_KERNEL);
+               buf = kmemdup(data, size, GFP_KERNEL);
                if (!buf)
                        goto out;
-               memcpy(buf, data, size);
        }
 
        err = usb_control_msg(
@@ -322,8 +321,29 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                /* get the packet length */
                size = (u16) (header & 0x0000ffff);
 
-               if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+               if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
+                       u8 alignment = (u32)skb->data & 0x3;
+                       if (alignment != 0x2) {
+                               /*
+                                * not 16bit aligned so use the room provided by
+                                * the 32 bit header to align the data
+                                *
+                                * note we want 16bit alignment as MAC header is
+                                * 14bytes thus ip header will be aligned on
+                                * 32bit boundary so accessing ipheader elements
+                                * using a cast to struct ip header wont cause
+                                * an unaligned accesses.
+                                */
+                               u8 realignment = (alignment + 2) & 0x3;
+                               memmove(skb->data - realignment,
+                                       skb->data,
+                                       size);
+                               skb->data -= realignment;
+                               skb_set_tail_pointer(skb, size);
+                       }
                        return 2;
+               }
+
                if (size > ETH_FRAME_LEN) {
                        netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
                                   size);
@@ -331,7 +351,18 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                }
                ax_skb = skb_clone(skb, GFP_ATOMIC);
                if (ax_skb) {
+                       u8 alignment = (u32)packet & 0x3;
                        ax_skb->len = size;
+
+                       if (alignment != 0x2) {
+                               /*
+                                * not 16bit aligned use the room provided by
+                                * the 32 bit header to align the data
+                                */
+                               u8 realignment = (alignment + 2) & 0x3;
+                               memmove(packet - realignment, packet, size);
+                               packet -= realignment;
+                       }
                        ax_skb->data = packet;
                        skb_set_tail_pointer(ax_skb, size);
                        usbnet_skb_return(dev, ax_skb);