usb: gadget: dummy_hcd: add sg support
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@linutronix.de> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
a04ce20d9f
commit
14fce33a96
@ -39,6 +39,7 @@
|
|||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/gadget.h>
|
#include <linux/usb/gadget.h>
|
||||||
#include <linux/usb/hcd.h>
|
#include <linux/usb/hcd.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
@ -147,6 +148,8 @@ static const char *const ep_name [] = {
|
|||||||
struct urbp {
|
struct urbp {
|
||||||
struct urb *urb;
|
struct urb *urb;
|
||||||
struct list_head urbp_list;
|
struct list_head urbp_list;
|
||||||
|
struct sg_mapping_iter miter;
|
||||||
|
u32 miter_started;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1077,13 +1080,11 @@ static int dummy_urb_enqueue (
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!urb->transfer_buffer && urb->transfer_buffer_length)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
urbp = kmalloc (sizeof *urbp, mem_flags);
|
urbp = kmalloc (sizeof *urbp, mem_flags);
|
||||||
if (!urbp)
|
if (!urbp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
urbp->urb = urb;
|
urbp->urb = urb;
|
||||||
|
urbp->miter_started = 0;
|
||||||
|
|
||||||
dum_hcd = hcd_to_dummy_hcd(hcd);
|
dum_hcd = hcd_to_dummy_hcd(hcd);
|
||||||
spin_lock_irqsave(&dum_hcd->dum->lock, flags);
|
spin_lock_irqsave(&dum_hcd->dum->lock, flags);
|
||||||
@ -1137,17 +1138,66 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
|
|||||||
u32 len)
|
u32 len)
|
||||||
{
|
{
|
||||||
void *ubuf, *rbuf;
|
void *ubuf, *rbuf;
|
||||||
|
struct urbp *urbp = urb->hcpriv;
|
||||||
int to_host;
|
int to_host;
|
||||||
|
struct sg_mapping_iter *miter = &urbp->miter;
|
||||||
|
u32 trans = 0;
|
||||||
|
u32 this_sg;
|
||||||
|
bool next_sg;
|
||||||
|
|
||||||
to_host = usb_pipein(urb->pipe);
|
to_host = usb_pipein(urb->pipe);
|
||||||
rbuf = req->req.buf + req->req.actual;
|
rbuf = req->req.buf + req->req.actual;
|
||||||
ubuf = urb->transfer_buffer + urb->actual_length;
|
|
||||||
|
|
||||||
if (to_host)
|
if (!urb->num_sgs) {
|
||||||
memcpy(ubuf, rbuf, len);
|
ubuf = urb->transfer_buffer + urb->actual_length;
|
||||||
else
|
if (to_host)
|
||||||
memcpy(rbuf, ubuf, len);
|
memcpy(ubuf, rbuf, len);
|
||||||
return len;
|
else
|
||||||
|
memcpy(rbuf, ubuf, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!urbp->miter_started) {
|
||||||
|
u32 flags = SG_MITER_ATOMIC;
|
||||||
|
|
||||||
|
if (to_host)
|
||||||
|
flags |= SG_MITER_TO_SG;
|
||||||
|
else
|
||||||
|
flags |= SG_MITER_FROM_SG;
|
||||||
|
|
||||||
|
sg_miter_start(miter, urb->sg, urb->num_sgs, flags);
|
||||||
|
urbp->miter_started = 1;
|
||||||
|
}
|
||||||
|
next_sg = sg_miter_next(miter);
|
||||||
|
if (next_sg == false) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
ubuf = miter->addr;
|
||||||
|
this_sg = min_t(u32, 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;
|
||||||
|
next_sg = sg_miter_next(miter);
|
||||||
|
if (next_sg == false) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rbuf += this_sg;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
sg_miter_stop(miter);
|
||||||
|
return trans;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* transfer up to a frame's worth; caller must own lock */
|
/* transfer up to a frame's worth; caller must own lock */
|
||||||
@ -1198,10 +1248,13 @@ top:
|
|||||||
len = dummy_perform_transfer(urb, req, len);
|
len = dummy_perform_transfer(urb, req, len);
|
||||||
|
|
||||||
ep->last_io = jiffies;
|
ep->last_io = jiffies;
|
||||||
|
if (len < 0) {
|
||||||
limit -= len;
|
req->req.status = len;
|
||||||
urb->actual_length += len;
|
} else {
|
||||||
req->req.actual += len;
|
limit -= len;
|
||||||
|
urb->actual_length += len;
|
||||||
|
req->req.actual += len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* short packets terminate, maybe with overflow/underflow.
|
/* short packets terminate, maybe with overflow/underflow.
|
||||||
@ -2212,6 +2265,7 @@ static int dummy_h_get_frame (struct usb_hcd *hcd)
|
|||||||
|
|
||||||
static int dummy_setup(struct usb_hcd *hcd)
|
static int dummy_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
|
hcd->self.sg_tablesize = ~0;
|
||||||
if (usb_hcd_is_primary_hcd(hcd)) {
|
if (usb_hcd_is_primary_hcd(hcd)) {
|
||||||
the_controller.hs_hcd = hcd_to_dummy_hcd(hcd);
|
the_controller.hs_hcd = hcd_to_dummy_hcd(hcd);
|
||||||
the_controller.hs_hcd->dum = &the_controller;
|
the_controller.hs_hcd->dum = &the_controller;
|
||||||
|
Loading…
Reference in New Issue
Block a user