diff --git a/common_arm/usb/usb_cdc_at32.c b/common_arm/usb/usb_cdc_at32.c index 2c6b7d4b8..2edd1aaa6 100644 --- a/common_arm/usb/usb_cdc_at32.c +++ b/common_arm/usb/usb_cdc_at32.c @@ -431,10 +431,23 @@ uint16_t usb_available_length(void) { * @return */ bool usb_poll_validate_length(void) { +#ifndef AS_BOOTROM + // A previous USB packet may already contain the next command. + if (usb_read_ng_has_buffered_data()) { + return true; + } +#endif if (usb_poll() == false) { return false; } - return usb_available_length() > 0; + if (usb_available_length() > 0) { + return true; + } + + // Consume a terminating zero-length packet and rearm the OUT endpoint. + // Otherwise no subsequent command can be received. + usb_vcp_get_rxdata(udev, NULL, 0); + return false; } /** diff --git a/common_arm/usb/usb_cdc_at91.c b/common_arm/usb/usb_cdc_at91.c index 3e80b36a9..f7ed19963 100644 --- a/common_arm/usb/usb_cdc_at91.c +++ b/common_arm/usb/usb_cdc_at91.c @@ -184,6 +184,7 @@ static uint16_t usb_read_ng_data_available(void) { static uint8_t usb_read_ng_data_read(void) { return pUdp->UDP_FDR[AT91C_EP_OUT]; } +#endif // Implemented for read_ng static void usb_read_ng_clear(void) { @@ -196,6 +197,7 @@ static void usb_read_ng_clear(void) { } } +#ifndef AS_BOOTROM // Instance for 'read_ng' apis static const usb_read_ng_config_t g_usb_read_ng_config = { .is_link_ready = usb_read_ng_link_ready, @@ -372,13 +374,25 @@ FORCE_INLINE uint16_t usb_available_length(void) { bug. **/ bool usb_poll_validate_length(void) { +#ifndef AS_BOOTROM + // A previous USB packet may already contain the next command. + if (usb_read_ng_has_buffered_data()) { + return true; + } +#endif // Reuse 'usb_poll()' implemented. if (usb_poll() == false) { return false; } - // Why code this: return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0); - // For speed? but 'usb_available_length()' is a inline function. - return (usb_available_length() > 0); + if (usb_available_length() > 0) { + return true; + } + + // A transfer whose size is an exact multiple of the endpoint size can end + // with a zero-length packet. Acknowledge it here; otherwise this receive + // bank remains selected forever and data queued in the other bank stalls. + usb_read_ng_clear(); + return false; } /* diff --git a/tools/usb_ping_test.py b/tools/usb_ping_test.py new file mode 100644 index 000000000..bd91adc56 --- /dev/null +++ b/tools/usb_ping_test.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +# SPDX-License-Identifier: GPL-3.0-or-later +"""Test USB packet boundaries and buffered commands on a connected PM3 (POSIX).""" + +import argparse +import os +import select +import struct +import termios +import time +import tty + + +def ping(payload): + return struct.pack('