Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14fce33a authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Felipe Balbi
Browse files

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: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent a04ce20d
Loading
Loading
Loading
Loading
+67 −13
Original line number Diff line number Diff line
@@ -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>
@@ -147,6 +148,8 @@ static const char *const ep_name [] = {
struct urbp {
	struct urb		*urb;
	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;
	int		rc;

	if (!urb->transfer_buffer && urb->transfer_buffer_length)
		return -EINVAL;

	urbp = kmalloc (sizeof *urbp, mem_flags);
	if (!urbp)
		return -ENOMEM;
	urbp->urb = urb;
	urbp->miter_started = 0;

	dum_hcd = hcd_to_dummy_hcd(hcd);
	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
@@ -1137,12 +1138,18 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
		u32 len)
{
	void *ubuf, *rbuf;
	struct urbp *urbp = urb->hcpriv;
	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);
	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
@@ -1150,6 +1157,49 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
		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 */
static int
transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit,
@@ -1198,11 +1248,14 @@ top:
			len = dummy_perform_transfer(urb, req, len);

			ep->last_io = jiffies;

			if (len < 0) {
				req->req.status = len;
			} else {
				limit -= len;
				urb->actual_length += len;
				req->req.actual += len;
			}
		}

		/* short packets terminate, maybe with overflow/underflow.
		 * it's only really an error to write too much.
@@ -2212,6 +2265,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;