usb: renesas_usbhs: fix the behavior of some usbhs_pkt_handle
Some gadget drivers will call usb_ep_queue() more than once before
the first queue doesn't finish. However, this driver didn't handle
it correctly. So, this patch fixes the behavior of some
usbhs_pkt_handle using the "running" flag. Otherwise, the oops below
happens if we use g_ncm driver and when the "iperf -u -c host -b 200M"
is running.
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c0004000
[00000000] *pgd=00000000
Internal error: Oops: 80000007 [#1] SMP ARM
Modules linked in: usb_f_ncm g_ncm libcomposite u_ether
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.17.0-rc1-00008-g8b2be8a-dirty #20
task: c051c7e0
ti: c0512000 task.ti: c0512000
PC is at 0x0
LR is at usbhsf_pkt_handler+0xa8/0x114
pc : [<00000000>] lr : [<c0278fb4>] psr: 60000193
sp : c0513ce8 ip : c0513c58 fp : c0513d24
r10: 00000001 r9 : 00000193 r8 : eebec4a0
r7 : eebec410 r6 : eebe0c6c r5 : 00000000 r4 : ee4a2774
r3 : 00000000 r2 : ee251e00 r1 : c0513cf4 r0 : ee4a2774
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
f0798d6a04
commit
8355b2b308
3 changed files with 41 additions and 1 deletions
|
@ -544,6 +544,7 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
|
|||
usbhsf_send_terminator(pipe, fifo);
|
||||
|
||||
usbhsf_tx_irq_ctrl(pipe, !*is_done);
|
||||
usbhs_pipe_running(pipe, !*is_done);
|
||||
usbhs_pipe_enable(pipe);
|
||||
|
||||
dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
|
||||
|
@ -570,12 +571,21 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
|
|||
* retry in interrupt
|
||||
*/
|
||||
usbhsf_tx_irq_ctrl(pipe, 1);
|
||||
usbhs_pipe_running(pipe, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
if (usbhs_pipe_is_running(pkt->pipe))
|
||||
return 0;
|
||||
|
||||
return usbhsf_pio_try_push(pkt, is_done);
|
||||
}
|
||||
|
||||
struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
|
||||
.prepare = usbhsf_pio_try_push,
|
||||
.prepare = usbhsf_pio_prepare_push,
|
||||
.try_run = usbhsf_pio_try_push,
|
||||
};
|
||||
|
||||
|
@ -589,6 +599,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
|
|||
if (usbhs_pipe_is_busy(pipe))
|
||||
return 0;
|
||||
|
||||
if (usbhs_pipe_is_running(pipe))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* pipe enable to prepare packet receive
|
||||
*/
|
||||
|
@ -597,6 +610,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
|
|||
|
||||
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
|
||||
usbhs_pipe_enable(pipe);
|
||||
usbhs_pipe_running(pipe, 1);
|
||||
usbhsf_rx_irq_ctrl(pipe, 1);
|
||||
|
||||
return 0;
|
||||
|
@ -642,6 +656,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
|||
(total_len < maxp)) { /* short packet */
|
||||
*is_done = 1;
|
||||
usbhsf_rx_irq_ctrl(pipe, 0);
|
||||
usbhs_pipe_running(pipe, 0);
|
||||
usbhs_pipe_disable(pipe); /* disable pipe first */
|
||||
}
|
||||
|
||||
|
@ -805,6 +820,7 @@ static void xfer_work(struct work_struct *work)
|
|||
dev_dbg(dev, " %s %d (%d/ %d)\n",
|
||||
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
|
||||
|
||||
usbhs_pipe_running(pipe, 1);
|
||||
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
|
||||
usbhs_pipe_enable(pipe);
|
||||
usbhsf_dma_start(pipe, fifo);
|
||||
|
@ -836,6 +852,10 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
|||
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
/* return at this time if the pipe is running */
|
||||
if (usbhs_pipe_is_running(pipe))
|
||||
return 0;
|
||||
|
||||
/* get enable DMA fifo */
|
||||
fifo = usbhsf_get_dma_fifo(priv, pkt);
|
||||
if (!fifo)
|
||||
|
@ -873,6 +893,7 @@ static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
|
|||
pkt->actual = pkt->trans;
|
||||
|
||||
*is_done = !pkt->zero; /* send zero packet ? */
|
||||
usbhs_pipe_running(pipe, !*is_done);
|
||||
|
||||
usbhsf_dma_stop(pipe, pipe->fifo);
|
||||
usbhsf_dma_unmap(pkt);
|
||||
|
@ -972,8 +993,10 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
|
|||
if ((pkt->actual == pkt->length) || /* receive all data */
|
||||
(pkt->trans < maxp)) { /* short packet */
|
||||
*is_done = 1;
|
||||
usbhs_pipe_running(pipe, 0);
|
||||
} else {
|
||||
/* re-enable */
|
||||
usbhs_pipe_running(pipe, 0);
|
||||
usbhsf_prepare_pop(pkt, is_done);
|
||||
}
|
||||
|
||||
|
|
|
@ -578,6 +578,19 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe)
|
|||
return usbhsp_flags_has(pipe, IS_DIR_HOST);
|
||||
}
|
||||
|
||||
int usbhs_pipe_is_running(struct usbhs_pipe *pipe)
|
||||
{
|
||||
return usbhsp_flags_has(pipe, IS_RUNNING);
|
||||
}
|
||||
|
||||
void usbhs_pipe_running(struct usbhs_pipe *pipe, int running)
|
||||
{
|
||||
if (running)
|
||||
usbhsp_flags_set(pipe, IS_RUNNING);
|
||||
else
|
||||
usbhsp_flags_clr(pipe, IS_RUNNING);
|
||||
}
|
||||
|
||||
void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
|
||||
{
|
||||
u16 mask = (SQCLR | SQSET);
|
||||
|
|
|
@ -36,6 +36,7 @@ struct usbhs_pipe {
|
|||
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
|
||||
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
|
||||
#define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2)
|
||||
#define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3)
|
||||
|
||||
struct usbhs_pkt_handle *handler;
|
||||
|
||||
|
@ -80,6 +81,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv);
|
|||
void usbhs_pipe_remove(struct usbhs_priv *priv);
|
||||
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
|
||||
int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe);
|
||||
int usbhs_pipe_is_running(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_running(struct usbhs_pipe *pipe, int running);
|
||||
|
||||
void usbhs_pipe_init(struct usbhs_priv *priv,
|
||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
|
||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
||||
|
|
Loading…
Reference in a new issue