Began implementation of WCH-Link protocol, for the WCH-LinkE debug tool.
Foundations have been laid.
This commit is contained in:
@@ -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())
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user