This patch adds sg support to dummy_hcd. It seems that uas is not able to work with a hcd which does not support sg only based transfers. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/dummy_hcd.c | 80 ++++++++++++++++++++++++++++++++++++--- 1 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 670ee24..92c04f6 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -39,6 +39,7 @@ #include <linux/usb.h> #include <linux/usb/gadget.h> #include <linux/usb/hcd.h> +#include <linux/scatterlist.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -1078,9 +1079,10 @@ static int dummy_urb_enqueue ( unsigned long flags; int rc; - if (!urb->transfer_buffer && urb->transfer_buffer_length) - return -EINVAL; - + if (!urb->transfer_buffer && urb->transfer_buffer_length) { + if (!urb->num_sgs) + return -EINVAL; + } urbp = kmalloc (sizeof *urbp, mem_flags); if (!urbp) return -ENOMEM; @@ -1139,16 +1141,79 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, { void *ubuf, *rbuf; int to_host; + struct sg_mapping_iter miter; + u32 flags = SG_MITER_ATOMIC; + u32 done = 0; + u32 skip = 0; + u32 trans = 0; to_host = usb_pipein(urb->pipe); rbuf = req->req.buf + req->req.actual; - ubuf = urb->transfer_buffer += urb->actual_length; + + if (!urb->num_sgs) { + ubuf = urb->transfer_buffer += urb->actual_length; + if (to_host) + memcpy(ubuf, rbuf, len); + else + memcpy(rbuf, ubuf, len); + return len; + } if (to_host) - memcpy(ubuf, rbuf, len); + flags |= SG_MITER_TO_SG; else - memcpy(rbuf, ubuf, len); - return len; + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&miter, urb->sg, urb->num_sgs, + flags); + + BUG_ON(!sg_miter_next(&miter)); + if (urb->actual_length) { + do { + u32 old_done = done; + + done += miter.length; + if (urb->actual_length > done) { + sg_miter_next(&miter); + continue; + } + if (urb->actual_length == done) { + + skip = done - old_done; + if (skip < miter.length) + break; + skip = 0; + sg_miter_next(&miter); + break; + } + + done -= miter.length; + skip = urb->actual_length - done; + break; + } while (1); + } + do { + u32 this_sg; + + ubuf = miter.addr + skip; + rbuf += skip; + this_sg = min(len, miter.length - skip); + trans += this_sg; + + if (to_host) + memcpy(ubuf, rbuf, this_sg); + else + memcpy(rbuf, ubuf, this_sg); + len -= this_sg; + + if (!len) + break; + BUG_ON(!sg_miter_next(&miter)); + skip = 0; + } while (1); + + sg_miter_stop(&miter); + return trans; } /* transfer up to a frame's worth; caller must own lock */ @@ -2213,6 +2278,7 @@ static int dummy_h_get_frame (struct usb_hcd *hcd) static int dummy_setup(struct usb_hcd *hcd) { + hcd->self.sg_tablesize = ~0; if (usb_hcd_is_primary_hcd(hcd)) { the_controller.hs_hcd = hcd_to_dummy_hcd(hcd); the_controller.hs_hcd->dum = &the_controller; -- 1.7.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html