mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user