unsigned long destination, status;
        int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
        int len = urb->transfer_buffer_length;
-       dma_addr_t data = urb->transfer_dma;
+       int this_sg_len;
+       dma_addr_t data;
        __le32 *plink;
        struct urb_priv *urbp = urb->hcpriv;
        unsigned int toggle;
+       struct scatterlist  *sg;
+       int i;
 
        if (len < 0)
                return -EINVAL;
        if (usb_pipein(urb->pipe))
                status |= TD_CTRL_SPD;
 
+       i = urb->num_sgs;
+       if (len > 0 && i > 0) {
+               sg = urb->sg;
+               data = sg_dma_address(sg);
+
+               /* urb->transfer_buffer_length may be smaller than the
+                * size of the scatterlist (or vice versa)
+                */
+               this_sg_len = min_t(int, sg_dma_len(sg), len);
+       } else {
+               sg = NULL;
+               data = urb->transfer_dma;
+               this_sg_len = len;
+       }
        /*
         * Build the DATA TDs
         */
        plink = NULL;
        td = qh->dummy_td;
-       do {    /* Allow zero length packets */
+       for (;;) {      /* Allow zero length packets */
                int pktsze = maxsze;
 
                if (len <= pktsze) {            /* The last packet */
                plink = &td->link;
                status |= TD_CTRL_ACTIVE;
 
+               toggle ^= 1;
                data += pktsze;
+               this_sg_len -= pktsze;
                len -= maxsze;
-               toggle ^= 1;
-       } while (len > 0);
+               if (this_sg_len <= 0) {
+                       if (--i <= 0 || len <= 0)
+                               break;
+                       sg = sg_next(sg);
+                       data = sg_dma_address(sg);
+                       this_sg_len = min_t(int, sg_dma_len(sg), len);
+               }
+       }
 
        /*
         * URB_ZERO_PACKET means adding a 0-length packet, if direction