Automatically exit IAP mode on WCH-Link tools

This commit is contained in:
Nav
2024-12-08 23:33:07 +00:00
parent 1477719264
commit 3afcb65f31
11 changed files with 118 additions and 19 deletions

View File

@@ -33,4 +33,7 @@ ACTION=="add", ATTR{idVendor}=="03eb", ATTR{idProduct}=="2180", MODE="0666"
# WCH-LinkE # WCH-LinkE
ACTION=="add", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="0666" 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" LABEL="bloom_end"

View File

@@ -2,6 +2,7 @@
#include <libusb-1.0/libusb.h> #include <libusb-1.0/libusb.h>
#include <array> #include <array>
#include <thread>
#include "src/Logger/Logger.hpp" #include "src/Logger/Logger.hpp"
#include "src/Services/StringService.hpp" #include "src/Services/StringService.hpp"
@@ -25,6 +26,10 @@ namespace Usb
} }
} }
UsbDevice::~UsbDevice() {
this->close();
}
void UsbDevice::init() { void UsbDevice::init() {
// ::libusb_set_option(this->libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE); // ::libusb_set_option(this->libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
auto devices = this->findMatchingDevices(this->vendorId, this->productId); auto devices = this->findMatchingDevices(this->vendorId, this->productId);
@@ -102,6 +107,32 @@ namespace Usb
} }
} }
std::optional<UsbDevice> 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 UsbDevice::getFirstEndpointAddress(
std::uint8_t interfaceNumber, std::uint8_t interfaceNumber,
::libusb_endpoint_direction direction ::libusb_endpoint_direction direction
@@ -152,7 +183,7 @@ namespace Usb
std::vector<LibusbDevice> UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) { std::vector<LibusbDevice> UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) {
::libusb_device** devices = nullptr; ::libusb_device** devices = nullptr;
::libusb_device* device; ::libusb_device* device;
std::vector<LibusbDevice> matchedDevices; auto matchedDevices = std::vector<LibusbDevice>{};
auto libusbStatusCode = ::libusb_get_device_list(UsbDevice::libusbContext.get(), &devices); auto libusbStatusCode = ::libusb_get_device_list(UsbDevice::libusbContext.get(), &devices);
if (libusbStatusCode < 0) { if (libusbStatusCode < 0) {
@@ -163,7 +194,7 @@ namespace Usb
ssize_t i = 0; ssize_t i = 0;
while ((device = devices[i++]) != nullptr) { while ((device = devices[i++]) != nullptr) {
auto libusbDevice = LibusbDevice(device, ::libusb_unref_device); auto libusbDevice = LibusbDevice{device, ::libusb_unref_device};
struct ::libusb_device_descriptor desc = {}; struct ::libusb_device_descriptor desc = {};
if ((libusbStatusCode = ::libusb_get_device_descriptor(device, &desc)) < 0) { if ((libusbStatusCode = ::libusb_get_device_descriptor(device, &desc)) < 0) {
@@ -221,8 +252,4 @@ namespace Usb
this->libusbDeviceHandle.reset(); this->libusbDeviceHandle.reset();
this->libusbDevice.reset(); this->libusbDevice.reset();
} }
UsbDevice::~UsbDevice() {
this->close();
}
} }

View File

@@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <optional> #include <optional>
#include <libusb-1.0/libusb.h> #include <libusb-1.0/libusb.h>
#include <chrono>
#include "src/DebugToolDrivers/DebugTool.hpp" #include "src/DebugToolDrivers/DebugTool.hpp"
@@ -18,10 +19,16 @@ namespace Usb
class UsbDevice class UsbDevice
{ {
public: 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 vendorId;
std::uint16_t productId; std::uint16_t productId;
UsbDevice(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(const UsbDevice& other) = delete;
UsbDevice& operator = (const UsbDevice& other) = delete; UsbDevice& operator = (const UsbDevice& other) = delete;
@@ -61,20 +68,13 @@ namespace Usb
*/ */
virtual void setConfiguration(std::uint8_t configurationIndex); virtual void setConfiguration(std::uint8_t configurationIndex);
virtual ~UsbDevice(); static std::optional<UsbDevice> 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: protected:
static inline LibusbContext libusbContext = {nullptr, ::libusb_exit}; static std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device};
LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close};
std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
LibusbConfigDescriptor getConfigDescriptor(std::optional<std::uint8_t> configurationIndex = std::nullopt); LibusbConfigDescriptor getConfigDescriptor(std::optional<std::uint8_t> configurationIndex = std::nullopt);
void detachKernelDriverFromInterface(std::uint8_t interfaceNumber); void detachKernelDriverFromInterface(std::uint8_t interfaceNumber);
void close(); void close();
}; };
} }

View File

@@ -20,6 +20,10 @@ namespace Usb
, deviceHandle(deviceHandle) , deviceHandle(deviceHandle)
{} {}
UsbInterface::~UsbInterface() {
this->close();
}
void UsbInterface::init() { void UsbInterface::init() {
Logger::debug("Claiming USB interface (number: " + std::to_string(this->interfaceNumber) + ")"); Logger::debug("Claiming USB interface (number: " + std::to_string(this->interfaceNumber) + ")");
@@ -37,12 +41,14 @@ namespace Usb
void UsbInterface::close() { void UsbInterface::close() {
if (this->claimed) { if (this->claimed) {
const auto statusCode = ::libusb_release_interface(this->deviceHandle, this->interfaceNumber); const auto statusCode = ::libusb_release_interface(this->deviceHandle, this->interfaceNumber);
if (statusCode != 0) { if (statusCode != 0 && statusCode != ::LIBUSB_ERROR_NO_DEVICE) {
Logger::error( Logger::error(
"Failed to release USB interface (number: " + std::to_string(this->interfaceNumber) "Failed to release USB interface (number: " + std::to_string(this->interfaceNumber)
+ ") - error code: " + std::to_string(statusCode) + ") - error code: " + std::to_string(statusCode)
); );
} }
this->claimed = false;
} }
} }

View File

@@ -23,6 +23,8 @@ namespace Usb
::libusb_device_handle* deviceHandle ::libusb_device_handle* deviceHandle
); );
~UsbInterface();
UsbInterface(const UsbInterface& other) = delete; UsbInterface(const UsbInterface& other) = delete;
UsbInterface& operator = (const UsbInterface& other) = delete; UsbInterface& operator = (const UsbInterface& other) = delete;

View File

@@ -1,7 +1,10 @@
#include "WchLinkBase.hpp" #include "WchLinkBase.hpp"
#include <chrono>
#include "Protocols/WchLink/WchLinkInterface.hpp" #include "Protocols/WchLink/WchLinkInterface.hpp"
#include "src/TargetController/Exceptions/DeviceNotFound.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
#include "src/Logger/Logger.hpp" #include "src/Logger/Logger.hpp"
@@ -15,16 +18,49 @@ namespace DebugToolDrivers::Wch
WchLinkVariant variant, WchLinkVariant variant,
std::uint16_t vendorId, std::uint16_t vendorId,
std::uint16_t productId, std::uint16_t productId,
std::uint16_t iapVendorId,
std::uint16_t iapProductId,
std::uint8_t wchLinkUsbInterfaceNumber std::uint8_t wchLinkUsbInterfaceNumber
) )
: UsbDevice(vendorId, productId) : UsbDevice(vendorId, productId)
, toolConfig(WchLinkToolConfig{toolConfig}) , toolConfig(WchLinkToolConfig{toolConfig})
, iapVendorId(iapVendorId)
, iapProductId(iapProductId)
, variant(variant) , variant(variant)
, wchLinkUsbInterfaceNumber(wchLinkUsbInterfaceNumber) , wchLinkUsbInterfaceNumber(wchLinkUsbInterfaceNumber)
{} {}
void WchLinkBase::init() { 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); this->detachKernelDriverFromInterface(this->wchLinkUsbInterfaceNumber);
@@ -93,4 +129,15 @@ namespace DebugToolDrivers::Wch
return *(this->cachedDeviceInfo); 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<unsigned char>({0x83});
interface.writeBulk(IAP_COMMAND_ENDPOINT_ADDRESS, COMMAND_BUFFER, 64);
}
} }

View File

@@ -30,6 +30,8 @@ namespace DebugToolDrivers::Wch
WchLinkVariant variant, WchLinkVariant variant,
std::uint16_t vendorId, std::uint16_t vendorId,
std::uint16_t productId, std::uint16_t productId,
std::uint16_t iapVendorId,
std::uint16_t iapProductId,
std::uint8_t wchLinkUsbInterfaceNumber std::uint8_t wchLinkUsbInterfaceNumber
); );
@@ -50,6 +52,9 @@ namespace DebugToolDrivers::Wch
protected: protected:
WchLinkToolConfig toolConfig; WchLinkToolConfig toolConfig;
std::uint16_t iapVendorId;
std::uint16_t iapProductId;
bool initialised = false; bool initialised = false;
WchLinkVariant variant; WchLinkVariant variant;
@@ -62,5 +67,6 @@ namespace DebugToolDrivers::Wch
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt; mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
const DeviceInfo& getDeviceInfo() const; const DeviceInfo& getDeviceInfo() const;
void exitIapMode(Usb::UsbDevice& iapDevice) const;
}; };
} }

View File

@@ -8,6 +8,8 @@ namespace DebugToolDrivers::Wch
WchLinkVariant::LINK_E_CH32V307, WchLinkVariant::LINK_E_CH32V307,
WchLinkE::USB_VENDOR_ID, WchLinkE::USB_VENDOR_ID,
WchLinkE::USB_PRODUCT_ID, WchLinkE::USB_PRODUCT_ID,
WchLinkE::IAP_USB_VENDOR_ID,
WchLinkE::IAP_USB_PRODUCT_ID,
WchLinkE::WCH_LINK_INTERFACE_NUMBER WchLinkE::WCH_LINK_INTERFACE_NUMBER
) )
{} {}

View File

@@ -21,6 +21,8 @@ namespace DebugToolDrivers::Wch
public: public:
static const inline std::uint16_t USB_VENDOR_ID = 0x1a86; 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 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; static const inline std::uint8_t WCH_LINK_INTERFACE_NUMBER = 0;
explicit WchLinkE(const DebugToolConfig& toolConfig); explicit WchLinkE(const DebugToolConfig& toolConfig);

View File

@@ -7,6 +7,10 @@ namespace DebugToolDrivers::Wch
{ {
const auto& toolNode = toolConfig.toolNode; const auto& toolNode = toolConfig.toolNode;
if (toolNode["exit_iap_mode"]) {
this->exitIapMode = toolNode["exit_iap_mode"].as<bool>(this->exitIapMode);
}
if (toolNode["riscv_debug_translator"]) { if (toolNode["riscv_debug_translator"]) {
this->riscVDebugTranslatorConfig = ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig{ this->riscVDebugTranslatorConfig = ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig{
toolNode["riscv_debug_translator"] toolNode["riscv_debug_translator"]

View File

@@ -12,7 +12,7 @@ namespace DebugToolDrivers::Wch
*/ */
struct WchLinkToolConfig: public DebugToolConfig struct WchLinkToolConfig: public DebugToolConfig
{ {
public: bool exitIapMode = true;
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig riscVDebugTranslatorConfig = {}; ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig riscVDebugTranslatorConfig = {};
explicit WchLinkToolConfig(const DebugToolConfig& toolConfig); explicit WchLinkToolConfig(const DebugToolConfig& toolConfig);