mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-04-07 15:04:17 +00:00
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_send_terminator(pipe, fifo);
|
||||||
|
|
||||||
usbhsf_tx_irq_ctrl(pipe, !*is_done);
|
usbhsf_tx_irq_ctrl(pipe, !*is_done);
|
||||||
|
usbhs_pipe_running(pipe, !*is_done);
|
||||||
usbhs_pipe_enable(pipe);
|
usbhs_pipe_enable(pipe);
|
||||||
|
|
||||||
dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
|
dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
|
||||||
|
@ -570,12 +571,21 @@ usbhs_fifo_write_busy:
|
||||||
* retry in interrupt
|
* retry in interrupt
|
||||||
*/
|
*/
|
||||||
usbhsf_tx_irq_ctrl(pipe, 1);
|
usbhsf_tx_irq_ctrl(pipe, 1);
|
||||||
|
usbhs_pipe_running(pipe, 1);
|
||||||
|
|
||||||
return ret;
|
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 = {
|
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,
|
.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))
|
if (usbhs_pipe_is_busy(pipe))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (usbhs_pipe_is_running(pipe))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pipe enable to prepare packet receive
|
* 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_set_trans_count_if_bulk(pipe, pkt->length);
|
||||||
usbhs_pipe_enable(pipe);
|
usbhs_pipe_enable(pipe);
|
||||||
|
usbhs_pipe_running(pipe, 1);
|
||||||
usbhsf_rx_irq_ctrl(pipe, 1);
|
usbhsf_rx_irq_ctrl(pipe, 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -642,6 +656,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
||||||
(total_len < maxp)) { /* short packet */
|
(total_len < maxp)) { /* short packet */
|
||||||
*is_done = 1;
|
*is_done = 1;
|
||||||
usbhsf_rx_irq_ctrl(pipe, 0);
|
usbhsf_rx_irq_ctrl(pipe, 0);
|
||||||
|
usbhs_pipe_running(pipe, 0);
|
||||||
usbhs_pipe_disable(pipe); /* disable pipe first */
|
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",
|
dev_dbg(dev, " %s %d (%d/ %d)\n",
|
||||||
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
|
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_set_trans_count_if_bulk(pipe, pkt->trans);
|
||||||
usbhs_pipe_enable(pipe);
|
usbhs_pipe_enable(pipe);
|
||||||
usbhsf_dma_start(pipe, fifo);
|
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 */
|
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
|
||||||
goto usbhsf_pio_prepare_push;
|
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 */
|
/* get enable DMA fifo */
|
||||||
fifo = usbhsf_get_dma_fifo(priv, pkt);
|
fifo = usbhsf_get_dma_fifo(priv, pkt);
|
||||||
if (!fifo)
|
if (!fifo)
|
||||||
|
@ -873,6 +893,7 @@ static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
|
||||||
pkt->actual = pkt->trans;
|
pkt->actual = pkt->trans;
|
||||||
|
|
||||||
*is_done = !pkt->zero; /* send zero packet ? */
|
*is_done = !pkt->zero; /* send zero packet ? */
|
||||||
|
usbhs_pipe_running(pipe, !*is_done);
|
||||||
|
|
||||||
usbhsf_dma_stop(pipe, pipe->fifo);
|
usbhsf_dma_stop(pipe, pipe->fifo);
|
||||||
usbhsf_dma_unmap(pkt);
|
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 */
|
if ((pkt->actual == pkt->length) || /* receive all data */
|
||||||
(pkt->trans < maxp)) { /* short packet */
|
(pkt->trans < maxp)) { /* short packet */
|
||||||
*is_done = 1;
|
*is_done = 1;
|
||||||
|
usbhs_pipe_running(pipe, 0);
|
||||||
} else {
|
} else {
|
||||||
/* re-enable */
|
/* re-enable */
|
||||||
|
usbhs_pipe_running(pipe, 0);
|
||||||
usbhsf_prepare_pop(pkt, is_done);
|
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);
|
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)
|
void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
|
||||||
{
|
{
|
||||||
u16 mask = (SQCLR | SQSET);
|
u16 mask = (SQCLR | SQSET);
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct usbhs_pipe {
|
||||||
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
|
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
|
||||||
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
|
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
|
||||||
#define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2)
|
#define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2)
|
||||||
|
#define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3)
|
||||||
|
|
||||||
struct usbhs_pkt_handle *handler;
|
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);
|
void usbhs_pipe_remove(struct usbhs_priv *priv);
|
||||||
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
|
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
|
||||||
int usbhs_pipe_is_dir_host(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,
|
void usbhs_pipe_init(struct usbhs_priv *priv,
|
||||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
|
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
|
||||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
||||||
|
|
Loading…
Add table
Reference in a new issue