Automatically exit IAP mode on WCH-Link tools
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace Usb
|
||||
::libusb_device_handle* deviceHandle
|
||||
);
|
||||
|
||||
~UsbInterface();
|
||||
|
||||
UsbInterface(const UsbInterface& other) = delete;
|
||||
UsbInterface& operator = (const UsbInterface& other) = delete;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
{}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user