From 0c6190fa3cfeafd773b51b751a473d6775c23309 Mon Sep 17 00:00:00 2001 From: P33M <2474547+P33M@users.noreply.github.com> Date: Wed, 14 Aug 2019 14:35:50 +0100 Subject: [PATCH] dwc_otg: use align_buf for small IN control transfers (#3150) The hardware will do a 4-byte write to memory on any IN packet received that is between 1 and 3 bytes long. This tramples memory in the uvcvideo driver, as it uses a sequence of 1- and 2-byte control transfers to query the min/max/range/step of each individual camera control and gives us buffers that are offsets into a struct. Catch small control transfers in the data phase and use the align_buf to bounce the correct number of bytes into the URB's buffer. In general, short packets on non-control endpoints should be OK as URBs should have enough buffer space for a wMaxPacket size transfer. See: https://github.com/raspberrypi/linux/issues/3148 Signed-off-by: Jonathan Bell --- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -1182,6 +1182,7 @@ static void assign_and_init_hc(dwc_otg_h dwc_otg_qtd_t *qtd; dwc_otg_hcd_urb_t *urb; void* ptr = NULL; + uint16_t wLength; uint32_t intr_enable; unsigned long flags; gintmsk_data_t gintmsk = { .d32 = 0, }; @@ -1293,6 +1294,23 @@ static void assign_and_init_hc(dwc_otg_h break; case DWC_OTG_CONTROL_DATA: DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); + /* + * Hardware bug: small IN packets with length < 4 + * cause a 4-byte write to memory. We can only catch + * the case where we know a short packet is going to be + * returned in a control transfer, as the length is + * specified in the setup packet. This is only an issue + * for drivers that insist on packing a device's various + * properties into a struct and querying them one at a + * time (uvcvideo). + * Force the use of align_buf so that the subsequent + * memcpy puts the right number of bytes in the URB's + * buffer. + */ + wLength = ((uint16_t *)urb->setup_packet)[3]; + if (hc->ep_is_in && wLength < 4) + ptr = hc->xfer_buff; + hc->data_pid_start = qtd->data_toggle; break; case DWC_OTG_CONTROL_STATUS: