Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8355b2b3 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Felipe Balbi
Browse files

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: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent f0798d6a
Loading
Loading
Loading
Loading
+24 −1
Original line number Original line Diff line number Diff line
@@ -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 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
	 * 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);
	}
	}


+13 −0
Original line number Original line Diff line number Diff line
@@ -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);
+4 −0
Original line number Original line Diff line number Diff line
@@ -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);