diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/gadget/fsl_udc_core.c | 56 |
1 files changed, 40 insertions, 16 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 20fab22cc13b..2fab37a2a094 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -846,9 +846,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); struct fsl_req *req = container_of(_req, struct fsl_req, req); - struct fsl_udc *udc; + struct fsl_udc *udc = ep->udc; unsigned long flags; + enum dma_data_direction dir; int is_iso = 0; + int status; /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf @@ -856,17 +858,27 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) VDBG("%s, bad params", __func__); return -EINVAL; } - if (unlikely(!_ep || !ep->desc)) { + + spin_lock_irqsave(&udc->lock, flags); + + if (unlikely(!ep->desc)) { VDBG("%s, bad ep", __func__); + spin_unlock_irqrestore(&udc->lock, flags); return -EINVAL; } + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - if (req->req.length > ep->ep.maxpacket) + if (req->req.length > ep->ep.maxpacket) { + spin_unlock_irqrestore(&udc->lock, flags); return -EMSGSIZE; + } is_iso = 1; } - udc = ep->udc; + dir = ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + spin_unlock_irqrestore(&udc->lock, flags); + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; @@ -874,18 +886,12 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* map virtual address to hardware */ if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); + req->req.dma = dma_map_single(udc->gadget.dev.parent, + req->req.buf, req->req.length, dir); req->mapped = 1; } else { - dma_sync_single_for_device(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); + dma_sync_single_for_device(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); req->mapped = 0; } @@ -895,10 +901,19 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* build dtds and push them to device queue */ - if (fsl_req_to_dtd(req, gfp_flags)) - return -ENOMEM; + status = fsl_req_to_dtd(req, gfp_flags); + if (status) + goto err_unmap; spin_lock_irqsave(&udc->lock, flags); + + /* re-check if the ep has not been disabled */ + if (unlikely(!ep->desc)) { + spin_unlock_irqrestore(&udc->lock, flags); + status = -EINVAL; + goto err_unmap; + } + fsl_queue_td(ep, req); /* Update ep0 state */ @@ -911,6 +926,15 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) spin_unlock_irqrestore(&udc->lock, flags); return 0; + +err_unmap: + if (req->mapped) { + dma_unmap_single(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } + return status; } /* dequeues (cancels, unlinks) an I/O request from an endpoint */ |