Commit 4d599cd3 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Greg Kroah-Hartman
Browse files

usb: renesas_usbhs: gadget: Fix usb_ep_set_{halt,wedge}() behavior



According to usb_ep_set_halt()'s description,
__usbhsg_ep_set_halt_wedge() should return -EAGAIN if the IN endpoint
has any queue or data. Otherwise, this driver is possible to cause
just STALL without sending a short packet data on g_mass_storage driver,
and then a few resetting a device happens on a host side during
a usb enumaration.

Fixes: 2f98382d ("usb: renesas_usbhs: Add Renesas USBHS Gadget")
Cc: <stable@vger.kernel.org> # v3.0+
Signed-off-by: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Link: https://lore.kernel.org/r/1569924633-322-3-git-send-email-yoshihiro.shimoda.uh@renesas.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1aae1394
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -211,6 +211,7 @@ struct usbhs_priv;
/* DCPCTR */
#define BSTS		(1 << 15)	/* Buffer Status */
#define SUREQ		(1 << 14)	/* Sending SETUP Token */
#define INBUFM		(1 << 14)	/* (PIPEnCTR) Transfer Buffer Monitor */
#define CSSTS		(1 << 12)	/* CSSTS Status */
#define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
#define SQCLR		(1 << 8)	/* Toggle Bit Clear */
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
	list_del_init(&pkt->node);
}

static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
{
	return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node);
}
+1 −0
Original line number Diff line number Diff line
@@ -97,5 +97,6 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
		    void *buf, int len, int zero, int sequence);
struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt);
void usbhs_pkt_start(struct usbhs_pipe *pipe);
struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe);

#endif /* RENESAS_USB_FIFO_H */
+15 −1
Original line number Diff line number Diff line
@@ -722,6 +722,7 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
	unsigned long flags;
	int ret = 0;

	dev_dbg(dev, "set halt %d (pipe %d)\n",
		halt, usbhs_pipe_number(pipe));
@@ -729,6 +730,18 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
	/********************  spin lock ********************/
	usbhs_lock(priv, flags);

	/*
	 * According to usb_ep_set_halt()'s description, this function should
	 * return -EAGAIN if the IN endpoint has any queue or data. Note
	 * that the usbhs_pipe_is_dir_in() returns false if the pipe is an
	 * IN endpoint in the gadget mode.
	 */
	if (!usbhs_pipe_is_dir_in(pipe) && (__usbhsf_pkt_get(pipe) ||
	    usbhs_pipe_contains_transmittable_data(pipe))) {
		ret = -EAGAIN;
		goto out;
	}

	if (halt)
		usbhs_pipe_stall(pipe);
	else
@@ -739,10 +752,11 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
	else
		usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);

out:
	usbhs_unlock(priv, flags);
	/********************  spin unlock ******************/

	return 0;
	return ret;
}

static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
+15 −0
Original line number Diff line number Diff line
@@ -277,6 +277,21 @@ int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe)
	return -EBUSY;
}

bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe)
{
	u16 val;

	/* Do not support for DCP pipe */
	if (usbhs_pipe_is_dcp(pipe))
		return false;

	val = usbhsp_pipectrl_get(pipe);
	if (val & INBUFM)
		return true;

	return false;
}

/*
 *		PID ctrl
 */
Loading