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 | 78 ++++++++++++++++++++++++++++++++++++---- 1 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index bfba4aa..5ba8e18 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> @@ -1084,9 +1085,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; @@ -1140,21 +1142,82 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) return rc; } +static void forward_miter(struct sg_mapping_iter *miter, u32 bytes) +{ + u32 done = 0; + + /* press the forward button on the URB side */ + if (!bytes) + return; + do { + done += miter->length; + if (bytes > done) { + sg_miter_next(miter); + continue; + } + if (bytes == done) + break; + + miter->consumed = miter->length - (done - bytes); + break; + } while (1); + sg_miter_next(miter); +} + static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, u32 len) { void *ubuf, *rbuf; int to_host; + struct sg_mapping_iter miter; + u32 flags = SG_MITER_ATOMIC; + u32 trans = 0; + u32 this_sg; 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)); + + forward_miter(&miter, urb->actual_length); + + do { + ubuf = miter.addr; + this_sg = min(len, miter.length); + miter.consumed = this_sg; + 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)); + rbuf += this_sg; + } while (1); + + sg_miter_stop(&miter); + return trans; } /* transfer up to a frame's worth; caller must own lock */ @@ -2219,6 +2282,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.3 -- 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