Began implementation of WCH-Link protocol, for the WCH-LinkE debug tool.
Foundations have been laid.
This commit is contained in:
@@ -33,4 +33,7 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkInterface.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,3 +11,5 @@
|
|||||||
#include "src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.hpp"
|
#include "src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.hpp"
|
||||||
#include "src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.hpp"
|
#include "src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.hpp"
|
||||||
#include "src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp"
|
#include "src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp"
|
||||||
|
|||||||
21
src/DebugToolDrivers/WCH/DeviceInfo.hpp
Normal file
21
src/DebugToolDrivers/WCH/DeviceInfo.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/WchFirmwareVersion.hpp"
|
||||||
|
#include "src/DebugToolDrivers/WCH/WchGeneric.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
class DeviceInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WchFirmwareVersion firmwareVersion;
|
||||||
|
std::optional<WchLinkVariant> variant;
|
||||||
|
|
||||||
|
explicit DeviceInfo(WchFirmwareVersion firmwareVersion, std::optional<WchLinkVariant> variant)
|
||||||
|
: firmwareVersion(firmwareVersion)
|
||||||
|
, variant(variant)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/Response.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
template <class PayloadContainerType = std::vector<unsigned char>>
|
||||||
|
class Command
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
std::is_same_v<typename PayloadContainerType::value_type, unsigned char>,
|
||||||
|
"Invalid payload container type"
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ExpectedResponseType = Responses::Response;
|
||||||
|
|
||||||
|
std::uint8_t commandId;
|
||||||
|
PayloadContainerType payload;
|
||||||
|
|
||||||
|
explicit Command(std::uint8_t commandId)
|
||||||
|
: commandId(commandId)
|
||||||
|
{};
|
||||||
|
|
||||||
|
virtual ~Command() = default;
|
||||||
|
|
||||||
|
Command(const Command& other) = default;
|
||||||
|
Command(Command&& other) noexcept = default;
|
||||||
|
|
||||||
|
Command& operator = (const Command& other) = default;
|
||||||
|
Command& operator = (Command&& other) noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] auto getRawCommand() const {
|
||||||
|
assert(this->payload.size() <= 256);
|
||||||
|
|
||||||
|
auto rawCommand = std::vector<unsigned char>(3 + this->payload.size());
|
||||||
|
|
||||||
|
rawCommand[0] = 0x81;
|
||||||
|
rawCommand[1] = this->commandId;
|
||||||
|
rawCommand[2] = static_cast<std::uint8_t >(this->payload.size());
|
||||||
|
|
||||||
|
if (!this->payload.empty()) {
|
||||||
|
std::copy(this->payload.begin(), this->payload.end(), rawCommand.begin() + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawCommand;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands::Control
|
||||||
|
{
|
||||||
|
class GetDeviceInfo: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GetDeviceInfo()
|
||||||
|
: Command(0x0d)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x01
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Responses
|
||||||
|
{
|
||||||
|
class Response
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<unsigned char> payload;
|
||||||
|
|
||||||
|
explicit Response(const std::vector<unsigned char>& payload)
|
||||||
|
: payload(payload)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include "WchLinkInterface.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "src/Helpers/BiMap.hpp"
|
||||||
|
|
||||||
|
#include "Commands/Control/GetDeviceInfo.hpp"
|
||||||
|
|
||||||
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||||
|
{
|
||||||
|
using namespace Exceptions;
|
||||||
|
|
||||||
|
WchLinkInterface::WchLinkInterface(Usb::UsbInterface& usbInterface)
|
||||||
|
: usbInterface(usbInterface)
|
||||||
|
{}
|
||||||
|
|
||||||
|
DeviceInfo WchLinkInterface::getDeviceInfo() {
|
||||||
|
const auto response = this->sendCommandAndWaitForResponse(
|
||||||
|
Commands::Control::GetDeviceInfo()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.payload.size() < 3) {
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Cannot construct DeviceInfo response - invalid payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto variantsById = BiMap<std::uint8_t, WchLinkVariant>({
|
||||||
|
{0x01, WchLinkVariant::LINK_CH549},
|
||||||
|
{0x02, WchLinkVariant::LINK_E_CH32V307},
|
||||||
|
{0x03, WchLinkVariant::LINK_S_CH32V203},
|
||||||
|
});
|
||||||
|
|
||||||
|
return DeviceInfo(
|
||||||
|
WchFirmwareVersion(response.payload[0], response.payload[1]),
|
||||||
|
response.payload.size() >= 4
|
||||||
|
? std::optional(variantsById.valueAt(response.payload[2]).value_or(WchLinkVariant::UNKNOWN))
|
||||||
|
: std::nullopt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/USB/UsbInterface.hpp"
|
||||||
|
|
||||||
|
#include "Commands/Command.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/DeviceInfo.hpp"
|
||||||
|
|
||||||
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||||
|
{
|
||||||
|
class WchLinkInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit WchLinkInterface(Usb::UsbInterface& usbInterface);
|
||||||
|
|
||||||
|
DeviceInfo getDeviceInfo();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr std::uint8_t USB_ENDPOINT_IN = 0x81;
|
||||||
|
static constexpr std::uint8_t USB_ENDPOINT_OUT = 0x01;
|
||||||
|
|
||||||
|
Usb::UsbInterface& usbInterface;
|
||||||
|
|
||||||
|
template <class CommandType>
|
||||||
|
auto sendCommandAndWaitForResponse(const CommandType& command) {
|
||||||
|
this->usbInterface.writeBulk(WchLinkInterface::USB_ENDPOINT_OUT, command.getRawCommand());
|
||||||
|
|
||||||
|
const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_ENDPOINT_IN);
|
||||||
|
|
||||||
|
if (rawResponse.size() < 3) {
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Invalid response size from device");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first byte of the response should be 0x82 (for success) or 0x81 (for failure)
|
||||||
|
if ((rawResponse[0] != 0x81 && rawResponse[0] != 0x82)) {
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Invalid response code from device");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawResponse[0] == 0x81) {
|
||||||
|
// TODO: Create ErrorResponse exception class and throw an instance of it here.
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Error response");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawResponse[1] != command.commandId) {
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Missing/invalid command ID in response from device");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rawResponse.size() - 3) != rawResponse[2]) {
|
||||||
|
throw Exceptions::DeviceCommunicationFailure("Actual response payload size mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
return typename CommandType::ExpectedResponseType(
|
||||||
|
std::vector<unsigned char>(rawResponse.begin() + 3, rawResponse.end())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
48
src/DebugToolDrivers/WCH/WchFirmwareVersion.hpp
Normal file
48
src/DebugToolDrivers/WCH/WchFirmwareVersion.hpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
class WchFirmwareVersion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::uint8_t major = 0;
|
||||||
|
std::uint8_t minor = 0;
|
||||||
|
|
||||||
|
WchFirmwareVersion() = default;
|
||||||
|
WchFirmwareVersion(std::uint8_t major, std::uint8_t minor)
|
||||||
|
: major{major}
|
||||||
|
, minor{minor}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string toString() const {
|
||||||
|
return std::to_string(this->major) + "." + std::to_string(this->minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const WchFirmwareVersion& other) const {
|
||||||
|
return this->major == other.major && this->minor == other.minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const WchFirmwareVersion& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator < (const WchFirmwareVersion& other) const {
|
||||||
|
return this->major < other.major || (this->major == other.major && this->minor < other.minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator > (const WchFirmwareVersion& other) const {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator <= (const WchFirmwareVersion& other) const {
|
||||||
|
return !(other < *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator >= (const WchFirmwareVersion& other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
14
src/DebugToolDrivers/WCH/WchGeneric.hpp
Normal file
14
src/DebugToolDrivers/WCH/WchGeneric.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
enum class WchLinkVariant: std::uint8_t
|
||||||
|
{
|
||||||
|
LINK_CH549,
|
||||||
|
LINK_E_CH32V307,
|
||||||
|
LINK_S_CH32V203,
|
||||||
|
UNKNOWN,
|
||||||
|
};
|
||||||
|
}
|
||||||
69
src/DebugToolDrivers/WCH/WchLinkBase.cpp
Normal file
69
src/DebugToolDrivers/WCH/WchLinkBase.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include "WchLinkBase.hpp"
|
||||||
|
|
||||||
|
#include "Protocols/WchLink/Commands/Control/GetDeviceInfo.hpp"
|
||||||
|
|
||||||
|
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
|
||||||
|
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||||
|
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
using Exceptions::DeviceFailure;
|
||||||
|
using Exceptions::DeviceInitializationFailure;
|
||||||
|
|
||||||
|
WchLinkBase::WchLinkBase(
|
||||||
|
WchLinkVariant variant,
|
||||||
|
std::uint16_t vendorId,
|
||||||
|
std::uint16_t productId,
|
||||||
|
std::uint8_t wchLinkUsbInterfaceNumber
|
||||||
|
)
|
||||||
|
: UsbDevice(vendorId, productId)
|
||||||
|
, variant(variant)
|
||||||
|
, wchLinkUsbInterfaceNumber(wchLinkUsbInterfaceNumber)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WchLinkBase::init() {
|
||||||
|
UsbDevice::init();
|
||||||
|
|
||||||
|
this->detachKernelDriverFromInterface(this->wchLinkUsbInterfaceNumber);
|
||||||
|
|
||||||
|
this->wchLinkUsbInterface = std::make_unique<Usb::UsbInterface>(
|
||||||
|
this->wchLinkUsbInterfaceNumber,
|
||||||
|
this->libusbDeviceHandle.get()
|
||||||
|
);
|
||||||
|
|
||||||
|
this->wchLinkUsbInterface->init();
|
||||||
|
|
||||||
|
this->wchLinkInterface = std::make_unique<Protocols::WchLink::WchLinkInterface>(
|
||||||
|
*(this->wchLinkUsbInterface.get())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this->getDeviceInfo().variant != this->variant) {
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"WCH-Link variant mismatch - device returned variant ID that doesn't match the " + this->getName()
|
||||||
|
+ " variant ID"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->setInitialised(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WchLinkBase::close() {
|
||||||
|
if (this->wchLinkUsbInterface) {
|
||||||
|
this->wchLinkUsbInterface->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WchLinkBase::getSerialNumber() {
|
||||||
|
return UsbDevice::getSerialNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeviceInfo& WchLinkBase::getDeviceInfo() const {
|
||||||
|
if (!this->cachedDeviceInfo.has_value()) {
|
||||||
|
this->cachedDeviceInfo = this->wchLinkInterface->getDeviceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *(this->cachedDeviceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/DebugToolDrivers/WCH/WchLinkBase.hpp
Normal file
44
src/DebugToolDrivers/WCH/WchLinkBase.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||||
|
#include "src/DebugToolDrivers/USB/UsbDevice.hpp"
|
||||||
|
|
||||||
|
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||||
|
|
||||||
|
#include "WchGeneric.hpp"
|
||||||
|
#include "DeviceInfo.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
class WchLinkBase: public DebugTool, public Usb::UsbDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WchLinkBase(
|
||||||
|
WchLinkVariant variant,
|
||||||
|
std::uint16_t vendorId,
|
||||||
|
std::uint16_t productId,
|
||||||
|
std::uint8_t wchLinkUsbInterfaceNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
std::string getSerialNumber() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WchLinkVariant variant;
|
||||||
|
|
||||||
|
std::uint8_t wchLinkUsbInterfaceNumber;
|
||||||
|
std::unique_ptr<Usb::UsbInterface> wchLinkUsbInterface = nullptr;
|
||||||
|
std::unique_ptr<Protocols::WchLink::WchLinkInterface> wchLinkInterface = nullptr;
|
||||||
|
|
||||||
|
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
|
||||||
|
|
||||||
|
const DeviceInfo& getDeviceInfo() const;
|
||||||
|
};
|
||||||
|
}
|
||||||
13
src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp
Normal file
13
src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "WchLinkE.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
WchLinkE::WchLinkE()
|
||||||
|
: WchLinkBase(
|
||||||
|
WchLinkVariant::LINK_E_CH32V307,
|
||||||
|
WchLinkE::USB_VENDOR_ID,
|
||||||
|
WchLinkE::USB_PRODUCT_ID,
|
||||||
|
WchLinkE::WCH_LINK_INTERFACE_NUMBER
|
||||||
|
)
|
||||||
|
{}
|
||||||
|
}
|
||||||
30
src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp
Normal file
30
src/DebugToolDrivers/WCH/WchLinkE/WchLinkE.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/WchLinkBase.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The WCH-LinkE debug tool is a variant of the WCH-Link.
|
||||||
|
*
|
||||||
|
* USB:
|
||||||
|
* Vendor ID: 0x1a86 (6790)
|
||||||
|
* Product ID: 0x8010 (32784)
|
||||||
|
*/
|
||||||
|
class WchLinkE: public WchLinkBase
|
||||||
|
{
|
||||||
|
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::uint8_t WCH_LINK_INTERFACE_NUMBER = 0;
|
||||||
|
|
||||||
|
WchLinkE();
|
||||||
|
|
||||||
|
std::string getName() override {
|
||||||
|
return "WCH-LinkE";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -362,6 +362,12 @@ namespace TargetController
|
|||||||
return std::make_unique<DebugToolDrivers::JtagIce3>();
|
return std::make_unique<DebugToolDrivers::JtagIce3>();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"wch-link-e",
|
||||||
|
[] {
|
||||||
|
return std::make_unique<DebugToolDrivers::Wch::WchLinkE>();
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user