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
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"

View File

@@ -2,6 +2,7 @@
#include <libusb-1.0/libusb.h>
#include <array>
#include <thread>
#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> 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<LibusbDevice> UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) {
::libusb_device** devices = nullptr;
::libusb_device* device;
std::vector<LibusbDevice> matchedDevices;
auto matchedDevices = std::vector<LibusbDevice>{};
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();
}
}

View File

@@ -5,6 +5,7 @@
#include <vector>
#include <optional>
#include <libusb-1.0/libusb.h>
#include <chrono>
#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<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:
static inline LibusbContext libusbContext = {nullptr, ::libusb_exit};
LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device};
LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close};
std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
static std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
LibusbConfigDescriptor getConfigDescriptor(std::optional<std::uint8_t> configurationIndex = std::nullopt);
void detachKernelDriverFromInterface(std::uint8_t interfaceNumber);
void close();
};
}

View File

@@ -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;
}
}

View File

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

View File

@@ -1,7 +1,10 @@
#include "WchLinkBase.hpp"
#include <chrono>
#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<unsigned char>({0x83});
interface.writeBulk(IAP_COMMAND_ENDPOINT_ADDRESS, COMMAND_BUFFER, 64);
}
}

View File

@@ -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<DeviceInfo> cachedDeviceInfo = std::nullopt;
const DeviceInfo& getDeviceInfo() const;
void exitIapMode(Usb::UsbDevice& iapDevice) const;
};
}

View File

@@ -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
)
{}

View File

@@ -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);

View File

@@ -7,6 +7,10 @@ namespace DebugToolDrivers::Wch
{
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"]) {
this->riscVDebugTranslatorConfig = ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig{
toolNode["riscv_debug_translator"]

View File

@@ -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);