diff --git a/build/distributed/udevrules/99-bloom.rules b/build/distributed/udevrules/99-bloom.rules index fac15820..89ecd75d 100644 --- a/build/distributed/udevrules/99-bloom.rules +++ b/build/distributed/udevrules/99-bloom.rules @@ -33,4 +33,7 @@ ACTION=="add", ATTR{idVendor}=="03eb", ATTR{idProduct}=="2180", MODE="0666" # WCH-LinkE ACTION=="add", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="0666" +# WCH-LinkE IAP mode +ACTION=="add", ATTR{idVendor}=="4348", ATTR{idProduct}=="55e0", MODE="0666" + LABEL="bloom_end" diff --git a/src/DebugToolDrivers/USB/UsbDevice.cpp b/src/DebugToolDrivers/USB/UsbDevice.cpp index 8cf1a7c6..5a96e5d6 100644 --- a/src/DebugToolDrivers/USB/UsbDevice.cpp +++ b/src/DebugToolDrivers/USB/UsbDevice.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "src/Logger/Logger.hpp" #include "src/Services/StringService.hpp" @@ -25,6 +26,10 @@ namespace Usb } } + UsbDevice::~UsbDevice() { + this->close(); + } + void UsbDevice::init() { // ::libusb_set_option(this->libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE); auto devices = this->findMatchingDevices(this->vendorId, this->productId); @@ -102,6 +107,32 @@ namespace Usb } } + std::optional UsbDevice::tryDevice(std::uint16_t vendorId, std::uint16_t productId) { + auto device = UsbDevice{vendorId, productId}; + + try { + device.init(); + + } catch (const DeviceNotFound&) { + return std::nullopt; + } + + return device; + } + + bool UsbDevice::waitForDevice(std::uint16_t vendorId, std::uint16_t productId, std::chrono::milliseconds timeout) { + static constexpr auto DELAY = std::chrono::milliseconds{50}; + for (auto i = 0; (i * DELAY) <= timeout; ++i){ + if (!UsbDevice::findMatchingDevices(vendorId, productId).empty()) { + return true; + } + + std::this_thread::sleep_for(DELAY); + } + + return false; + } + std::uint8_t UsbDevice::getFirstEndpointAddress( std::uint8_t interfaceNumber, ::libusb_endpoint_direction direction @@ -152,7 +183,7 @@ namespace Usb std::vector UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) { ::libusb_device** devices = nullptr; ::libusb_device* device; - std::vector matchedDevices; + auto matchedDevices = std::vector{}; auto libusbStatusCode = ::libusb_get_device_list(UsbDevice::libusbContext.get(), &devices); if (libusbStatusCode < 0) { @@ -163,7 +194,7 @@ namespace Usb ssize_t i = 0; while ((device = devices[i++]) != nullptr) { - auto libusbDevice = LibusbDevice(device, ::libusb_unref_device); + auto libusbDevice = LibusbDevice{device, ::libusb_unref_device}; struct ::libusb_device_descriptor desc = {}; if ((libusbStatusCode = ::libusb_get_device_descriptor(device, &desc)) < 0) { @@ -221,8 +252,4 @@ namespace Usb this->libusbDeviceHandle.reset(); this->libusbDevice.reset(); } - - UsbDevice::~UsbDevice() { - this->close(); - } } diff --git a/src/DebugToolDrivers/USB/UsbDevice.hpp b/src/DebugToolDrivers/USB/UsbDevice.hpp index c3e07044..9267b5e4 100644 --- a/src/DebugToolDrivers/USB/UsbDevice.hpp +++ b/src/DebugToolDrivers/USB/UsbDevice.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "src/DebugToolDrivers/DebugTool.hpp" @@ -18,10 +19,16 @@ namespace Usb class UsbDevice { public: + static inline LibusbContext libusbContext = {nullptr, ::libusb_exit}; + + LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device}; + LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close}; + std::uint16_t vendorId; std::uint16_t productId; UsbDevice(std::uint16_t vendorId, std::uint16_t productId); + virtual ~UsbDevice(); UsbDevice(const UsbDevice& other) = delete; UsbDevice& operator = (const UsbDevice& other) = delete; @@ -61,20 +68,13 @@ namespace Usb */ virtual void setConfiguration(std::uint8_t configurationIndex); - virtual ~UsbDevice(); + static std::optional tryDevice(std::uint16_t vendorId, std::uint16_t productId); + static bool waitForDevice(std::uint16_t vendorId, std::uint16_t productId, std::chrono::milliseconds timeout); protected: - static inline LibusbContext libusbContext = {nullptr, ::libusb_exit}; - - LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device}; - LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close}; - - std::vector findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId); - + static std::vector findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId); LibusbConfigDescriptor getConfigDescriptor(std::optional configurationIndex = std::nullopt); - void detachKernelDriverFromInterface(std::uint8_t interfaceNumber); - void close(); }; } diff --git a/src/DebugToolDrivers/USB/UsbInterface.cpp b/src/DebugToolDrivers/USB/UsbInterface.cpp index 45aeb935..bf65e41e 100644 --- a/src/DebugToolDrivers/USB/UsbInterface.cpp +++ b/src/DebugToolDrivers/USB/UsbInterface.cpp @@ -20,6 +20,10 @@ namespace Usb , deviceHandle(deviceHandle) {} + UsbInterface::~UsbInterface() { + this->close(); + } + void UsbInterface::init() { Logger::debug("Claiming USB interface (number: " + std::to_string(this->interfaceNumber) + ")"); @@ -37,12 +41,14 @@ namespace Usb void UsbInterface::close() { if (this->claimed) { const auto statusCode = ::libusb_release_interface(this->deviceHandle, this->interfaceNumber); - if (statusCode != 0) { + if (statusCode != 0 && statusCode != ::LIBUSB_ERROR_NO_DEVICE) { Logger::error( "Failed to release USB interface (number: " + std::to_string(this->interfaceNumber) + ") - error code: " + std::to_string(statusCode) ); } + + this->claimed = false; } } diff --git a/src/DebugToolDrivers/USB/UsbInterface.hpp b/src/DebugToolDrivers/USB/UsbInterface.hpp index 2ced6497..073fb01b 100644 --- a/src/DebugToolDrivers/USB/UsbInterface.hpp +++ b/src/DebugToolDrivers/USB/UsbInterface.hpp @@ -23,6 +23,8 @@ namespace Usb ::libusb_device_handle* deviceHandle ); + ~UsbInterface(); + UsbInterface(const UsbInterface& other) = delete; UsbInterface& operator = (const UsbInterface& other) = delete; diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.cpp b/src/DebugToolDrivers/WCH/WchLinkBase.cpp index c3395885..b76f5767 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.cpp @@ -1,7 +1,10 @@ #include "WchLinkBase.hpp" +#include + #include "Protocols/WchLink/WchLinkInterface.hpp" +#include "src/TargetController/Exceptions/DeviceNotFound.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" #include "src/Logger/Logger.hpp" @@ -15,16 +18,49 @@ namespace DebugToolDrivers::Wch WchLinkVariant variant, std::uint16_t vendorId, std::uint16_t productId, + std::uint16_t iapVendorId, + std::uint16_t iapProductId, std::uint8_t wchLinkUsbInterfaceNumber ) : UsbDevice(vendorId, productId) , toolConfig(WchLinkToolConfig{toolConfig}) + , iapVendorId(iapVendorId) + , iapProductId(iapProductId) , variant(variant) , wchLinkUsbInterfaceNumber(wchLinkUsbInterfaceNumber) {} void WchLinkBase::init() { - UsbDevice::init(); + using Exceptions::DeviceNotFound; + + try { + UsbDevice::init(); + + } catch (const DeviceNotFound& exception) { + auto iapDevice = Usb::UsbDevice::tryDevice(this->iapVendorId, this->iapProductId); + + if (!iapDevice.has_value()) { + throw exception; + } + + if (!this->toolConfig.exitIapMode) { + throw DeviceInitializationFailure{ + "Device found in IAP mode - Bloom can have the device exit this mode - see the 'exit_iap_mode' " + "tool config parameter, for more" + }; + } + + Logger::warning("Found device in IAP mode - attempting exit operation"); + this->exitIapMode(*iapDevice); + + Logger::info("Waiting for device to re-enumerate..."); + if (!Usb::UsbDevice::waitForDevice(this->vendorId, this->productId, std::chrono::seconds{8})) { + throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"}; + } + + Logger::info("Re-enumerated device found - IAP exit operation was successful"); + UsbDevice::init(); + } this->detachKernelDriverFromInterface(this->wchLinkUsbInterfaceNumber); @@ -93,4 +129,15 @@ namespace DebugToolDrivers::Wch return *(this->cachedDeviceInfo); } + + void WchLinkBase::exitIapMode(UsbDevice& iapDevice) const { + static constexpr auto IAP_INTERFACE_NUMBER = std::uint8_t{0}; + static constexpr auto IAP_COMMAND_ENDPOINT_ADDRESS = std::uint8_t{0x02}; + + auto interface = Usb::UsbInterface{IAP_INTERFACE_NUMBER, iapDevice.libusbDeviceHandle.get()}; + interface.init(); + + static constexpr auto COMMAND_BUFFER = std::to_array({0x83}); + interface.writeBulk(IAP_COMMAND_ENDPOINT_ADDRESS, COMMAND_BUFFER, 64); + } } diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.hpp b/src/DebugToolDrivers/WCH/WchLinkBase.hpp index 340b7237..82c1fcbe 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.hpp @@ -30,6 +30,8 @@ namespace DebugToolDrivers::Wch WchLinkVariant variant, std::uint16_t vendorId, std::uint16_t productId, + std::uint16_t iapVendorId, + std::uint16_t iapProductId, std::uint8_t wchLinkUsbInterfaceNumber ); @@ -50,6 +52,9 @@ namespace DebugToolDrivers::Wch protected: WchLinkToolConfig toolConfig; + std::uint16_t iapVendorId; + std::uint16_t iapProductId; + bool initialised = false; WchLinkVariant variant; @@ -62,5 +67,6 @@ namespace DebugToolDrivers::Wch mutable std::optional cachedDeviceInfo = std::nullopt; const DeviceInfo& getDeviceInfo() const; + void exitIapMode(Usb::UsbDevice& iapDevice) const; }; } diff --git a/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp b/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp index 8af678e6..062c469c 100644 --- a/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp @@ -8,6 +8,8 @@ namespace DebugToolDrivers::Wch WchLinkVariant::LINK_E_CH32V307, WchLinkE::USB_VENDOR_ID, WchLinkE::USB_PRODUCT_ID, + WchLinkE::IAP_USB_VENDOR_ID, + WchLinkE::IAP_USB_PRODUCT_ID, WchLinkE::WCH_LINK_INTERFACE_NUMBER ) {} diff --git a/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp b/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp index 66267a6b..b50ee221 100644 --- a/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp @@ -21,6 +21,8 @@ namespace DebugToolDrivers::Wch public: static const inline std::uint16_t USB_VENDOR_ID = 0x1a86; static const inline std::uint16_t USB_PRODUCT_ID = 0x8010; + static const inline std::uint16_t IAP_USB_VENDOR_ID = 0x4348; + static const inline std::uint16_t IAP_USB_PRODUCT_ID = 0x55e0; static const inline std::uint8_t WCH_LINK_INTERFACE_NUMBER = 0; explicit WchLinkE(const DebugToolConfig& toolConfig); diff --git a/src/DebugToolDrivers/WCH/WchLinkToolConfig.cpp b/src/DebugToolDrivers/WCH/WchLinkToolConfig.cpp index 42c70a8b..e1081801 100644 --- a/src/DebugToolDrivers/WCH/WchLinkToolConfig.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkToolConfig.cpp @@ -7,6 +7,10 @@ namespace DebugToolDrivers::Wch { const auto& toolNode = toolConfig.toolNode; + if (toolNode["exit_iap_mode"]) { + this->exitIapMode = toolNode["exit_iap_mode"].as(this->exitIapMode); + } + if (toolNode["riscv_debug_translator"]) { this->riscVDebugTranslatorConfig = ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig{ toolNode["riscv_debug_translator"] diff --git a/src/DebugToolDrivers/WCH/WchLinkToolConfig.hpp b/src/DebugToolDrivers/WCH/WchLinkToolConfig.hpp index 2ce70a99..94684f2f 100644 --- a/src/DebugToolDrivers/WCH/WchLinkToolConfig.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkToolConfig.hpp @@ -12,7 +12,7 @@ namespace DebugToolDrivers::Wch */ struct WchLinkToolConfig: public DebugToolConfig { - public: + bool exitIapMode = true; ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig riscVDebugTranslatorConfig = {}; explicit WchLinkToolConfig(const DebugToolConfig& toolConfig);