usb: renesas_usbhs: fix the behavior of some usbhs_pkt_handle
PATCH 1/4 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 000000 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 <[email protected]>
--- drivers/usb/renesas_usbhs/fifo.c | 25 ++++++++++++++++++++++++- drivers/usb/renesas_usbhs/pipe.c | 13 +++++++++++++ drivers/usb/renesas_usbhs/pipe.h | 4 ++++ 3 files changed, 41 insertions(+), 1 deletion(-)
drivers/usb/renesas_usbhs/fifo.c +24 -1
@@ -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 @@ usbhs_fifo_write_busy:* 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); }
drivers/usb/renesas_usbhs/pipe.c +13 -0
@@ -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 );
drivers/usb/renesas_usbhs/pipe.h +4 -0
@@ -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);
Signature · 1 line
1.7.9.5