When handling the endpoint interrupt handler, it maybe disable the endpoint
from another core user to set the USB endpoint descriptor pointor to be NULL
while issuing usb_gadget_giveback_request() function to release lock. So it
will be one bug to check the endpoint type by usb_endpoint_xfer_xxx() APIs with
one NULL USB endpoint descriptor.

Thus this patch introduces safety dwc3_endpoint_xfer_xxx() APIs to check
endpoint type with checking if the endpoint flag 'DWC3_EP_ENABLED' is set or
not.

[ 4007.812527] c1 init(1) call enable_store(0)
[ 4007.812633] c0 dwc3 20500000.dwc3: ep1out disabled while handling ep event
[ 4007.812657] c0 Unable to handle kernel NULL pointer dereference at virtual 
address 00000003
[ 4007.812669] c0 pgd = ffffffc0260a8000
[ 4007.812681] c0 [00000003] *pgd=00000000b4ab4003, *pud=00000000b4ab4003, 
*pmd=0000000000000000
[ 4007.812709] c0 Internal error: Oops: 96000006 [#1] PREEMPT SMP
[ 4007.818441] c0 Modules linked in: bcmdhd
[ 4007.821854] c3 warning saudio_recv cache is empty!
[ 4007.821860] c3 aud_recv_cmd  ENODATA
[ 4007.821869] c3 aud_send_cmd in,cmd =11 id:10 ret-value:0
[ 4007.821876] c3 saudio_send: dst=1, channel=0, timeout=-1
[ 4007.840767] c0  mali_kbase(O)
[ 4007.843707] c0
[ 4007.843889] c0 CPU: 0 PID: 8126 Comm: adbd Tainted: G        W  O   3.18.12+ 
#1
[ 4007.851153] c0 Hardware name: Spreadtrum SP9860g Board (DT)
[ 4007.856693] c0 task: ffffffc02638a400 ti: ffffffc026148000 task.ti: 
ffffffc026148000
[ 4007.864404] c0 PC is at dwc3_interrupt_bh+0x710/0x112c
[ 4007.869503] c0 LR is at dwc3_interrupt_bh+0x70c/0x112c
[ 4007.874604] c0 pc : [<ffffffc0005bc48c>] lr : [<ffffffc0005bc488>] pstate: 
200001c5
[ 4007.882218] c0 sp : ffffffc02614b970
[ 4007.885766] c0 x29: ffffffc02614b970 x28: ffffffc000c75f90
[ 4007.891044] c0 x27: 0000000000000008 x26: 0000000000000000
[ 4007.896323] c0 x25: 0000000000000000 x24: 000000000000030c
[ 4007.901602] c0 x23: ffffffc000ff1000 x22: 0000000000000004
[ 4007.906881] c0 x21: ffffffc03d9fb5d8 x20: ffffffc13e573600
[ 4007.912160] c0 x19: ffffffc13e133020 x18: 0000000000000007
[ 4007.917437] c0 x17: 000000000000000e x16: 0000000000000001
[ 4007.922717] c0 x15: 0000000000000007 x14: 0fffffffffffffff
[ 4007.927996] c0 x13: 0000000000000001 x12: 0101010101010101
[ 4007.933274] c0 x11: 00000000000c4f5d x10: 0000000000000002
[ 4007.938553] c0 x9 : 0000000000000000 x8 : 00000000000c4f5e
[ 4007.943833] c0 x7 : 696c646e61682065 x6 : ffffffc000fa6cdc
[ 4007.949111] c0 x5 : 0000000000000000 x4 : 0000000000000105
[ 4007.954390] c0 x3 : 0000000000000000 x2 : 0000000000000000
[ 4007.959669] c0 x1 : 000000005533250a x0 : 0000000000000000
......
[ 4009.150699] c0 Call trace:
[ 4009.153394] c0 [<ffffffc0005bc48c>] dwc3_interrupt_bh+0x710/0x112c
[ 4009.159532] c0 [<ffffffc0000c0370>] tasklet_action+0xb0/0x188
[ 4009.165243] c0 [<ffffffc0000bf598>] __do_softirq+0x114/0x3b4
[ 4009.170867] c0 [<ffffffc0000bfb9c>] irq_exit+0xa8/0xdc
[ 4009.175975] c0 [<ffffffc00010cc44>] __handle_domain_irq+0x90/0xf8
[ 4009.182030] c0 [<ffffffc00008249c>] gic_handle_irq+0x58/0x1b4
[ 4009.187739] c0 Exception stack(0xffffffc02614bb70 to 0xffffffc02614bc90)
[ 4009.194404] c0 bb60:                                     00000140 00000000 
3e133108 ffffffc1
[ 4009.202802] c0 bb80: 2614bcd0 ffffffc0 009cff0c ffffffc0 80000145 00000000 
00000018 00000000
[ 4009.211194] c0 bba0: 00000400 00000000 00459900 ffffffc0 00000000 00000000 
00d1aa50 ffffffc0
[ 4009.219589] c0 bbc0: 2614bc00 ffffffc0 26148000 ffffffc0 00000001 00000000 
3d2ce4e0 ffffffc1
[ 4009.227984] c0 bbe0: bdc00000 00000000 00000001 00000000 2638a980 ffffffc0 
2614ba40 ffffffc0
[ 4009.236379] c0 bc00: 00000400 00000000 00000001 00000000 1d37bbb0 ffffffc0 
00000013 00000000
[ 4009.244772] c0 bc20: 0000000e 00000000 00000007 00000000 00000001 00000000 
0000000e 00000000
[ 4009.253167] c0 bc40: 00000007 00000000 00000140 00000000 3e133108 ffffffc1 
3e133108 ffffffc1
[ 4009.261560] c0 bc60: 00000140 00000000 3eb833c0 ffffffc1 00000018 00000000 
00000400 00000000
[ 4009.269951] c0 bc80: 1eb29b80 ffffffc1 3acd7c00 ffffffc0
[ 4009.275233] c0 [<ffffffc0000860e8>] el1_irq+0x68/0xdc
[ 4009.280255] c0 [<ffffffc0005b940c>] dwc3_gadget_ep_dequeue+0x180/0x1b8
[ 4009.286741] c0 [<ffffffc0005feb48>] ffs_epfile_io+0x330/0x374
[ 4009.292453] c0 [<ffffffc0005fec3c>] ffs_epfile_read+0x30/0x40
[ 4009.298169] c0 [<ffffffc0001ecb3c>] vfs_read+0xa0/0x1dc
[ 4009.303357] c0 [<ffffffc0001ed644>] SyS_read+0x50/0xb0

Signed-off-by: Baolin Wang <baolin.w...@linaro.org>
---
 drivers/usb/dwc3/gadget.c |   95 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 72 insertions(+), 23 deletions(-)

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8e4a1b1..5d095f2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -36,6 +36,56 @@
 #include "io.h"
 
 /**
+* dwc3_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+* @ep: endpoint to be checked
+*
+* Returns true if the endpoint is of type bulk, otherwise it returns false.
+*/
+static inline int dwc3_endpoint_xfer_bulk(struct dwc3_ep *ep)
+{
+       return ((ep->flags & DWC3_EP_ENABLED) &&
+               ep->type == USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+* dwc3_endpoint_xfer_control - check if the endpoint has control transfer type
+* @ep: endpoint to be checked
+*
+* Returns true if the endpoint is of type control, otherwise it returns false.
+*/
+static inline int dwc3_endpoint_xfer_control(struct dwc3_ep *ep)
+{
+       return ((ep->flags & DWC3_EP_ENABLED) &&
+               ep->type == USB_ENDPOINT_XFER_CONTROL);
+}
+
+/**
+* dwc3_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+* @ep: endpoint to be checked
+*
+* Returns true if the endpoint is of type interrupt, otherwise it returns
+* false.
+*/
+static inline int dwc3_endpoint_xfer_int(struct dwc3_ep *ep)
+{
+       return ((ep->flags & DWC3_EP_ENABLED) &&
+               ep->type == USB_ENDPOINT_XFER_INT);
+}
+
+/**
+* dwc3_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+* @ep: endpoint to be checked
+*
+* Returns true if the endpoint is of type isochronous, otherwise it returns
+* false.
+*/
+static inline int dwc3_endpoint_xfer_isoc(struct dwc3_ep *ep)
+{
+       return ((ep->flags & DWC3_EP_ENABLED) &&
+               ep->type == USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
  * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
  * @dwc: pointer to our context structure
  * @mode: the mode to set (J, K SE0 NAK, Force Enable)
@@ -198,8 +248,8 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
                if (!(dep->flags & DWC3_EP_ENABLED))
                        continue;
 
-               if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
-                               || usb_endpoint_xfer_isoc(dep->endpoint.desc))
+               if (dwc3_endpoint_xfer_bulk(dep) ||
+                   dwc3_endpoint_xfer_isoc(dep))
                        mult = 3;
 
                /*
@@ -248,7 +298,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct 
dwc3_request *req,
                         */
                        if (((dep->busy_slot & DWC3_TRB_MASK) ==
                                DWC3_TRB_NUM- 1) &&
-                               usb_endpoint_xfer_isoc(dep->endpoint.desc))
+                               dwc3_endpoint_xfer_isoc(dep))
                                dep->busy_slot++;
                } while(++i < req->request.num_mapped_sgs);
                req->queued = false;
@@ -567,7 +617,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                reg |= DWC3_DALEPENA_EP(dep->number);
                dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
 
-               if (!usb_endpoint_xfer_isoc(desc))
+               if (!dwc3_endpoint_xfer_isoc(dep))
                        goto out;
 
                /* Link TRB for ISOC. The HWO bit is never reset */
@@ -795,7 +845,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        dep->free_slot++;
        /* Skip the LINK-TRB on ISOC */
        if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
-                       usb_endpoint_xfer_isoc(dep->endpoint.desc))
+                       dwc3_endpoint_xfer_isoc(dep))
                dep->free_slot++;
 
        trb->size = DWC3_TRB_SIZE_LENGTH(length);
@@ -829,7 +879,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        if (!req->request.no_interrupt && !chain)
                trb->ctrl |= DWC3_TRB_CTRL_IOC;
 
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+       if (dwc3_endpoint_xfer_isoc(dep)) {
                trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
                trb->ctrl |= DWC3_TRB_CTRL_CSP;
        } else if (last) {
@@ -839,7 +889,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        if (chain)
                trb->ctrl |= DWC3_TRB_CTRL_CHN;
 
-       if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
+       if (dwc3_endpoint_xfer_bulk(dep) && dep->stream_capable)
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
 
        trb->ctrl |= DWC3_TRB_CTRL_HWO;
@@ -869,7 +919,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool 
starting)
        trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
 
        /* Can't wrap around on a non-isoc EP since there's no link TRB */
-       if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+       if (!dwc3_endpoint_xfer_isoc(dep)) {
                max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
                if (trbs_left > max)
                        trbs_left = max;
@@ -895,7 +945,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool 
starting)
                 * processed from the first TRB until the last one. Since we
                 * don't wrap around we have to start at the beginning.
                 */
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+               if (dwc3_endpoint_xfer_isoc(dep)) {
                        dep->busy_slot = 1;
                        dep->free_slot = 1;
                } else {
@@ -905,7 +955,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool 
starting)
        }
 
        /* The last TRB is a link TRB, not used for xfer */
-       if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
+       if ((trbs_left <= 1) && dwc3_endpoint_xfer_isoc(dep))
                return;
 
        list_for_each_entry_safe(req, n, &dep->request_list, list) {
@@ -1123,8 +1173,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, 
struct dwc3_request *req)
         * This will save one IRQ (XFER_NOT_READY) and possibly make it a
         * little bit faster.
         */
-       if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
-                       !usb_endpoint_xfer_int(dep->endpoint.desc) &&
+       if (!dwc3_endpoint_xfer_isoc(dep) &&
+                       !dwc3_endpoint_xfer_int(dep) &&
                        !(dep->flags & DWC3_EP_BUSY)) {
                ret = __dwc3_gadget_kick_transfer(dep, 0, true);
                goto out;
@@ -1148,7 +1198,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, 
struct dwc3_request *req)
                 * you can receive xfernotready again and can have
                 * notion of current microframe.
                 */
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+               if (dwc3_endpoint_xfer_isoc(dep)) {
                        if (list_empty(&dep->req_queued)) {
                                dwc3_stop_active_transfer(dwc, dep->number, 
true);
                                dep->flags = DWC3_EP_ENABLED;
@@ -1168,7 +1218,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, 
struct dwc3_request *req)
         *    kick the transfer here after queuing a request, otherwise the
         *    core may not see the modified TRB(s).
         */
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+       if (dwc3_endpoint_xfer_isoc(dep) &&
                        (dep->flags & DWC3_EP_BUSY) &&
                        !(dep->flags & DWC3_EP_MISSED_ISOC)) {
                WARN_ON_ONCE(!dep->resource_index);
@@ -1304,7 +1354,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int 
value, int protocol)
        struct dwc3                             *dwc = dep->dwc;
        int                                     ret;
 
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+       if (dwc3_endpoint_xfer_isoc(dep)) {
                dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
                return -EINVAL;
        }
@@ -1971,7 +2021,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, 
struct dwc3_ep *dep,
                do {
                        slot = req->start_slot + i;
                        if ((slot == DWC3_TRB_NUM - 1) &&
-                               usb_endpoint_xfer_isoc(dep->endpoint.desc))
+                               dwc3_endpoint_xfer_isoc(dep))
                                slot++;
                        slot %= DWC3_TRB_NUM;
                        trb = &dep->trb_pool[slot];
@@ -1988,7 +2038,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, 
struct dwc3_ep *dep,
                        break;
        } while (1);
 
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+       if (dwc3_endpoint_xfer_isoc(dep) &&
                        list_empty(&dep->req_queued)) {
                if (list_empty(&dep->request_list)) {
                        /*
@@ -2021,8 +2071,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 
*dwc,
                status = -ECONNRESET;
 
        clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
-       if (clean_busy && (is_xfer_complete ||
-                               usb_endpoint_xfer_isoc(dep->endpoint.desc)))
+       if (clean_busy && (is_xfer_complete || dwc3_endpoint_xfer_isoc(dep)))
                dep->flags &= ~DWC3_EP_BUSY;
 
        /*
@@ -2050,7 +2099,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 
*dwc,
                dwc->u1u2 = 0;
        }
 
-       if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+       if (!dwc3_endpoint_xfer_isoc(dep)) {
                int ret;
 
                ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
@@ -2079,7 +2128,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
        case DWC3_DEPEVT_XFERCOMPLETE:
                dep->resource_index = 0;
 
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+               if (dwc3_endpoint_xfer_isoc(dep)) {
                        dwc3_trace(trace_dwc3_gadget,
                                        "%s is an Isochronous endpoint\n",
                                        dep->name);
@@ -2092,7 +2141,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                dwc3_endpoint_transfer_complete(dwc, dep, event);
                break;
        case DWC3_DEPEVT_XFERNOTREADY:
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+               if (dwc3_endpoint_xfer_isoc(dep)) {
                        dwc3_gadget_start_isoc(dwc, dep, event);
                } else {
                        int active;
@@ -2115,7 +2164,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
                break;
        case DWC3_DEPEVT_STREAMEVT:
-               if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
+               if (!dwc3_endpoint_xfer_bulk(dep)) {
                        dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
                                        dep->name);
                        return;
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to