Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 2f98382dcdfe ("usb: renesas_usbhs: Add Renesas USBHS Gadget")
Cc: <stable@vger.kernel.org> # v3.0+
Signed-off-by: Yoshihiro 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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Yoshihiro Shimoda and committed by
Greg Kroah-Hartman
4d599cd3 1aae1394

+34 -2
+1
drivers/usb/renesas_usbhs/common.h
··· 211 211 /* DCPCTR */ 212 212 #define BSTS (1 << 15) /* Buffer Status */ 213 213 #define SUREQ (1 << 14) /* Sending SETUP Token */ 214 + #define INBUFM (1 << 14) /* (PIPEnCTR) Transfer Buffer Monitor */ 214 215 #define CSSTS (1 << 12) /* CSSTS Status */ 215 216 #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ 216 217 #define SQCLR (1 << 8) /* Toggle Bit Clear */
+1 -1
drivers/usb/renesas_usbhs/fifo.c
··· 89 89 list_del_init(&pkt->node); 90 90 } 91 91 92 - static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) 92 + struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) 93 93 { 94 94 return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node); 95 95 }
+1
drivers/usb/renesas_usbhs/fifo.h
··· 97 97 void *buf, int len, int zero, int sequence); 98 98 struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); 99 99 void usbhs_pkt_start(struct usbhs_pipe *pipe); 100 + struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe); 100 101 101 102 #endif /* RENESAS_USB_FIFO_H */
+15 -1
drivers/usb/renesas_usbhs/mod_gadget.c
··· 722 722 struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 723 723 struct device *dev = usbhsg_gpriv_to_dev(gpriv); 724 724 unsigned long flags; 725 + int ret = 0; 725 726 726 727 dev_dbg(dev, "set halt %d (pipe %d)\n", 727 728 halt, usbhs_pipe_number(pipe)); 728 729 729 730 /******************** spin lock ********************/ 730 731 usbhs_lock(priv, flags); 732 + 733 + /* 734 + * According to usb_ep_set_halt()'s description, this function should 735 + * return -EAGAIN if the IN endpoint has any queue or data. Note 736 + * that the usbhs_pipe_is_dir_in() returns false if the pipe is an 737 + * IN endpoint in the gadget mode. 738 + */ 739 + if (!usbhs_pipe_is_dir_in(pipe) && (__usbhsf_pkt_get(pipe) || 740 + usbhs_pipe_contains_transmittable_data(pipe))) { 741 + ret = -EAGAIN; 742 + goto out; 743 + } 731 744 732 745 if (halt) 733 746 usbhs_pipe_stall(pipe); ··· 752 739 else 753 740 usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE); 754 741 742 + out: 755 743 usbhs_unlock(priv, flags); 756 744 /******************** spin unlock ******************/ 757 745 758 - return 0; 746 + return ret; 759 747 } 760 748 761 749 static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
+15
drivers/usb/renesas_usbhs/pipe.c
··· 277 277 return -EBUSY; 278 278 } 279 279 280 + bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe) 281 + { 282 + u16 val; 283 + 284 + /* Do not support for DCP pipe */ 285 + if (usbhs_pipe_is_dcp(pipe)) 286 + return false; 287 + 288 + val = usbhsp_pipectrl_get(pipe); 289 + if (val & INBUFM) 290 + return true; 291 + 292 + return false; 293 + } 294 + 280 295 /* 281 296 * PID ctrl 282 297 */
+1
drivers/usb/renesas_usbhs/pipe.h
··· 83 83 void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, 84 84 int needs_bfre, int bfre_enable); 85 85 int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); 86 + bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe); 86 87 void usbhs_pipe_enable(struct usbhs_pipe *pipe); 87 88 void usbhs_pipe_disable(struct usbhs_pipe *pipe); 88 89 void usbhs_pipe_stall(struct usbhs_pipe *pipe);