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
|
# 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"
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user