Commit 8355b2b3082d302091506703d2e4e239f7deed7f
Committed by
Felipe Balbi
1 parent
f0798d6a04
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
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>
Showing 3 changed files with 41 additions and 1 deletions Side-by-side Diff
drivers/usb/renesas_usbhs/fifo.c
... | ... | @@ -544,6 +544,7 @@ |
544 | 544 | usbhsf_send_terminator(pipe, fifo); |
545 | 545 | |
546 | 546 | usbhsf_tx_irq_ctrl(pipe, !*is_done); |
547 | + usbhs_pipe_running(pipe, !*is_done); | |
547 | 548 | usbhs_pipe_enable(pipe); |
548 | 549 | |
549 | 550 | dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", |
550 | 551 | |
551 | 552 | |
... | ... | @@ -570,12 +571,21 @@ |
570 | 571 | * retry in interrupt |
571 | 572 | */ |
572 | 573 | usbhsf_tx_irq_ctrl(pipe, 1); |
574 | + usbhs_pipe_running(pipe, 1); | |
573 | 575 | |
574 | 576 | return ret; |
575 | 577 | } |
576 | 578 | |
579 | +static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done) | |
580 | +{ | |
581 | + if (usbhs_pipe_is_running(pkt->pipe)) | |
582 | + return 0; | |
583 | + | |
584 | + return usbhsf_pio_try_push(pkt, is_done); | |
585 | +} | |
586 | + | |
577 | 587 | struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { |
578 | - .prepare = usbhsf_pio_try_push, | |
588 | + .prepare = usbhsf_pio_prepare_push, | |
579 | 589 | .try_run = usbhsf_pio_try_push, |
580 | 590 | }; |
581 | 591 | |
... | ... | @@ -589,6 +599,9 @@ |
589 | 599 | if (usbhs_pipe_is_busy(pipe)) |
590 | 600 | return 0; |
591 | 601 | |
602 | + if (usbhs_pipe_is_running(pipe)) | |
603 | + return 0; | |
604 | + | |
592 | 605 | /* |
593 | 606 | * pipe enable to prepare packet receive |
594 | 607 | */ |
... | ... | @@ -597,6 +610,7 @@ |
597 | 610 | |
598 | 611 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); |
599 | 612 | usbhs_pipe_enable(pipe); |
613 | + usbhs_pipe_running(pipe, 1); | |
600 | 614 | usbhsf_rx_irq_ctrl(pipe, 1); |
601 | 615 | |
602 | 616 | return 0; |
... | ... | @@ -642,6 +656,7 @@ |
642 | 656 | (total_len < maxp)) { /* short packet */ |
643 | 657 | *is_done = 1; |
644 | 658 | usbhsf_rx_irq_ctrl(pipe, 0); |
659 | + usbhs_pipe_running(pipe, 0); | |
645 | 660 | usbhs_pipe_disable(pipe); /* disable pipe first */ |
646 | 661 | } |
647 | 662 | |
... | ... | @@ -805,6 +820,7 @@ |
805 | 820 | dev_dbg(dev, " %s %d (%d/ %d)\n", |
806 | 821 | fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); |
807 | 822 | |
823 | + usbhs_pipe_running(pipe, 1); | |
808 | 824 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); |
809 | 825 | usbhs_pipe_enable(pipe); |
810 | 826 | usbhsf_dma_start(pipe, fifo); |
... | ... | @@ -836,6 +852,10 @@ |
836 | 852 | if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ |
837 | 853 | goto usbhsf_pio_prepare_push; |
838 | 854 | |
855 | + /* return at this time if the pipe is running */ | |
856 | + if (usbhs_pipe_is_running(pipe)) | |
857 | + return 0; | |
858 | + | |
839 | 859 | /* get enable DMA fifo */ |
840 | 860 | fifo = usbhsf_get_dma_fifo(priv, pkt); |
841 | 861 | if (!fifo) |
... | ... | @@ -873,6 +893,7 @@ |
873 | 893 | pkt->actual = pkt->trans; |
874 | 894 | |
875 | 895 | *is_done = !pkt->zero; /* send zero packet ? */ |
896 | + usbhs_pipe_running(pipe, !*is_done); | |
876 | 897 | |
877 | 898 | usbhsf_dma_stop(pipe, pipe->fifo); |
878 | 899 | usbhsf_dma_unmap(pkt); |
879 | 900 | |
... | ... | @@ -972,8 +993,10 @@ |
972 | 993 | if ((pkt->actual == pkt->length) || /* receive all data */ |
973 | 994 | (pkt->trans < maxp)) { /* short packet */ |
974 | 995 | *is_done = 1; |
996 | + usbhs_pipe_running(pipe, 0); | |
975 | 997 | } else { |
976 | 998 | /* re-enable */ |
999 | + usbhs_pipe_running(pipe, 0); | |
977 | 1000 | usbhsf_prepare_pop(pkt, is_done); |
978 | 1001 | } |
979 | 1002 |
drivers/usb/renesas_usbhs/pipe.c
... | ... | @@ -578,6 +578,19 @@ |
578 | 578 | return usbhsp_flags_has(pipe, IS_DIR_HOST); |
579 | 579 | } |
580 | 580 | |
581 | +int usbhs_pipe_is_running(struct usbhs_pipe *pipe) | |
582 | +{ | |
583 | + return usbhsp_flags_has(pipe, IS_RUNNING); | |
584 | +} | |
585 | + | |
586 | +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running) | |
587 | +{ | |
588 | + if (running) | |
589 | + usbhsp_flags_set(pipe, IS_RUNNING); | |
590 | + else | |
591 | + usbhsp_flags_clr(pipe, IS_RUNNING); | |
592 | +} | |
593 | + | |
581 | 594 | void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) |
582 | 595 | { |
583 | 596 | u16 mask = (SQCLR | SQSET); |
drivers/usb/renesas_usbhs/pipe.h
... | ... | @@ -36,6 +36,7 @@ |
36 | 36 | #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) |
37 | 37 | #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) |
38 | 38 | #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) |
39 | +#define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3) | |
39 | 40 | |
40 | 41 | struct usbhs_pkt_handle *handler; |
41 | 42 | |
... | ... | @@ -80,6 +81,9 @@ |
80 | 81 | void usbhs_pipe_remove(struct usbhs_priv *priv); |
81 | 82 | int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); |
82 | 83 | int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); |
84 | +int usbhs_pipe_is_running(struct usbhs_pipe *pipe); | |
85 | +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running); | |
86 | + | |
83 | 87 | void usbhs_pipe_init(struct usbhs_priv *priv, |
84 | 88 | int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); |
85 | 89 | int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); |