From 183526bc0486c9759c6c811a8ab963ffbbe17263 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Mon, 15 Jan 2018 21:09:31 -0600 Subject: [PATCH] Fix LPC17XX and LPC40XX USB race condition If a SETUP packet arrives shortly after an IN then the packets will be processed in the wrong order - SETUP first then IN. This causes the subsequent control transfer to fail. Fix this problem by processing IN packets before processing SETUP packets. --- .../targets/TARGET_NXP/USBHAL_LPC17.cpp | 25 ++++++++++++++----- .../targets/TARGET_NXP/USBHAL_LPC40.cpp | 25 ++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC17.cpp b/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC17.cpp index 61dc87e0aa..5d516f5304 100644 --- a/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC17.cpp +++ b/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC17.cpp @@ -590,6 +590,25 @@ void USBHAL::usbisr(void) { if (LPC_USB->USBDevIntSt & EP_SLOW) { // (Slow) Endpoint Interrupt + // Process IN packets before SETUP packets + // Note - order of OUT and SETUP does not matter as OUT packets + // are clobbered by SETUP packets and thus ignored. + // + // A SETUP packet can arrive at any time where as an IN packet is + // only sent after calling EP0write and an OUT packet after EP0read. + // The functions EP0write and EP0read are called only in response to + // a setup packet or IN/OUT packets sent in response to that + // setup packet. Therefore, if an IN or OUT packet is pending + // at the same time as a SETUP packet, the IN or OUT packet belongs + // to the previous control transfer and should either be processed + // before the SETUP packet (in the case of IN) or dropped (in the + // case of OUT as SETUP clobbers the OUT data). + if (LPC_USB->USBEpIntSt & EP(EP0IN)) { + selectEndpointClearInterrupt(EP0IN); + LPC_USB->USBDevIntClr = EP_SLOW; + EP0in(); + } + // Process each endpoint interrupt if (LPC_USB->USBEpIntSt & EP(EP0OUT)) { if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP) { @@ -601,12 +620,6 @@ void USBHAL::usbisr(void) { LPC_USB->USBDevIntClr = EP_SLOW; } - if (LPC_USB->USBEpIntSt & EP(EP0IN)) { - selectEndpointClearInterrupt(EP0IN); - LPC_USB->USBDevIntClr = EP_SLOW; - EP0in(); - } - for (uint8_t num = 2; num < 16*2; num++) { if (LPC_USB->USBEpIntSt & EP(num)) { selectEndpointClearInterrupt(num); diff --git a/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC40.cpp b/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC40.cpp index a5d7b44401..7147366239 100644 --- a/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC40.cpp +++ b/features/unsupported/USBDevice/targets/TARGET_NXP/USBHAL_LPC40.cpp @@ -595,6 +595,25 @@ void USBHAL::usbisr(void) { if (LPC_USB->DevIntSt & EP_SLOW) { // (Slow) Endpoint Interrupt + // Process IN packets before SETUP packets + // Note - order of OUT and SETUP does not matter as OUT packets + // are clobbered by SETUP packets and thus ignored. + // + // A SETUP packet can arrive at any time where as an IN packet is + // only sent after calling EP0write and an OUT packet after EP0read. + // The functions EP0write and EP0read are called only in response to + // a setup packet or IN/OUT packets sent in response to that + // setup packet. Therefore, if an IN or OUT packet is pending + // at the same time as a SETUP packet, the IN or OUT packet belongs + // to the previous control transfer and should either be processed + // before the SETUP packet (in the case of IN) or dropped (in the + // case of OUT as SETUP clobbers the OUT data). + if (LPC_USB->EpIntSt & EP(EP0IN)) { + selectEndpointClearInterrupt(EP0IN); + LPC_USB->DevIntClr = EP_SLOW; + EP0in(); + } + // Process each endpoint interrupt if (LPC_USB->EpIntSt & EP(EP0OUT)) { if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP) { @@ -606,12 +625,6 @@ void USBHAL::usbisr(void) { LPC_USB->DevIntClr = EP_SLOW; } - if (LPC_USB->EpIntSt & EP(EP0IN)) { - selectEndpointClearInterrupt(EP0IN); - LPC_USB->DevIntClr = EP_SLOW; - EP0in(); - } - for (uint8_t num = 2; num < 16*2; num++) { if (LPC_USB->EpIntSt & EP(num)) { selectEndpointClearInterrupt(num);