/*-------------------------------------------------------------------------*/
 
+static int build_dma_sg(const struct sk_buff *skb, struct urb *urb)
+{
+       unsigned num_sgs, total_len = 0;
+       int i, s = 0;
+
+       num_sgs = skb_shinfo(skb)->nr_frags + 1;
+       if (num_sgs == 1)
+               return 0;
+
+       urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), GFP_ATOMIC);
+       if (!urb->sg)
+               return -ENOMEM;
+
+       urb->num_sgs = num_sgs;
+       sg_init_table(urb->sg, urb->num_sgs);
+
+       sg_set_buf(&urb->sg[s++], skb->data, skb_headlen(skb));
+       total_len += skb_headlen(skb);
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               struct skb_frag_struct *f = &skb_shinfo(skb)->frags[i];
+
+               total_len += skb_frag_size(f);
+               sg_set_page(&urb->sg[i + s], f->page.p, f->size,
+                               f->page_offset);
+       }
+       urb->transfer_buffer_length = total_len;
+
+       return 1;
+}
+
 netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
                                     struct net_device *net)
 {
                        goto drop;
                }
        }
-       length = skb->len;
 
        if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
                netif_dbg(dev, tx_err, dev->net, "no urb\n");
        entry = (struct skb_data *) skb->cb;
        entry->urb = urb;
        entry->dev = dev;
-       entry->length = length;
 
        usb_fill_bulk_urb (urb, dev->udev, dev->out,
                        skb->data, skb->len, tx_complete, skb);
+       if (dev->can_dma_sg) {
+               if (build_dma_sg(skb, urb) < 0)
+                       goto drop;
+       }
+       entry->length = length = urb->transfer_buffer_length;
 
        /* don't assume the hardware handles USB_ZERO_PACKET
         * NOTE:  strictly conforming cdc-ether devices should expect
 not_drop:
                if (skb)
                        dev_kfree_skb_any (skb);
-               usb_free_urb (urb);
+               if (urb) {
+                       kfree(urb->sg);
+                       usb_free_urb(urb);
+               }
        } else
                netif_dbg(dev, tx_queued, dev->net,
                          "> tx, len %d, type 0x%x\n", length, skb->protocol);
                        rx_process (dev, skb);
                        continue;
                case tx_done:
+                       kfree(entry->urb->sg);
                case rx_cleanup:
                        usb_free_urb (entry->urb);
                        dev_kfree_skb (skb);
                        retval = usb_submit_urb(res, GFP_ATOMIC);
                        if (retval < 0) {
                                dev_kfree_skb_any(skb);
+                               kfree(res->sg);
                                usb_free_urb(res);
                                usb_autopm_put_interface_async(dev->intf);
                        } else {