Skip to content

Commit

Permalink
usb: dwc2: gadget: add unaligned buffers support
Browse files Browse the repository at this point in the history
When using DMA, dwc2 requires buffers to be 4 bytes aligned. Use
bounce buffers if they are not.

Tested-by: Robert Baldyga <[email protected]>
Acked-by: John Youn <[email protected]>
Signed-off-by: Mian Yousaf Kaukab <[email protected]>
Signed-off-by: Felipe Balbi <[email protected]>
  • Loading branch information
Mian Yousaf Kaukab authored and nemunaire committed Jun 16, 2019
1 parent b927b5d commit c82f8b7
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 1 deletion.
2 changes: 2 additions & 0 deletions drivers/usb/dwc2/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,12 @@ struct s3c_hsotg_ep {
* struct s3c_hsotg_req - data transfer request
* @req: The USB gadget request
* @queue: The list of requests for the endpoint this is queued for.
* @saved_req_buf: variable to save req.buf when bounce buffers are used.
*/
struct s3c_hsotg_req {
struct usb_request req;
struct list_head queue;
void *saved_req_buf;
};

#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
Expand Down
62 changes: 61 additions & 1 deletion drivers/usb/dwc2/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,13 +726,67 @@ static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
return -EIO;
}

static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
{
void *req_buf = hs_req->req.buf;

/* If dma is not being used or buffer is aligned */
if (!using_dma(hsotg) || !((long)req_buf & 3))
return 0;

WARN_ON(hs_req->saved_req_buf);

dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
hs_ep->ep.name, req_buf, hs_req->req.length);

hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
if (!hs_req->req.buf) {
hs_req->req.buf = req_buf;
dev_err(hsotg->dev,
"%s: unable to allocate memory for bounce buffer\n",
__func__);
return -ENOMEM;
}

/* Save actual buffer */
hs_req->saved_req_buf = req_buf;

if (hs_ep->dir_in)
memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
return 0;
}

static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
{
/* If dma is not being used or buffer was aligned */
if (!using_dma(hsotg) || !hs_req->saved_req_buf)
return;

dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);

/* Copy data from bounce buffer on successful out transfer */
if (!hs_ep->dir_in && !hs_req->req.status)
memcpy(hs_req->saved_req_buf, hs_req->req.buf,
hs_req->req.actual);

/* Free bounce buffer */
kfree(hs_req->req.buf);

hs_req->req.buf = hs_req->saved_req_buf;
hs_req->saved_req_buf = NULL;
}

static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
struct s3c_hsotg_req *hs_req = our_req(req);
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
int ret;

dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
Expand All @@ -743,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->actual = 0;
req->status = -EINPROGRESS;

ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
if (ret)
return ret;

/* if we're using DMA, sync the buffers as necessary */
if (using_dma(hs)) {
int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
ret = s3c_hsotg_map_dma(hs, hs_ep, req);
if (ret)
return ret;
}
Expand Down Expand Up @@ -1326,6 +1384,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
if (hs_req->req.status == -EINPROGRESS)
hs_req->req.status = result;

s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);

hs_ep->req = NULL;
list_del_init(&hs_req->queue);

Expand Down

0 comments on commit c82f8b7

Please sign in to comment.