usb: dwc_otg_310: gadget: stop current transfer on dequeue

If the request being dequeued is already started, disable endpoint
to stop the transfer and then call dwc_otg_request_done().
Endpoint will be re-enabled on next call to dwc_otg_ep_start_transfer().

TEST=use RK3128/RK3288/RK3368 board, adb root and then adb reboot,
check if usb_req->buf is used after free.

Change-Id: I8c9304f29c578f679ad0841350920f1350bd32df
Signed-off-by: Wu Liang feng <wulf@rock-chips.com>
This commit is contained in:
Wu Liang feng
2015-09-02 10:07:56 +08:00
parent 468345a502
commit 288180cba4

View File

@@ -1712,6 +1712,127 @@ out_unlocked:
}
static int dwc_otg_wait_bit_set(volatile uint32_t *reg,
uint32_t bit, uint32_t timeout)
{
int i;
for (i = 0; i < timeout; i++) {
if (DWC_READ_REG32(reg) & bit)
return 0;
dwc_udelay(1);
}
return -ETIMEDOUT;
}
static void dwc_otg_pcd_ep_stop_transfer(dwc_otg_core_if_t
*core_if, dwc_ep_t *ep)
{
depctl_data_t depctl = {.d32 = 0 };
/* Read DEPCTLn register */
if (ep->is_in == 1)
depctl.d32 = DWC_READ_REG32(&core_if->dev_if->
in_ep_regs[ep->num]->
diepctl);
else
depctl.d32 = DWC_READ_REG32(&core_if->dev_if->
out_ep_regs[ep->num]->
doepctl);
if (ep->is_in == 1) {
diepint_data_t diepint = {.d32 = 0 };
depctl.b.snak = 1;
DWC_WRITE_REG32(&core_if->dev_if->
in_ep_regs[ep->num]->diepctl,
depctl.d32);
diepint.b.inepnakeff = 1;
/* Wait for Nak effect */
if (dwc_otg_wait_bit_set(&core_if->dev_if->
in_ep_regs[ep->num]
->diepint,
diepint.d32,
100)) {
DWC_WARN("%s: timeout diepctl.snak\n",
__func__);
} else {
DWC_WRITE_REG32(&core_if->dev_if->
in_ep_regs[ep->num]->
diepint, diepint.d32);
}
depctl.d32 = 0;
depctl.b.epdis = 1;
DWC_WRITE_REG32(&core_if->dev_if->
in_ep_regs[ep->num]->diepctl,
depctl.d32);
diepint.d32 = 0;
diepint.b.epdisabled = 1;
if (dwc_otg_wait_bit_set(&core_if->dev_if->
in_ep_regs[ep->num]
->diepint,
diepint.d32,
100)) {
DWC_WARN("%s: timeout diepctl.epdis\n",
__func__);
} else {
DWC_WRITE_REG32(&core_if->dev_if->
in_ep_regs[ep->num]->
diepint, diepint.d32);
}
} else {
dctl_data_t dctl = {.d32 = 0 };
gintmsk_data_t gintsts = {.d32 = 0 };
doepint_data_t doepint = {.d32 = 0 };
dctl.b.sgoutnak = 1;
DWC_MODIFY_REG32(&core_if->dev_if->
dev_global_regs->dctl, 0, dctl.d32);
/* Wait for global nak to take effect */
gintsts.d32 = 0;
gintsts.b.goutnakeff = 1;
if (dwc_otg_wait_bit_set(&core_if->core_global_regs->
gintsts, gintsts.d32,
100)) {
DWC_WARN("%s: timeout dctl.sgoutnak\n",
__func__);
} else {
DWC_WRITE_REG32(&core_if->core_global_regs
->gintsts, gintsts.d32);
}
depctl.d32 = 0;
depctl.b.epdis = 1;
depctl.b.snak = 1;
DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
doepctl, depctl.d32);
doepint.b.epdisabled = 1;
if (dwc_otg_wait_bit_set(&core_if->dev_if
->out_ep_regs[ep->num]
->doepint, doepint.d32,
100)) {
DWC_WARN("%s: timeout doepctl.epdis\n",
__func__);
} else {
DWC_WRITE_REG32(&core_if->dev_if->
out_ep_regs[ep->num]->
doepint, doepint.d32);
}
dctl.d32 = 0;
dctl.b.cgoutnak = 1;
DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
dctl, 0, dctl.d32);
}
}
/******************************************************************************/
#ifdef DWC_UTE_PER_IO
@@ -2369,6 +2490,18 @@ int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t *pcd, void *ep_handle,
}
if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) {
dwc_otg_pcd_ep_stop_transfer(GET_CORE_IF(pcd),
&ep->dwc_ep);
/* Flush the Tx FIFO */
if (ep->dwc_ep.is_in) {
dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd),
ep->dwc_ep.tx_fifo_num);
release_perio_tx_fifo(GET_CORE_IF(pcd),
ep->dwc_ep.tx_fifo_num);
release_tx_fifo(GET_CORE_IF(pcd),
ep->dwc_ep.tx_fifo_num);
}
dwc_otg_request_done(ep, req, -DWC_E_RESTART);
} else {
req = NULL;