Tidied structure of all classes within the entire code base
Also some other small bits of tidying
This commit is contained in:
@@ -81,6 +81,10 @@ int Application::run(const std::vector<std::string>& arguments) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::isRunningAsRoot() {
|
||||||
|
return geteuid() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Application::startup() {
|
void Application::startup() {
|
||||||
auto applicationEventListener = this->applicationEventListener;
|
auto applicationEventListener = this->applicationEventListener;
|
||||||
this->eventManager.registerListener(applicationEventListener);
|
this->eventManager.registerListener(applicationEventListener);
|
||||||
@@ -131,6 +135,37 @@ void Application::startup() {
|
|||||||
Thread::setThreadState(ThreadState::READY);
|
Thread::setThreadState(ThreadState::READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::shutdown() {
|
||||||
|
auto appState = Thread::getThreadState();
|
||||||
|
if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED);
|
||||||
|
Logger::info("Shutting down Bloom");
|
||||||
|
|
||||||
|
this->stopDebugServer();
|
||||||
|
this->stopTargetController();
|
||||||
|
|
||||||
|
if (this->signalHandler.getThreadState() != ThreadState::STOPPED
|
||||||
|
&& this->signalHandler.getThreadState() != ThreadState::UNINITIALISED
|
||||||
|
) {
|
||||||
|
// Signal handler is still running
|
||||||
|
this->signalHandler.triggerShutdown();
|
||||||
|
|
||||||
|
// Send meaningless signal to the SignalHandler thread to have it shutdown.
|
||||||
|
pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->signalHandlerThread.joinable()) {
|
||||||
|
Logger::debug("Joining SignalHandler thread");
|
||||||
|
this->signalHandlerThread.join();
|
||||||
|
Logger::debug("SignalHandler thread joined");
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::setThreadState(ThreadState::STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
ApplicationConfig Application::extractConfig() {
|
ApplicationConfig Application::extractConfig() {
|
||||||
auto appConfig = ApplicationConfig();
|
auto appConfig = ApplicationConfig();
|
||||||
auto currentPath = std::filesystem::current_path().string();
|
auto currentPath = std::filesystem::current_path().string();
|
||||||
@@ -227,37 +262,6 @@ int Application::initProject() {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::shutdown() {
|
|
||||||
auto appState = Thread::getThreadState();
|
|
||||||
if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED);
|
|
||||||
Logger::info("Shutting down Bloom");
|
|
||||||
|
|
||||||
this->stopDebugServer();
|
|
||||||
this->stopTargetController();
|
|
||||||
|
|
||||||
if (this->signalHandler.getThreadState() != ThreadState::STOPPED
|
|
||||||
&& this->signalHandler.getThreadState() != ThreadState::UNINITIALISED
|
|
||||||
) {
|
|
||||||
// Signal handler is still running
|
|
||||||
this->signalHandler.triggerShutdown();
|
|
||||||
|
|
||||||
// Send meaningless signal to the SignalHandler thread to have it shutdown.
|
|
||||||
pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->signalHandlerThread.joinable()) {
|
|
||||||
Logger::debug("Joining SignalHandler thread");
|
|
||||||
this->signalHandlerThread.join();
|
|
||||||
Logger::debug("SignalHandler thread joined");
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::setThreadState(ThreadState::STOPPED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::startTargetController() {
|
void Application::startTargetController() {
|
||||||
this->targetController.setApplicationConfig(this->applicationConfig);
|
this->targetController.setApplicationConfig(this->applicationConfig);
|
||||||
this->targetController.setEnvironmentConfig(this->environmentConfig);
|
this->targetController.setEnvironmentConfig(this->environmentConfig);
|
||||||
@@ -354,7 +358,3 @@ void Application::onDebugServerThreadStateChanged(const Events::DebugServerThrea
|
|||||||
this->shutdown();
|
this->shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::isRunningAsRoot() {
|
|
||||||
return geteuid() == 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,6 +31,44 @@ namespace Bloom
|
|||||||
public:
|
public:
|
||||||
static const inline std::string VERSION_STR = "0.4.2";
|
static const inline std::string VERSION_STR = "0.4.2";
|
||||||
|
|
||||||
|
explicit Application() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This mapping is used to map debug server names from project configuration files to polymorphic instances of
|
||||||
|
* the DebugServer class.
|
||||||
|
*
|
||||||
|
* See Application::startDebugServer() for more on this.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
auto getSupportedDebugServers() {
|
||||||
|
return std::map<std::string, std::function<std::unique_ptr<DebugServers::DebugServer>()>> {
|
||||||
|
{
|
||||||
|
"avr-gdb-rsp",
|
||||||
|
[this]() -> std::unique_ptr<DebugServers::DebugServer> {
|
||||||
|
return std::make_unique<DebugServers::Gdb::AvrGdbRsp>(this->eventManager);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry-point for the Bloom program.
|
||||||
|
*
|
||||||
|
* @param arguments
|
||||||
|
* A vector of string arguments passed from the user via the cli.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int run(const std::vector<std::string>& arguments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current effective user running Bloom has root privileges.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static bool isRunningAsRoot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* This is the application wide event manager. It should be the only instance of the EventManager class at
|
* This is the application wide event manager. It should be the only instance of the EventManager class at
|
||||||
@@ -139,6 +177,18 @@ namespace Bloom
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kicks off the application.
|
||||||
|
*
|
||||||
|
* Will start the TargetController and DebugServer. And register the main application's event handlers.
|
||||||
|
*/
|
||||||
|
void startup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will cleanly shutdown the application. This should never fail.
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts config from the user's JSON config file and generates an ApplicationConfig object.
|
* Extracts config from the user's JSON config file and generates an ApplicationConfig object.
|
||||||
*
|
*
|
||||||
@@ -168,18 +218,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
int initProject();
|
int initProject();
|
||||||
|
|
||||||
/**
|
|
||||||
* Kicks off the application.
|
|
||||||
*
|
|
||||||
* Will start the TargetController and DebugServer. And register the main application's event handlers.
|
|
||||||
*/
|
|
||||||
void startup();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will cleanly shutdown the application. This should never fail.
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares a dedicated thread for the TargetController and kicks it off.
|
* Prepares a dedicated thread for the TargetController and kicks it off.
|
||||||
*/
|
*/
|
||||||
@@ -203,38 +241,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
void stopDebugServer();
|
void stopDebugServer();
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Application() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This mapping is used to map debug server names from project configuration files to polymorphic instances of
|
|
||||||
* the DebugServer class.
|
|
||||||
*
|
|
||||||
* See Application::startDebugServer() for more on this.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
auto getSupportedDebugServers() {
|
|
||||||
return std::map<std::string, std::function<std::unique_ptr<DebugServers::DebugServer>()>> {
|
|
||||||
{
|
|
||||||
"avr-gdb-rsp",
|
|
||||||
[this]() -> std::unique_ptr<DebugServers::DebugServer> {
|
|
||||||
return std::make_unique<DebugServers::Gdb::AvrGdbRsp>(this->eventManager);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main entry-point for the Bloom program.
|
|
||||||
*
|
|
||||||
* @param arguments
|
|
||||||
* A vector of string arguments passed from the user via the cli.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
int run(const std::vector<std::string>& arguments);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the TargetController unexpectedly shuts down, the rest of the application will follow.
|
* If the TargetController unexpectedly shuts down, the rest of the application will follow.
|
||||||
*
|
*
|
||||||
@@ -254,12 +260,5 @@ namespace Bloom
|
|||||||
* Triggers a shutdown of Bloom and all of its components.
|
* Triggers a shutdown of Bloom and all of its components.
|
||||||
*/
|
*/
|
||||||
void onShutdownApplicationRequest(const Events::ShutdownApplication&);
|
void onShutdownApplicationRequest(const Events::ShutdownApplication&);
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the current effective user running Bloom has root privileges.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
static bool isRunningAsRoot();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,40 +27,28 @@ namespace Bloom::DebugServers
|
|||||||
*/
|
*/
|
||||||
class DebugServer: public Thread
|
class DebugServer: public Thread
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
/**
|
explicit DebugServer(EventManager& eventManager): eventManager(eventManager) {};
|
||||||
* Prepares the debug server thread and then calls init().
|
virtual ~DebugServer() = default;
|
||||||
*
|
|
||||||
* Derived classes should not override this method - they should instead use init().
|
|
||||||
*/
|
|
||||||
void startup();
|
|
||||||
|
|
||||||
/**
|
void setApplicationConfig(const ApplicationConfig& applicationConfig) {
|
||||||
* Calls close() and updates the thread state.
|
this->applicationConfig = applicationConfig;
|
||||||
*
|
}
|
||||||
* As with startup(), derived classes should not override this method. They should use close() instead.
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
/**
|
void setEnvironmentConfig(const EnvironmentConfig& environmentConfig) {
|
||||||
* Updates the state of the DebugServer and emits a state changed event.
|
this->environmentConfig = environmentConfig;
|
||||||
*
|
}
|
||||||
* @param state
|
|
||||||
* @param emitEvent
|
void setDebugServerConfig(const DebugServerConfig& debugServerConfig) {
|
||||||
*/
|
this->debugServerConfig = debugServerConfig;
|
||||||
void setThreadStateAndEmitEvent(ThreadState state) {
|
|
||||||
Thread::setThreadState(state);
|
|
||||||
this->eventManager.triggerEvent(
|
|
||||||
std::make_shared<Events::DebugServerThreadStateChanged>(state)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a shutdown request.
|
* Entry point for the DebugServer. This must called from a dedicated thread.
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
*/
|
*/
|
||||||
void onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event);
|
void run();
|
||||||
|
|
||||||
|
virtual std::string getName() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
@@ -100,28 +88,39 @@ namespace Bloom::DebugServers
|
|||||||
*/
|
*/
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
explicit DebugServer(EventManager& eventManager): eventManager(eventManager) {};
|
/**
|
||||||
|
* Prepares the debug server thread and then calls init().
|
||||||
|
*
|
||||||
|
* Derived classes should not override this method - they should instead use init().
|
||||||
|
*/
|
||||||
|
void startup();
|
||||||
|
|
||||||
void setApplicationConfig(const ApplicationConfig& applicationConfig) {
|
/**
|
||||||
this->applicationConfig = applicationConfig;
|
* Calls close() and updates the thread state.
|
||||||
}
|
*
|
||||||
|
* As with startup(), derived classes should not override this method. They should use close() instead.
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
void setEnvironmentConfig(const EnvironmentConfig& environmentConfig) {
|
/**
|
||||||
this->environmentConfig = environmentConfig;
|
* Updates the state of the DebugServer and emits a state changed event.
|
||||||
}
|
*
|
||||||
|
* @param state
|
||||||
void setDebugServerConfig(const DebugServerConfig& debugServerConfig) {
|
* @param emitEvent
|
||||||
this->debugServerConfig = debugServerConfig;
|
*/
|
||||||
|
void setThreadStateAndEmitEvent(ThreadState state) {
|
||||||
|
Thread::setThreadState(state);
|
||||||
|
this->eventManager.triggerEvent(
|
||||||
|
std::make_shared<Events::DebugServerThreadStateChanged>(state)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for the DebugServer. This must called from a dedicated thread.
|
* Handles a shutdown request.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
*/
|
*/
|
||||||
void run();
|
void onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event);
|
||||||
|
|
||||||
virtual std::string getName() const = 0;
|
|
||||||
|
|
||||||
virtual ~DebugServer() = default;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,12 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
*/
|
*/
|
||||||
class AvrGdbRsp: public GdbRspDebugServer
|
class AvrGdbRsp: public GdbRspDebugServer
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
/**
|
explicit AvrGdbRsp(EventManager& eventManager): GdbRspDebugServer(eventManager) {};
|
||||||
* The mask used by the AVR GDB client to encode the memory type into memory addresses.
|
|
||||||
* See AvrGdbRsp::getMemoryTypeFromGdbAddress() for more.
|
|
||||||
*/
|
|
||||||
unsigned int gdbInternalMemoryMask = 0xFE0000u;
|
|
||||||
|
|
||||||
BiMap<GdbRegisterNumber, Targets::TargetRegisterDescriptor> registerNumberToDescriptorMapping = {};
|
std::string getName() const override {
|
||||||
|
return "AVR GDB Remote Serial Protocol Debug Server";
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
@@ -80,11 +78,13 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
|
|
||||||
void init() override;
|
void init() override;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
explicit AvrGdbRsp(EventManager& eventManager): GdbRspDebugServer(eventManager) {};
|
/**
|
||||||
|
* The mask used by the AVR GDB client to encode the memory type into memory addresses.
|
||||||
|
* See AvrGdbRsp::getMemoryTypeFromGdbAddress() for more.
|
||||||
|
*/
|
||||||
|
unsigned int gdbInternalMemoryMask = 0xFE0000u;
|
||||||
|
|
||||||
std::string getName() const override {
|
BiMap<GdbRegisterNumber, Targets::TargetRegisterDescriptor> registerNumberToDescriptorMapping = {};
|
||||||
return "AVR GDB Remote Serial Protocol Debug Server";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,51 +7,6 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb;
|
using namespace Bloom::DebugServers::Gdb;
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
|
|
||||||
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
|
||||||
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
|
||||||
// This is an interrupt request - create a fake packet for it
|
|
||||||
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
|
|
||||||
|
|
||||||
if (rawPacketString.size() >= 2) {
|
|
||||||
/*
|
|
||||||
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
|
|
||||||
* looking for a command identifier string.
|
|
||||||
*/
|
|
||||||
if (rawPacketString.find("qSupported") == 1) {
|
|
||||||
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
|
||||||
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'P') {
|
|
||||||
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'c') {
|
|
||||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 's') {
|
|
||||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'm') {
|
|
||||||
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'M') {
|
|
||||||
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'Z') {
|
|
||||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
|
||||||
|
|
||||||
} else if (rawPacketString[1] == 'z') {
|
|
||||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<CommandPacket>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
|
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
|
||||||
std::vector<std::vector<unsigned char>> output;
|
std::vector<std::vector<unsigned char>> output;
|
||||||
|
|
||||||
@@ -127,3 +82,48 @@ std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
||||||
|
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||||
|
// This is an interrupt request - create a fake packet for it
|
||||||
|
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
|
||||||
|
|
||||||
|
if (rawPacketString.size() >= 2) {
|
||||||
|
/*
|
||||||
|
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
|
||||||
|
* looking for a command identifier string.
|
||||||
|
*/
|
||||||
|
if (rawPacketString.find("qSupported") == 1) {
|
||||||
|
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
||||||
|
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'P') {
|
||||||
|
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'c') {
|
||||||
|
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 's') {
|
||||||
|
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'm') {
|
||||||
|
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'M') {
|
||||||
|
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'Z') {
|
||||||
|
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
||||||
|
|
||||||
|
} else if (rawPacketString[1] == 'z') {
|
||||||
|
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<CommandPacket>(rawPacket);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
|
|
||||||
|
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void ContinueExecution::init() {
|
void ContinueExecution::init() {
|
||||||
if (this->data.size() > 1) {
|
if (this->data.size() > 1) {
|
||||||
this->fromProgramCounter = static_cast<std::uint32_t>(
|
this->fromProgramCounter = static_cast<std::uint32_t>(
|
||||||
@@ -13,7 +17,3 @@ void ContinueExecution::init() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class ContinueExecution: public CommandPacket
|
class ContinueExecution: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The "c" packet can contain an address which defines the point from which the execution should be resumed on
|
* The "c" packet can contain an address which defines the point from which the execution should be resumed on
|
||||||
@@ -32,5 +29,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void ReadMemory::init() {
|
void ReadMemory::init() {
|
||||||
if (this->data.size() < 4) {
|
if (this->data.size() < 4) {
|
||||||
throw Exception("Invalid packet length");
|
throw Exception("Invalid packet length");
|
||||||
@@ -38,7 +42,3 @@ void ReadMemory::init() {
|
|||||||
throw Exception("Failed to parse read length from read memory packet data");
|
throw Exception("Failed to parse read length from read memory packet data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class ReadMemory: public CommandPacket
|
class ReadMemory: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The startAddress sent from the GDB client may include additional bits used to indicate the memory type.
|
* The startAddress sent from the GDB client may include additional bits used to indicate the memory type.
|
||||||
@@ -36,5 +33,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
|
|
||||||
|
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void ReadRegisters::init() {
|
void ReadRegisters::init() {
|
||||||
if (this->data.size() >= 2 && this->data.front() == 'p') {
|
if (this->data.size() >= 2 && this->data.front() == 'p') {
|
||||||
// This command packet is requesting a specific register
|
// This command packet is requesting a specific register
|
||||||
this->registerNumber = static_cast<size_t>(std::stoi(std::string(this->data.begin() + 1, this->data.end())));
|
this->registerNumber = static_cast<size_t>(std::stoi(std::string(this->data.begin() + 1, this->data.end())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class ReadRegisters: public CommandPacket
|
class ReadRegisters: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
|
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
|
||||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void RemoveBreakpoint::init() {
|
void RemoveBreakpoint::init() {
|
||||||
if (data.size() < 6) {
|
if (data.size() < 6) {
|
||||||
throw Exception("Unexpected RemoveBreakpoint packet size");
|
throw Exception("Unexpected RemoveBreakpoint packet size");
|
||||||
@@ -33,7 +37,3 @@ void RemoveBreakpoint::init() {
|
|||||||
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
|
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class RemoveBreakpoint: public CommandPacket
|
class RemoveBreakpoint: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Breakpoint type (Software or Hardware)
|
* Breakpoint type (Software or Hardware)
|
||||||
@@ -39,5 +36,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void SetBreakpoint::init() {
|
void SetBreakpoint::init() {
|
||||||
if (data.size() < 6) {
|
if (data.size() < 6) {
|
||||||
throw Exception("Unexpected SetBreakpoint packet size");
|
throw Exception("Unexpected SetBreakpoint packet size");
|
||||||
@@ -34,7 +38,3 @@ void SetBreakpoint::init() {
|
|||||||
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
|
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class SetBreakpoint: public CommandPacket
|
class SetBreakpoint: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Breakpoint type (Software or Hardware)
|
* Breakpoint type (Software or Hardware)
|
||||||
@@ -39,5 +36,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
|
|
||||||
|
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void StepExecution::init() {
|
void StepExecution::init() {
|
||||||
if (this->data.size() > 1) {
|
if (this->data.size() > 1) {
|
||||||
this->fromProgramCounter = static_cast<std::uint32_t>(
|
this->fromProgramCounter = static_cast<std::uint32_t>(
|
||||||
@@ -12,7 +16,3 @@ void StepExecution::init() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class StepExecution: public CommandPacket
|
class StepExecution: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The address from which to begin the step.
|
* The address from which to begin the step.
|
||||||
@@ -26,5 +23,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
|
|
||||||
|
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void SupportedFeaturesQuery::init() {
|
void SupportedFeaturesQuery::init() {
|
||||||
/*
|
/*
|
||||||
* For qSupported packets, supported and unsupported GDB features are reported in the packet
|
* For qSupported packets, supported and unsupported GDB features are reported in the packet
|
||||||
@@ -37,7 +41,3 @@ void SupportedFeaturesQuery::init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class SupportedFeaturesQuery: public CommandPacket
|
class SupportedFeaturesQuery: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::set<Feature> supportedFeatures;
|
|
||||||
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SupportedFeaturesQuery(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
|
explicit SupportedFeaturesQuery(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
|
||||||
this->init();
|
this->init();
|
||||||
@@ -40,5 +35,10 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::set<Feature> supportedFeatures;
|
||||||
|
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteMemory::init() {
|
void WriteMemory::init() {
|
||||||
if (this->data.size() < 4) {
|
if (this->data.size() < 4) {
|
||||||
throw Exception("Invalid packet length");
|
throw Exception("Invalid packet length");
|
||||||
@@ -47,7 +51,3 @@ void WriteMemory::init() {
|
|||||||
throw Exception("Buffer size does not match length value given in write memory packet");
|
throw Exception("Buffer size does not match length value given in write memory packet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class WriteMemory: public CommandPacket
|
class WriteMemory: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Like with the ReadMemory command packet, the start address carries additional bits that indicate
|
* Like with the ReadMemory command packet, the start address carries additional bits that indicate
|
||||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||||
|
gdbRspDebugServer.handleGdbPacket(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteRegister::init() {
|
void WriteRegister::init() {
|
||||||
// The P packet updates a single register
|
// The P packet updates a single register
|
||||||
auto packet = std::string(this->data.begin(), this->data.end());
|
auto packet = std::string(this->data.begin(), this->data.end());
|
||||||
@@ -23,7 +27,3 @@ void WriteRegister::init() {
|
|||||||
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
||||||
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
|
||||||
gdbRspDebugServer.handleGdbPacket(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
class WriteRegister: public CommandPacket
|
class WriteRegister: public CommandPacket
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int registerNumber = 0;
|
int registerNumber = 0;
|
||||||
std::vector<unsigned char> registerValue;
|
std::vector<unsigned char> registerValue;
|
||||||
@@ -25,5 +22,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "CommandPackets/CommandPacketFactory.hpp"
|
#include "CommandPackets/CommandPacketFactory.hpp"
|
||||||
|
|
||||||
#include "Exceptions/ClientDisconnected.hpp"
|
#include "Exceptions/ClientDisconnected.hpp"
|
||||||
#include "Exceptions/ClientCommunicationError.hpp"
|
#include "Exceptions/ClientCommunicationError.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
#include "src/Exceptions/DebugServerInterrupted.hpp"
|
#include "src/Exceptions/DebugServerInterrupted.hpp"
|
||||||
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb;
|
using namespace Bloom::DebugServers::Gdb;
|
||||||
@@ -51,32 +53,6 @@ void Connection::accept(int serverSocketFileDescriptor) {
|
|||||||
this->enableReadInterrupts();
|
this->enableReadInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::disableReadInterrupts() {
|
|
||||||
if (::epoll_ctl(
|
|
||||||
this->eventFileDescriptor,
|
|
||||||
EPOLL_CTL_DEL,
|
|
||||||
this->interruptEventNotifier->getFileDescriptor(),
|
|
||||||
NULL) != 0
|
|
||||||
) {
|
|
||||||
throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->readInterruptEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Connection::enableReadInterrupts() {
|
|
||||||
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
|
||||||
struct epoll_event event = {};
|
|
||||||
event.events = EPOLLIN;
|
|
||||||
event.data.fd = interruptFileDescriptor;
|
|
||||||
|
|
||||||
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
|
|
||||||
throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->readInterruptEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Connection::close() noexcept {
|
void Connection::close() noexcept {
|
||||||
if (this->socketFileDescriptor > 0) {
|
if (this->socketFileDescriptor > 0) {
|
||||||
::close(this->socketFileDescriptor);
|
::close(this->socketFileDescriptor);
|
||||||
@@ -84,18 +60,28 @@ void Connection::close() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::write(const std::vector<unsigned char>& buffer) {
|
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
|
||||||
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
|
auto buffer = this->read();
|
||||||
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
|
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end()));
|
||||||
if (errno == EPIPE || errno == ECONNRESET) {
|
|
||||||
// Connection was closed
|
|
||||||
throw ClientDisconnected();
|
|
||||||
|
|
||||||
} else {
|
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
|
||||||
throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size())
|
std::vector<std::unique_ptr<CommandPacket>> output;
|
||||||
+ " bytes to GDP client socket - error no: " + std::to_string(errno));
|
|
||||||
|
for (const auto& rawPacket : rawPackets) {
|
||||||
|
try {
|
||||||
|
output.push_back(CommandPacketFactory::create(rawPacket));
|
||||||
|
this->write({'+'});
|
||||||
|
|
||||||
|
} catch (const ClientDisconnected& exception) {
|
||||||
|
throw exception;
|
||||||
|
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
|
||||||
|
this->write({'-'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::writePacket(const ResponsePacket& packet) {
|
void Connection::writePacket(const ResponsePacket& packet) {
|
||||||
@@ -185,26 +171,42 @@ std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
|
void Connection::write(const std::vector<unsigned char>& buffer) {
|
||||||
auto buffer = this->read();
|
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
|
||||||
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end()));
|
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
|
||||||
|
if (errno == EPIPE || errno == ECONNRESET) {
|
||||||
|
// Connection was closed
|
||||||
|
throw ClientDisconnected();
|
||||||
|
|
||||||
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
|
} else {
|
||||||
std::vector<std::unique_ptr<CommandPacket>> output;
|
throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size())
|
||||||
|
+ " bytes to GDP client socket - error no: " + std::to_string(errno));
|
||||||
for (const auto& rawPacket : rawPackets) {
|
|
||||||
try {
|
|
||||||
output.push_back(CommandPacketFactory::create(rawPacket));
|
|
||||||
this->write({'+'});
|
|
||||||
|
|
||||||
} catch (const ClientDisconnected& exception) {
|
|
||||||
throw exception;
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
|
||||||
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
|
|
||||||
this->write({'-'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return output;
|
|
||||||
|
void Connection::disableReadInterrupts() {
|
||||||
|
if (::epoll_ctl(
|
||||||
|
this->eventFileDescriptor,
|
||||||
|
EPOLL_CTL_DEL,
|
||||||
|
this->interruptEventNotifier->getFileDescriptor(),
|
||||||
|
NULL) != 0
|
||||||
|
) {
|
||||||
|
throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->readInterruptEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::enableReadInterrupts() {
|
||||||
|
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
||||||
|
struct epoll_event event = {};
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
event.data.fd = interruptFileDescriptor;
|
||||||
|
|
||||||
|
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
|
||||||
|
throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->readInterruptEnabled = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,59 +22,6 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
*/
|
*/
|
||||||
class Connection
|
class Connection
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
int socketFileDescriptor = -1;
|
|
||||||
int eventFileDescriptor = -1;
|
|
||||||
|
|
||||||
struct sockaddr_in socketAddress = {};
|
|
||||||
int maxPacketSize = 1024;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The interruptEventNotifier allows us to interrupt blocking IO calls on the GDB debug server.
|
|
||||||
* Under the hood, this is just a wrapper for a Linux event notifier. See the EventNotifier class for more.
|
|
||||||
*/
|
|
||||||
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
|
||||||
bool readInterruptEnabled = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads data from the client into a raw buffer.
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Number of bytes to read.
|
|
||||||
*
|
|
||||||
* @param interruptible
|
|
||||||
* If this flag is set to false, no other component within Bloom will be able to gracefully interrupt
|
|
||||||
* the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled
|
|
||||||
* is false.
|
|
||||||
*
|
|
||||||
* @param msTimeout
|
|
||||||
* The timeout in milliseconds. If not supplied, no timeout will be applied.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
std::vector<unsigned char> read(std::size_t bytes = 0, bool interruptible = true, std::optional<int> msTimeout = std::nullopt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the same as Connection::read(), but only reads a single byte.
|
|
||||||
*
|
|
||||||
* @param interruptible
|
|
||||||
* See Connection::read().
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
std::optional<unsigned char> readSingleByte(bool interruptible = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes data from a raw buffer to the client connection.
|
|
||||||
*
|
|
||||||
* @param buffer
|
|
||||||
*/
|
|
||||||
void write(const std::vector<unsigned char>& buffer);
|
|
||||||
|
|
||||||
void disableReadInterrupts();
|
|
||||||
|
|
||||||
void enableReadInterrupts();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* When the GDB client is waiting for the target to reach a breakpoint, this is set to true so we know when to
|
* When the GDB client is waiting for the target to reach a breakpoint, this is set to true so we know when to
|
||||||
@@ -131,5 +78,58 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
[[nodiscard]] int getMaxPacketSize() const {
|
[[nodiscard]] int getMaxPacketSize() const {
|
||||||
return this->maxPacketSize;
|
return this->maxPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int socketFileDescriptor = -1;
|
||||||
|
int eventFileDescriptor = -1;
|
||||||
|
|
||||||
|
struct sockaddr_in socketAddress = {};
|
||||||
|
int maxPacketSize = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interruptEventNotifier allows us to interrupt blocking IO calls on the GDB debug server.
|
||||||
|
* Under the hood, this is just a wrapper for a Linux event notifier. See the EventNotifier class for more.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
||||||
|
bool readInterruptEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads data from the client into a raw buffer.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* Number of bytes to read.
|
||||||
|
*
|
||||||
|
* @param interruptible
|
||||||
|
* If this flag is set to false, no other component within Bloom will be able to gracefully interrupt
|
||||||
|
* the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled
|
||||||
|
* is false.
|
||||||
|
*
|
||||||
|
* @param msTimeout
|
||||||
|
* The timeout in milliseconds. If not supplied, no timeout will be applied.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::vector<unsigned char> read(std::size_t bytes = 0, bool interruptible = true, std::optional<int> msTimeout = std::nullopt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the same as Connection::read(), but only reads a single byte.
|
||||||
|
*
|
||||||
|
* @param interruptible
|
||||||
|
* See Connection::read().
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::optional<unsigned char> readSingleByte(bool interruptible = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes data from a raw buffer to the client connection.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
*/
|
||||||
|
void write(const std::vector<unsigned char>& buffer);
|
||||||
|
|
||||||
|
void disableReadInterrupts();
|
||||||
|
|
||||||
|
void enableReadInterrupts();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <cstdint>
|
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
#include "Exceptions/ClientDisconnected.hpp"
|
#include "Exceptions/ClientDisconnected.hpp"
|
||||||
#include "Exceptions/ClientNotSupported.hpp"
|
#include "Exceptions/ClientNotSupported.hpp"
|
||||||
@@ -10,7 +11,6 @@
|
|||||||
#include "Exceptions/DebugSessionAborted.hpp"
|
#include "Exceptions/DebugSessionAborted.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
#include "src/Exceptions/InvalidConfig.hpp"
|
#include "src/Exceptions/InvalidConfig.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
|
||||||
|
|
||||||
using namespace Bloom::DebugServers::Gdb;
|
using namespace Bloom::DebugServers::Gdb;
|
||||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||||
@@ -25,191 +25,6 @@ using Bloom::Targets::TargetRegisterDescriptor;
|
|||||||
using Bloom::Targets::TargetRegisterDescriptors;
|
using Bloom::Targets::TargetRegisterDescriptors;
|
||||||
using Bloom::Targets::TargetBreakpoint;
|
using Bloom::Targets::TargetBreakpoint;
|
||||||
|
|
||||||
void GdbRspDebugServer::init() {
|
|
||||||
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
|
|
||||||
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
|
|
||||||
auto configPortValue = configPortJsonValue->isString()
|
|
||||||
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
|
||||||
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
|
|
||||||
|
|
||||||
if (!ipAddress.empty()) {
|
|
||||||
this->listeningAddress = ipAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configPortValue > 0) {
|
|
||||||
this->listeningPortNumber = configPortValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->socketAddress.sin_family = AF_INET;
|
|
||||||
this->socketAddress.sin_port = htons(this->listeningPortNumber);
|
|
||||||
|
|
||||||
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
|
|
||||||
// Invalid IP address
|
|
||||||
throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
int socketFileDescriptor;
|
|
||||||
|
|
||||||
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
|
||||||
throw Exception("Failed to create socket file descriptor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (::setsockopt(
|
|
||||||
socketFileDescriptor,
|
|
||||||
SOL_SOCKET,
|
|
||||||
SO_REUSEADDR,
|
|
||||||
&(this->enableReuseAddressSocketOption),
|
|
||||||
sizeof(this->enableReuseAddressSocketOption)
|
|
||||||
) < 0
|
|
||||||
) {
|
|
||||||
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (::bind(
|
|
||||||
socketFileDescriptor,
|
|
||||||
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
|
||||||
sizeof(this->socketAddress)
|
|
||||||
) < 0
|
|
||||||
) {
|
|
||||||
throw Exception("Failed to bind address. The selected port number ("
|
|
||||||
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->serverSocketFileDescriptor = socketFileDescriptor;
|
|
||||||
|
|
||||||
this->eventFileDescriptor = ::epoll_create(2);
|
|
||||||
struct epoll_event event = {};
|
|
||||||
event.events = EPOLLIN;
|
|
||||||
event.data.fd = this->serverSocketFileDescriptor;
|
|
||||||
|
|
||||||
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
|
|
||||||
throw Exception("Failed epoll_ctl server socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->interruptEventNotifier != nullptr) {
|
|
||||||
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
|
||||||
event.events = EPOLLIN;
|
|
||||||
event.data.fd = interruptFileDescriptor;
|
|
||||||
|
|
||||||
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
|
|
||||||
throw Exception("Failed epoll_ctl interrupt event fd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::info("GDB RSP address: " + this->listeningAddress);
|
|
||||||
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
|
|
||||||
|
|
||||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
|
|
||||||
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
|
||||||
);
|
|
||||||
|
|
||||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
|
||||||
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::close() {
|
|
||||||
this->closeClientConnection();
|
|
||||||
|
|
||||||
if (this->serverSocketFileDescriptor > 0) {
|
|
||||||
::close(this->serverSocketFileDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::serve() {
|
|
||||||
try {
|
|
||||||
if (!this->clientConnection.has_value()) {
|
|
||||||
Logger::info("Waiting for GDB RSP connection");
|
|
||||||
|
|
||||||
do {
|
|
||||||
this->waitForConnection();
|
|
||||||
|
|
||||||
} while (!this->clientConnection.has_value());
|
|
||||||
|
|
||||||
this->clientConnection->accept(this->serverSocketFileDescriptor);
|
|
||||||
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
|
|
||||||
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
|
|
||||||
* service it.
|
|
||||||
*/
|
|
||||||
if (!this->targetControllerConsole.isTargetControllerInService()) {
|
|
||||||
this->closeClientConnection();
|
|
||||||
throw DebugSessionAborted("TargetController not in service");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto packets = this->clientConnection->readPackets();
|
|
||||||
|
|
||||||
// Only process the last packet - any others will likely be duplicates from an impatient client
|
|
||||||
if (!packets.empty()) {
|
|
||||||
// Double-dispatch to appropriate handler
|
|
||||||
packets.back()->dispatchToHandler(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
|
||||||
Logger::info("GDB RSP client disconnected");
|
|
||||||
this->closeClientConnection();
|
|
||||||
return;
|
|
||||||
|
|
||||||
} catch (const ClientCommunicationError& exception) {
|
|
||||||
Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection");
|
|
||||||
this->closeClientConnection();
|
|
||||||
return;
|
|
||||||
|
|
||||||
} catch (const ClientNotSupported& exception) {
|
|
||||||
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
|
|
||||||
this->closeClientConnection();
|
|
||||||
return;
|
|
||||||
|
|
||||||
} catch (const DebugSessionAborted& exception) {
|
|
||||||
Logger::warning("GDB debug session aborted - " + exception.getMessage());
|
|
||||||
this->closeClientConnection();
|
|
||||||
return;
|
|
||||||
|
|
||||||
} catch (const DebugServerInterrupted&) {
|
|
||||||
// Server was interrupted
|
|
||||||
Logger::debug("GDB RSP interrupted");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::waitForConnection() {
|
|
||||||
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
|
|
||||||
throw Exception("Failed to listen on server socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<struct epoll_event, 5> events = {};
|
|
||||||
int eventCount = ::epoll_wait(
|
|
||||||
this->eventFileDescriptor,
|
|
||||||
events.data(),
|
|
||||||
5,
|
|
||||||
-1
|
|
||||||
);
|
|
||||||
|
|
||||||
if (eventCount > 0) {
|
|
||||||
for (size_t i = 0; i < eventCount; i++) {
|
|
||||||
auto fileDescriptor = events[i].data.fd;
|
|
||||||
|
|
||||||
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
|
|
||||||
// Interrupted
|
|
||||||
this->interruptEventNotifier->clear();
|
|
||||||
throw DebugServerInterrupted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->clientConnection = Connection(this->interruptEventNotifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
|
|
||||||
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
|
|
||||||
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
|
|
||||||
this->closeClientConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
|
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
|
||||||
auto packetData = packet.getData();
|
auto packetData = packet.getData();
|
||||||
auto packetString = std::string(packetData.begin(), packetData.end());
|
auto packetString = std::string(packetData.begin(), packetData.end());
|
||||||
@@ -234,13 +49,6 @@ void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
|
|
||||||
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
|
|
||||||
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
|
|
||||||
this->clientConnection->waitingForBreak = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
|
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
|
||||||
Logger::debug("Handling QuerySupport packet");
|
Logger::debug("Handling QuerySupport packet");
|
||||||
|
|
||||||
@@ -455,3 +263,195 @@ void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& pack
|
|||||||
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
|
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::init() {
|
||||||
|
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
|
||||||
|
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
|
||||||
|
auto configPortValue = configPortJsonValue->isString()
|
||||||
|
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
||||||
|
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
|
||||||
|
|
||||||
|
if (!ipAddress.empty()) {
|
||||||
|
this->listeningAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configPortValue > 0) {
|
||||||
|
this->listeningPortNumber = configPortValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->socketAddress.sin_family = AF_INET;
|
||||||
|
this->socketAddress.sin_port = htons(this->listeningPortNumber);
|
||||||
|
|
||||||
|
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
|
||||||
|
// Invalid IP address
|
||||||
|
throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
int socketFileDescriptor;
|
||||||
|
|
||||||
|
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||||
|
throw Exception("Failed to create socket file descriptor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::setsockopt(
|
||||||
|
socketFileDescriptor,
|
||||||
|
SOL_SOCKET,
|
||||||
|
SO_REUSEADDR,
|
||||||
|
&(this->enableReuseAddressSocketOption),
|
||||||
|
sizeof(this->enableReuseAddressSocketOption)
|
||||||
|
) < 0
|
||||||
|
) {
|
||||||
|
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::bind(
|
||||||
|
socketFileDescriptor,
|
||||||
|
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
||||||
|
sizeof(this->socketAddress)
|
||||||
|
) < 0
|
||||||
|
) {
|
||||||
|
throw Exception("Failed to bind address. The selected port number ("
|
||||||
|
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->serverSocketFileDescriptor = socketFileDescriptor;
|
||||||
|
|
||||||
|
this->eventFileDescriptor = ::epoll_create(2);
|
||||||
|
struct epoll_event event = {};
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
event.data.fd = this->serverSocketFileDescriptor;
|
||||||
|
|
||||||
|
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
|
||||||
|
throw Exception("Failed epoll_ctl server socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->interruptEventNotifier != nullptr) {
|
||||||
|
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
event.data.fd = interruptFileDescriptor;
|
||||||
|
|
||||||
|
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
|
||||||
|
throw Exception("Failed epoll_ctl interrupt event fd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("GDB RSP address: " + this->listeningAddress);
|
||||||
|
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
|
||||||
|
|
||||||
|
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
|
||||||
|
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
||||||
|
);
|
||||||
|
|
||||||
|
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||||
|
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::close() {
|
||||||
|
this->closeClientConnection();
|
||||||
|
|
||||||
|
if (this->serverSocketFileDescriptor > 0) {
|
||||||
|
::close(this->serverSocketFileDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::serve() {
|
||||||
|
try {
|
||||||
|
if (!this->clientConnection.has_value()) {
|
||||||
|
Logger::info("Waiting for GDB RSP connection");
|
||||||
|
|
||||||
|
do {
|
||||||
|
this->waitForConnection();
|
||||||
|
|
||||||
|
} while (!this->clientConnection.has_value());
|
||||||
|
|
||||||
|
this->clientConnection->accept(this->serverSocketFileDescriptor);
|
||||||
|
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
|
||||||
|
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
|
||||||
|
* service it.
|
||||||
|
*/
|
||||||
|
if (!this->targetControllerConsole.isTargetControllerInService()) {
|
||||||
|
this->closeClientConnection();
|
||||||
|
throw DebugSessionAborted("TargetController not in service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto packets = this->clientConnection->readPackets();
|
||||||
|
|
||||||
|
// Only process the last packet - any others will likely be duplicates from an impatient client
|
||||||
|
if (!packets.empty()) {
|
||||||
|
// Double-dispatch to appropriate handler
|
||||||
|
packets.back()->dispatchToHandler(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (const ClientDisconnected&) {
|
||||||
|
Logger::info("GDB RSP client disconnected");
|
||||||
|
this->closeClientConnection();
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (const ClientCommunicationError& exception) {
|
||||||
|
Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection");
|
||||||
|
this->closeClientConnection();
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (const ClientNotSupported& exception) {
|
||||||
|
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
|
||||||
|
this->closeClientConnection();
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (const DebugSessionAborted& exception) {
|
||||||
|
Logger::warning("GDB debug session aborted - " + exception.getMessage());
|
||||||
|
this->closeClientConnection();
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (const DebugServerInterrupted&) {
|
||||||
|
// Server was interrupted
|
||||||
|
Logger::debug("GDB RSP interrupted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::waitForConnection() {
|
||||||
|
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
|
||||||
|
throw Exception("Failed to listen on server socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<struct epoll_event, 5> events = {};
|
||||||
|
int eventCount = ::epoll_wait(
|
||||||
|
this->eventFileDescriptor,
|
||||||
|
events.data(),
|
||||||
|
5,
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventCount > 0) {
|
||||||
|
for (size_t i = 0; i < eventCount; i++) {
|
||||||
|
auto fileDescriptor = events[i].data.fd;
|
||||||
|
|
||||||
|
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
|
||||||
|
// Interrupted
|
||||||
|
this->interruptEventNotifier->clear();
|
||||||
|
throw DebugServerInterrupted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->clientConnection = Connection(this->interruptEventNotifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
|
||||||
|
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
|
||||||
|
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
|
||||||
|
this->closeClientConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
|
||||||
|
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
|
||||||
|
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
|
||||||
|
this->clientConnection->waitingForBreak = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,92 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
*/
|
*/
|
||||||
class GdbRspDebugServer: public DebugServer
|
class GdbRspDebugServer: public DebugServer
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
explicit GdbRspDebugServer(EventManager& eventManager): DebugServer(eventManager) {};
|
||||||
|
|
||||||
|
std::string getName() const override {
|
||||||
|
return "GDB Remote Serial Protocol DebugServer";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles any other GDB command packet that has not been promoted to a more specific type.
|
||||||
|
* This would be packets like "?" and "qAttached".
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::CommandPacket& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the supported features query ("qSupported") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the read registers ("g" and "p") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::ReadRegisters& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the write general register ("P") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::WriteRegister& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the continue execution ("c") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::ContinueExecution& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the step execution ("s") packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::StepExecution& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the read memory ("m") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::ReadMemory& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the write memory ("M") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::WriteMemory& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the set breakpoint ("Z") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::SetBreakpoint& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the remove breakpoint ("z") command packet.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::RemoveBreakpoint& packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the interrupt command packet.
|
||||||
|
* Will attempt to halt execution on the target. Should respond with a "stop reply" packet, or an error code.
|
||||||
|
*
|
||||||
|
* @param packet
|
||||||
|
*/
|
||||||
|
virtual void handleGdbPacket(CommandPackets::InterruptExecution& packet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* The port number for the GDB server to listen on.
|
* The port number for the GDB server to listen on.
|
||||||
@@ -156,19 +242,12 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
|
|
||||||
if (!mapping.contains(number)) {
|
if (!mapping.contains(number)) {
|
||||||
throw Exceptions::Exception("Unknown register from GDB - register number ("
|
throw Exceptions::Exception("Unknown register from GDB - register number ("
|
||||||
+ std::to_string(number) + ") not mapped to any register descriptor.");
|
+ std::to_string(number) + ") not mapped to any register descriptor.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapping.valueAt(number).value();
|
return mapping.valueAt(number).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
explicit GdbRspDebugServer(EventManager& eventManager): DebugServer(eventManager) {};
|
|
||||||
|
|
||||||
std::string getName() const override {
|
|
||||||
return "GDB Remote Serial Protocol DebugServer";
|
|
||||||
};
|
|
||||||
|
|
||||||
void onTargetControllerStateReported(const Events::TargetControllerStateReported& event);
|
void onTargetControllerStateReported(const Events::TargetControllerStateReported& event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,84 +255,5 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
* a "stop reply" packet to the client once the target execution stops.
|
* a "stop reply" packet to the client once the target execution stops.
|
||||||
*/
|
*/
|
||||||
void onTargetExecutionStopped(const Events::TargetExecutionStopped&);
|
void onTargetExecutionStopped(const Events::TargetExecutionStopped&);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles any other GDB command packet that has not been promoted to a more specific type.
|
|
||||||
* This would be packets like "?" and "qAttached".
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::CommandPacket& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the supported features query ("qSupported") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the read registers ("g" and "p") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::ReadRegisters& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the write general register ("P") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::WriteRegister& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the continue execution ("c") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::ContinueExecution& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the step execution ("s") packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::StepExecution& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the read memory ("m") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::ReadMemory& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the write memory ("M") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::WriteMemory& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the set breakpoint ("Z") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::SetBreakpoint& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the remove breakpoint ("z") command packet.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::RemoveBreakpoint& packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the interrupt command packet.
|
|
||||||
* Will attempt to halt execution on the target. Should respond with a "stop reply" packet, or an error code.
|
|
||||||
*
|
|
||||||
* @param packet
|
|
||||||
*/
|
|
||||||
virtual void handleGdbPacket(CommandPackets::InterruptExecution& packet);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,21 +16,12 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
*/
|
*/
|
||||||
class Packet
|
class Packet
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
std::vector<unsigned char> data;
|
|
||||||
|
|
||||||
void init(const std::vector<unsigned char>& rawPacket) {
|
|
||||||
this->data.insert(
|
|
||||||
this->data.begin(),
|
|
||||||
rawPacket.begin() + 1,
|
|
||||||
rawPacket.end() - 3
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
Packet() = default;
|
Packet() = default;
|
||||||
explicit Packet(const std::vector<unsigned char>& rawPacket) {
|
explicit Packet(const std::vector<unsigned char>& rawPacket) {
|
||||||
this->init(rawPacket);
|
this->init(rawPacket);
|
||||||
}
|
}
|
||||||
|
virtual ~Packet() = default;
|
||||||
|
|
||||||
[[nodiscard]] virtual std::vector<unsigned char> getData() const {
|
[[nodiscard]] virtual std::vector<unsigned char> getData() const {
|
||||||
return this->data;
|
return this->data;
|
||||||
@@ -118,6 +109,15 @@ namespace Bloom::DebugServers::Gdb
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Packet() = default;
|
protected:
|
||||||
|
std::vector<unsigned char> data;
|
||||||
|
|
||||||
|
void init(const std::vector<unsigned char>& rawPacket) {
|
||||||
|
this->data.insert(
|
||||||
|
this->data.begin(),
|
||||||
|
rawPacket.begin() + 1,
|
||||||
|
rawPacket.end() - 3
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "ResponsePacket.hpp"
|
#include "ResponsePacket.hpp"
|
||||||
|
|
||||||
namespace Bloom::DebugServers::Gdb {
|
namespace Bloom::DebugServers::Gdb {
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ namespace Bloom::DebugServers::Gdb::ResponsePackets
|
|||||||
*/
|
*/
|
||||||
class SupportedFeaturesResponse: public ResponsePacket
|
class SupportedFeaturesResponse: public ResponsePacket
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SupportedFeaturesResponse() = default;
|
SupportedFeaturesResponse() = default;
|
||||||
explicit SupportedFeaturesResponse(std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures)
|
explicit SupportedFeaturesResponse(std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures)
|
||||||
: supportedFeatures(std::move(supportedFeatures)) {};
|
: supportedFeatures(std::move(supportedFeatures)) {};
|
||||||
|
|
||||||
[[nodiscard]] std::vector<unsigned char> getData() const override;
|
[[nodiscard]] std::vector<unsigned char> getData() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,18 +14,8 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class DebugTool
|
class DebugTool
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
bool initialised = false;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setInitialised(bool initialised) {
|
|
||||||
this->initialised = initialised;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] bool isInitialised() const {
|
virtual ~DebugTool() = default;
|
||||||
return this->initialised;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should establish a connection to the device and prepare it for a debug session.
|
* Should establish a connection to the device and prepare it for a debug session.
|
||||||
@@ -55,6 +45,16 @@ namespace Bloom
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~DebugTool() = default;
|
[[nodiscard]] bool isInitialised() const {
|
||||||
|
return this->initialised;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setInitialised(bool initialised) {
|
||||||
|
this->initialised = initialised;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initialised = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,25 +40,6 @@ namespace Bloom::DebugToolDrivers
|
|||||||
*/
|
*/
|
||||||
class AtmelIce: public DebugTool, public Usb::UsbDevice
|
class AtmelIce: public DebugTool, public Usb::UsbDevice
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* The EDBG interface implements additional functionality via vendor specific CMSIS-DAP commands.
|
|
||||||
* In other words, all EDBG commands are just CMSIS-DAP vendor commands that allow the debug tool
|
|
||||||
* to support additional functionality, like AVR programming and debugging.
|
|
||||||
*
|
|
||||||
* Any non-EDBG CMSIS-DAP commands for the Atmel-ICE can be sent through the EdbgInterface (as the
|
|
||||||
* EdbgInterface extends the CmsisDapInterface).
|
|
||||||
*/
|
|
||||||
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Atmel-ICE employs the EDBG AVR8 Generic protocol, for debugging AVR8 targets. This protocol is
|
|
||||||
* implemented in EdbgAvr8Interface. See the EdbgAvr8Interface class for more information.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
|
||||||
|
|
||||||
bool sessionStarted = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const std::uint16_t USB_VENDOR_ID = 1003;
|
static const std::uint16_t USB_VENDOR_ID = 1003;
|
||||||
static const std::uint16_t USB_PRODUCT_ID = 8513;
|
static const std::uint16_t USB_PRODUCT_ID = 8513;
|
||||||
@@ -97,5 +78,24 @@ namespace Bloom::DebugToolDrivers
|
|||||||
* Ends the active session with the debug tool.
|
* Ends the active session with the debug tool.
|
||||||
*/
|
*/
|
||||||
void endSession();
|
void endSession();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* The EDBG interface implements additional functionality via vendor specific CMSIS-DAP commands.
|
||||||
|
* In other words, all EDBG commands are just CMSIS-DAP vendor commands that allow the debug tool
|
||||||
|
* to support additional functionality, like AVR programming and debugging.
|
||||||
|
*
|
||||||
|
* Any non-EDBG CMSIS-DAP commands for the Atmel-ICE can be sent through the EdbgInterface (as the
|
||||||
|
* EdbgInterface extends the CmsisDapInterface).
|
||||||
|
*/
|
||||||
|
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Atmel-ICE employs the EDBG AVR8 Generic protocol, for debugging AVR8 targets. This protocol is
|
||||||
|
* implemented in EdbgAvr8Interface. See the EdbgAvr8Interface class for more information.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
||||||
|
|
||||||
|
bool sessionStarted = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,17 +34,6 @@ namespace Bloom::DebugToolDrivers
|
|||||||
*/
|
*/
|
||||||
class MplabSnap: public DebugTool, public Usb::UsbDevice
|
class MplabSnap: public DebugTool, public Usb::UsbDevice
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The MPLAB Snap employs the EDBG AVR8 Generic protocol, for debugging AVR8 targets. This protocol is
|
|
||||||
* implemented in EdbgAvr8Interface. See the EdbgAvr8Interface class for more information.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
|
||||||
|
|
||||||
bool sessionStarted = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const std::uint16_t USB_VENDOR_ID = 1003;
|
static const std::uint16_t USB_VENDOR_ID = 1003;
|
||||||
static const std::uint16_t USB_PRODUCT_ID = 8576;
|
static const std::uint16_t USB_PRODUCT_ID = 8576;
|
||||||
@@ -83,5 +72,16 @@ namespace Bloom::DebugToolDrivers
|
|||||||
* Ends the active session with the debug tool.
|
* Ends the active session with the debug tool.
|
||||||
*/
|
*/
|
||||||
void endSession();
|
void endSession();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MPLAB Snap employs the EDBG AVR8 Generic protocol, for debugging AVR8 targets. This protocol is
|
||||||
|
* implemented in EdbgAvr8Interface. See the EdbgAvr8Interface class for more information.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
||||||
|
|
||||||
|
bool sessionStarted = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,24 +27,6 @@ namespace Bloom::DebugToolDrivers
|
|||||||
*/
|
*/
|
||||||
class PowerDebugger: public DebugTool, public Usb::UsbDevice
|
class PowerDebugger: public DebugTool, public Usb::UsbDevice
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* The EDBG interface implements additional functionality via vendor specific CMSIS-DAP commands.
|
|
||||||
* In other words, all EDBG commands are just CMSIS-DAP vendor commands that allow the debug tool
|
|
||||||
* to support additional functionality, like AVR programming and debugging.
|
|
||||||
*
|
|
||||||
* Any non-EDBG CMSIS-DAP commands for the Power Debugger can be sent through the EDBGInterface (as the
|
|
||||||
* EdbgInterface extends the CmsisDapInterface).
|
|
||||||
*/
|
|
||||||
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Power Debugger employs the EDBG AVR8Generic protocol for interfacing with AVR8 targets.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface;
|
|
||||||
|
|
||||||
bool sessionStarted = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const std::uint16_t USB_VENDOR_ID = 1003;
|
static const std::uint16_t USB_VENDOR_ID = 1003;
|
||||||
static const std::uint16_t USB_PRODUCT_ID = 8516;
|
static const std::uint16_t USB_PRODUCT_ID = 8516;
|
||||||
@@ -83,5 +65,23 @@ namespace Bloom::DebugToolDrivers
|
|||||||
* Ends the active session with the debug tool.
|
* Ends the active session with the debug tool.
|
||||||
*/
|
*/
|
||||||
void endSession();
|
void endSession();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* The EDBG interface implements additional functionality via vendor specific CMSIS-DAP commands.
|
||||||
|
* In other words, all EDBG commands are just CMSIS-DAP vendor commands that allow the debug tool
|
||||||
|
* to support additional functionality, like AVR programming and debugging.
|
||||||
|
*
|
||||||
|
* Any non-EDBG CMSIS-DAP commands for the Power Debugger can be sent through the EDBGInterface (as the
|
||||||
|
* EdbgInterface extends the CmsisDapInterface).
|
||||||
|
*/
|
||||||
|
Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Power Debugger employs the EDBG AVR8Generic protocol for interfacing with AVR8 targets.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface;
|
||||||
|
|
||||||
|
bool sessionStarted = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "CmsisDapInterface.hpp"
|
#include "CmsisDapInterface.hpp"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||||
|
|||||||
@@ -17,27 +17,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
*/
|
*/
|
||||||
class CmsisDapInterface
|
class CmsisDapInterface
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* All CMSIS-DAP devices employ the USB HID interface for communication.
|
|
||||||
*
|
|
||||||
* For many CMSIS-DAP devices, the USB HID interface parameters (interface number, endpoint config, etc) vary
|
|
||||||
* amongst devices, so we'll need to be able to preActivationConfigure the CMSISDAPInterface from a
|
|
||||||
* higher level. For an example, see the constructor of the AtmelIce device class.
|
|
||||||
*/
|
|
||||||
Usb::HidInterface usbHidInterface = Usb::HidInterface();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some CMSIS-DAP debug tools fail to operate properly when we send commands too quickly. Even if we've
|
|
||||||
* received a response from every previous command.
|
|
||||||
*
|
|
||||||
* Because of this, we may need to enforce a minimum time gap between sending CMSIS commands.
|
|
||||||
* Setting msSendCommandDelay to any value above 0 will enforce an x millisecond gap between each command
|
|
||||||
* being sent, where x is the value of msSendCommandDelay.
|
|
||||||
*/
|
|
||||||
std::chrono::milliseconds msSendCommandDelay = std::chrono::milliseconds(0);
|
|
||||||
long lastCommandSentTimeStamp = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CmsisDapInterface() = default;
|
explicit CmsisDapInterface() = default;
|
||||||
|
|
||||||
@@ -81,5 +60,26 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
virtual std::unique_ptr<Protocols::CmsisDap::Response> sendCommandAndWaitForResponse(
|
virtual std::unique_ptr<Protocols::CmsisDap::Response> sendCommandAndWaitForResponse(
|
||||||
const Protocols::CmsisDap::Command& cmsisDapCommand
|
const Protocols::CmsisDap::Command& cmsisDapCommand
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* All CMSIS-DAP devices employ the USB HID interface for communication.
|
||||||
|
*
|
||||||
|
* For many CMSIS-DAP devices, the USB HID interface parameters (interface number, endpoint config, etc) vary
|
||||||
|
* amongst devices, so we'll need to be able to preActivationConfigure the CMSISDAPInterface from a
|
||||||
|
* higher level. For an example, see the constructor of the AtmelIce device class.
|
||||||
|
*/
|
||||||
|
Usb::HidInterface usbHidInterface = Usb::HidInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some CMSIS-DAP debug tools fail to operate properly when we send commands too quickly. Even if we've
|
||||||
|
* received a response from every previous command.
|
||||||
|
*
|
||||||
|
* Because of this, we may need to enforce a minimum time gap between sending CMSIS commands.
|
||||||
|
* Setting msSendCommandDelay to any value above 0 will enforce an x millisecond gap between each command
|
||||||
|
* being sent, where x is the value of msSendCommandDelay.
|
||||||
|
*/
|
||||||
|
std::chrono::milliseconds msSendCommandDelay = std::chrono::milliseconds(0);
|
||||||
|
long lastCommandSentTimeStamp = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
{
|
{
|
||||||
class Command
|
class Command
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
unsigned char commandId = 0x00;
|
|
||||||
std::vector<unsigned char> data;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual ~Command() = default;
|
||||||
|
|
||||||
[[nodiscard]] unsigned char getCommandId() const {
|
[[nodiscard]] unsigned char getCommandId() const {
|
||||||
return this->commandId;
|
return this->commandId;
|
||||||
}
|
}
|
||||||
@@ -45,6 +43,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
*/
|
*/
|
||||||
explicit virtual operator std::vector<unsigned char>() const;
|
explicit virtual operator std::vector<unsigned char>() const;
|
||||||
|
|
||||||
virtual ~Command() = default;
|
private:
|
||||||
|
unsigned char commandId = 0x00;
|
||||||
|
std::vector<unsigned char> data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,10 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
{
|
{
|
||||||
class Response
|
class Response
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
unsigned char responseId = 0x00;
|
|
||||||
|
|
||||||
std::vector<unsigned char> data;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setResponseId(unsigned char commandId) {
|
|
||||||
this->responseId = commandId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setData(const std::vector<unsigned char>& data) {
|
|
||||||
this->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Response() = default;
|
Response() = default;
|
||||||
|
virtual ~Response() = default;
|
||||||
|
|
||||||
virtual void init(const std::vector<unsigned char>& rawResponse);
|
virtual void init(const std::vector<unsigned char>& rawResponse);
|
||||||
|
|
||||||
[[nodiscard]] unsigned char getResponseId() const {
|
[[nodiscard]] unsigned char getResponseId() const {
|
||||||
@@ -32,6 +20,17 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
|||||||
return this->data;
|
return this->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Response() = default;
|
protected:
|
||||||
|
void setResponseId(unsigned char commandId) {
|
||||||
|
this->responseId = commandId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setData(const std::vector<unsigned char>& data) {
|
||||||
|
this->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char responseId = 0x00;
|
||||||
|
std::vector<unsigned char> data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
{
|
{
|
||||||
class AvrCommand: public Command
|
class AvrCommand: public Command
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
size_t fragmentNumber = 1;
|
|
||||||
size_t fragmentCount = 1;
|
|
||||||
|
|
||||||
std::vector<unsigned char> commandPacket;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvrCommand() {
|
AvrCommand() {
|
||||||
this->setCommandId(0x80);
|
this->setCommandId(0x80);
|
||||||
@@ -49,5 +43,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
void setCommandPacket(const std::vector<unsigned char>& commandPacket) {
|
void setCommandPacket(const std::vector<unsigned char>& commandPacket) {
|
||||||
this->commandPacket = commandPacket;
|
this->commandPacket = commandPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t fragmentNumber = 1;
|
||||||
|
size_t fragmentCount = 1;
|
||||||
|
|
||||||
|
std::vector<unsigned char> commandPacket;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ void AvrEvent::init(const std::vector<unsigned char>& rawResponse) {
|
|||||||
if (responseData.size() < 2) {
|
if (responseData.size() < 2) {
|
||||||
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
|
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
|
||||||
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
|
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
|
||||||
"returned no additional data.");
|
"returned no additional data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response size is two bytes, MSB
|
// Response size is two bytes, MSB
|
||||||
auto responsePacketSize = static_cast<size_t>((responseData[0] << 8) | responseData[1]);
|
auto responsePacketSize = static_cast<std::size_t>((responseData[0] << 8) | responseData[1]);
|
||||||
|
|
||||||
if (responseData.size() < 2) {
|
if (responseData.size() < 2) {
|
||||||
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
|
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
|
||||||
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
|
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
|
||||||
"contained invalid event data size.");
|
"contained invalid event data size.");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto eventData = std::vector<unsigned char>();
|
auto eventData = std::vector<unsigned char>();
|
||||||
|
|||||||
@@ -22,16 +22,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
|
|
||||||
class AvrEvent: public Response
|
class AvrEvent: public Response
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
unsigned char eventId = 0;
|
|
||||||
|
|
||||||
std::vector<unsigned char> eventData;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setEventData(const std::vector<unsigned char>& eventData) {
|
|
||||||
this->eventData = eventData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvrEvent() = default;
|
AvrEvent() = default;
|
||||||
|
|
||||||
@@ -59,5 +49,14 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
[[nodiscard]] AvrEventId getEventId() const {
|
[[nodiscard]] AvrEventId getEventId() const {
|
||||||
return static_cast<AvrEventId>(this->eventId);
|
return static_cast<AvrEventId>(this->eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setEventData(const std::vector<unsigned char>& eventData) {
|
||||||
|
this->eventData = eventData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char eventId = 0;
|
||||||
|
std::vector<unsigned char> eventData;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "AvrResponse.hpp"
|
#include "AvrResponse.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
||||||
|
|||||||
@@ -9,25 +9,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
{
|
{
|
||||||
class AvrResponse: public Response
|
class AvrResponse: public Response
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::uint8_t fragmentNumber = 0;
|
|
||||||
std::uint8_t fragmentCount = 0;
|
|
||||||
|
|
||||||
std::vector<unsigned char> responsePacket;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setFragmentNumber(std::uint8_t fragmentNumber) {
|
|
||||||
this->fragmentNumber = fragmentNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFragmentCount(std::uint8_t fragmentCount) {
|
|
||||||
this->fragmentCount = fragmentCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setResponsePacket(const std::vector<unsigned char>& responsePacket) {
|
|
||||||
this->responsePacket = responsePacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvrResponse() = default;
|
AvrResponse() = default;
|
||||||
|
|
||||||
@@ -55,5 +36,24 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
[[nodiscard]] const std::vector<unsigned char>& getResponsePacket() const {
|
[[nodiscard]] const std::vector<unsigned char>& getResponsePacket() const {
|
||||||
return this->responsePacket;
|
return this->responsePacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setFragmentNumber(std::uint8_t fragmentNumber) {
|
||||||
|
this->fragmentNumber = fragmentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFragmentCount(std::uint8_t fragmentCount) {
|
||||||
|
this->fragmentCount = fragmentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setResponsePacket(const std::vector<unsigned char>& responsePacket) {
|
||||||
|
this->responsePacket = responsePacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint8_t fragmentNumber = 0;
|
||||||
|
std::uint8_t fragmentCount = 0;
|
||||||
|
|
||||||
|
std::vector<unsigned char> responsePacket;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||||
|
|
||||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class ActivatePhysical: public Avr8GenericCommandFrame
|
class ActivatePhysical: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
bool reset = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ActivatePhysical() = default;
|
ActivatePhysical() = default;
|
||||||
explicit ActivatePhysical(bool reset): reset(reset) {};
|
explicit ActivatePhysical(bool reset): reset(reset) {};
|
||||||
@@ -31,6 +28,9 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool reset = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class Attach: public Avr8GenericCommandFrame
|
class Attach: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
bool breakAfterAttach = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Attach() = default;
|
Attach() = default;
|
||||||
explicit Attach(bool breakAfterAttach): breakAfterAttach(breakAfterAttach) {};
|
explicit Attach(bool breakAfterAttach): breakAfterAttach(breakAfterAttach) {};
|
||||||
@@ -31,6 +28,9 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool breakAfterAttach = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class ClearAllSoftwareBreakpoints: public Avr8GenericCommandFrame
|
class ClearAllSoftwareBreakpoints: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
ClearAllSoftwareBreakpoints() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -18,10 +23,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
ClearAllSoftwareBreakpoints() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class ClearSoftwareBreakpoints: public Avr8GenericCommandFrame
|
class ClearSoftwareBreakpoints: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::vector<std::uint32_t> addresses;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClearSoftwareBreakpoints() = default;
|
ClearSoftwareBreakpoints() = default;
|
||||||
|
|
||||||
@@ -43,5 +40,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::uint32_t> addresses;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class DeactivatePhysical: public Avr8GenericCommandFrame
|
class DeactivatePhysical: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
DeactivatePhysical() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -18,10 +23,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
DeactivatePhysical() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class Detach: public Avr8GenericCommandFrame
|
class Detach: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
Detach() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -18,10 +23,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
Detach() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class DisableDebugWire: public Avr8GenericCommandFrame
|
class DisableDebugWire: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
DisableDebugWire() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -18,10 +23,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
DisableDebugWire() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class GetDeviceId: public Avr8GenericCommandFrame
|
class GetDeviceId: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
using ResponseFrameType = ResponseFrames::Avr8Generic::GetDeviceId;
|
||||||
|
|
||||||
|
GetDeviceId() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -19,12 +26,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
using ResponseFrameType = ResponseFrames::Avr8Generic::GetDeviceId;
|
|
||||||
|
|
||||||
GetDeviceId() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class GetParameter: public Avr8GenericCommandFrame
|
class GetParameter: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
Avr8EdbgParameter parameter;
|
|
||||||
std::uint8_t size = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GetParameter() = default;
|
GetParameter() = default;
|
||||||
|
|
||||||
@@ -49,5 +45,9 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avr8EdbgParameter parameter;
|
||||||
|
std::uint8_t size = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class GetProgramCounter: public Avr8GenericCommandFrame
|
class GetProgramCounter: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
using ResponseFrameType = ResponseFrames::Avr8Generic::GetProgramCounter;
|
||||||
|
|
||||||
|
GetProgramCounter() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -19,12 +26,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
using ResponseFrameType = ResponseFrames::Avr8Generic::GetProgramCounter;
|
|
||||||
|
|
||||||
GetProgramCounter() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class ReadMemory: public Avr8GenericCommandFrame
|
class ReadMemory: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
Avr8MemoryType type = Avr8MemoryType::SRAM;
|
|
||||||
std::uint32_t address = 0;
|
|
||||||
std::uint32_t bytes = 0;
|
|
||||||
std::set<std::uint32_t> excludedAddresses;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using ResponseFrameType = ResponseFrames::Avr8Generic::ReadMemory;
|
using ResponseFrameType = ResponseFrames::Avr8Generic::ReadMemory;
|
||||||
|
|
||||||
@@ -38,5 +32,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<unsigned char> getPayload() const override;
|
[[nodiscard]] std::vector<unsigned char> getPayload() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avr8MemoryType type = Avr8MemoryType::SRAM;
|
||||||
|
std::uint32_t address = 0;
|
||||||
|
std::uint32_t bytes = 0;
|
||||||
|
std::set<std::uint32_t> excludedAddresses;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class Reset: public Avr8GenericCommandFrame
|
class Reset: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
bool stopAtMainAddress = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Reset() = default;
|
Reset() = default;
|
||||||
explicit Reset(bool stopAtMainAddress): stopAtMainAddress(stopAtMainAddress) {};
|
explicit Reset(bool stopAtMainAddress): stopAtMainAddress(stopAtMainAddress) {};
|
||||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool stopAtMainAddress = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class Run: public Avr8GenericCommandFrame
|
class Run: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
Run() {
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -18,10 +23,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
Run() {
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class RunTo: public Avr8GenericCommandFrame
|
class RunTo: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::uint32_t address = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RunTo() = default;
|
RunTo() = default;
|
||||||
|
|
||||||
@@ -41,5 +38,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint32_t address = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class SetParameter: public Avr8GenericCommandFrame
|
class SetParameter: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
Avr8EdbgParameter parameter;
|
|
||||||
std::vector<unsigned char> value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SetParameter() = default;
|
SetParameter() = default;
|
||||||
|
|
||||||
@@ -57,5 +53,9 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avr8EdbgParameter parameter;
|
||||||
|
std::vector<unsigned char> value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class SetProgramCounter: public Avr8GenericCommandFrame
|
class SetProgramCounter: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::uint32_t programCounter = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SetProgramCounter(std::uint32_t programCounter): programCounter(programCounter) {}
|
explicit SetProgramCounter(std::uint32_t programCounter): programCounter(programCounter) {}
|
||||||
|
|
||||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint32_t programCounter = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class SetSoftwareBreakpoints: public Avr8GenericCommandFrame
|
class SetSoftwareBreakpoints: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::vector<std::uint32_t> addresses;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SetSoftwareBreakpoints() = default;
|
SetSoftwareBreakpoints() = default;
|
||||||
|
|
||||||
@@ -43,5 +40,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::uint32_t> addresses;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class SetXmegaSoftwareBreakpoint: public Avr8GenericCommandFrame
|
class SetXmegaSoftwareBreakpoint: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::uint32_t address = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SetXmegaSoftwareBreakpoint() = default;
|
SetXmegaSoftwareBreakpoint() = default;
|
||||||
|
|
||||||
@@ -40,5 +37,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint32_t address = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class Stop: public Avr8GenericCommandFrame
|
class Stop: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
bool stopImmediately = true;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Stop() = default;
|
Stop() = default;
|
||||||
explicit Stop(bool stopImmediately): stopImmediately(stopImmediately) {};
|
explicit Stop(bool stopImmediately): stopImmediately(stopImmediately) {};
|
||||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool stopImmediately = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
{
|
{
|
||||||
class WriteMemory: public Avr8GenericCommandFrame
|
class WriteMemory: public Avr8GenericCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
Avr8MemoryType type = Avr8MemoryType::SRAM;
|
|
||||||
std::uint32_t address = 0;
|
|
||||||
Targets::TargetMemoryBuffer buffer;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WriteMemory() = default;
|
WriteMemory() = default;
|
||||||
|
|
||||||
@@ -62,5 +57,10 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avr8MemoryType type = Avr8MemoryType::SRAM;
|
||||||
|
std::uint32_t address = 0;
|
||||||
|
Targets::TargetMemoryBuffer buffer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,24 +14,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
{
|
{
|
||||||
class AvrCommandFrame
|
class AvrCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
unsigned char SOF = 0x0E;
|
|
||||||
|
|
||||||
unsigned char protocolVersion = 0x00;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Incrementing from 0x00
|
|
||||||
*/
|
|
||||||
std::uint16_t sequenceId = 0;
|
|
||||||
inline static std::uint16_t lastSequenceId = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destination sub-protocol handler ID
|
|
||||||
*/
|
|
||||||
ProtocolHandlerId protocolHandlerID = ProtocolHandlerId::DISCOVERY;
|
|
||||||
|
|
||||||
std::vector<unsigned char> payload;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using ResponseFrameType = AvrResponseFrame;
|
using ResponseFrameType = AvrResponseFrame;
|
||||||
|
|
||||||
@@ -102,6 +84,24 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
explicit virtual operator std::vector<unsigned char> () const;
|
explicit virtual operator std::vector<unsigned char> () const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char SOF = 0x0E;
|
||||||
|
|
||||||
|
unsigned char protocolVersion = 0x00;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incrementing from 0x00
|
||||||
|
*/
|
||||||
|
std::uint16_t sequenceId = 0;
|
||||||
|
inline static std::uint16_t lastSequenceId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination sub-protocol handler ID
|
||||||
|
*/
|
||||||
|
ProtocolHandlerId protocolHandlerID = ProtocolHandlerId::DISCOVERY;
|
||||||
|
|
||||||
|
std::vector<unsigned char> payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
*/
|
*/
|
||||||
class Query: public DiscoveryCommandFrame
|
class Query: public DiscoveryCommandFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
QueryContext context = QueryContext::COMMAND_HANDLERS;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Query(): DiscoveryCommandFrame() {}
|
Query(): DiscoveryCommandFrame() {}
|
||||||
|
|
||||||
@@ -45,5 +42,8 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QueryContext context = QueryContext::COMMAND_HANDLERS;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
*/
|
*/
|
||||||
class EndSession: public HouseKeepingCommandFrame
|
class EndSession: public HouseKeepingCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
EndSession(): HouseKeepingCommandFrame() {
|
||||||
|
this->init();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -21,10 +26,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[2] = 0x00;
|
payload[2] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
EndSession(): HouseKeepingCommandFrame() {
|
|
||||||
this->init();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
*/
|
*/
|
||||||
class StartSession: public HouseKeepingCommandFrame
|
class StartSession: public HouseKeepingCommandFrame
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
StartSession(): HouseKeepingCommandFrame() {
|
||||||
|
this->init();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init() {
|
void init() {
|
||||||
/*
|
/*
|
||||||
@@ -20,10 +25,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames
|
|||||||
payload[1] = 0x00;
|
payload[1] = 0x00;
|
||||||
this->setPayload(payload);
|
this->setPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
StartSession(): HouseKeepingCommandFrame() {
|
|
||||||
this->init();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,198 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
*/
|
*/
|
||||||
class EdbgAvr8Interface: public TargetInterfaces::Microchip::Avr::Avr8::Avr8Interface
|
class EdbgAvr8Interface: public TargetInterfaces::Microchip::Avr::Avr8::Avr8Interface
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
explicit EdbgAvr8Interface(EdbgInterface& edbgInterface)
|
||||||
|
: edbgInterface(edbgInterface) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables use of the masked read memory EDBG command. Masking will be performed at the driver-side.
|
||||||
|
*
|
||||||
|
* @param avoidMaskedMemoryRead
|
||||||
|
*/
|
||||||
|
void setAvoidMaskedMemoryRead(bool avoidMaskedMemoryRead) {
|
||||||
|
this->avoidMaskedMemoryRead = avoidMaskedMemoryRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The public methods below implement the interface defined by the Avr8Interface class.
|
||||||
|
* See the comments in that class for more info on the expected behaviour of each method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As already mentioned in numerous comments above, the EdbgAvr8Interface requires some configuration from
|
||||||
|
* the user. This is supplied via the user's Bloom configuration.
|
||||||
|
*
|
||||||
|
* @param targetConfig
|
||||||
|
*/
|
||||||
|
void configure(const TargetConfig& targetConfig) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the target family. For some physical interfaces, the target family is required in order
|
||||||
|
* properly configure the EDBG tool. See EdbgAvr8Interface::resolveConfigVariant() for more.
|
||||||
|
*
|
||||||
|
* @param family
|
||||||
|
*/
|
||||||
|
void setFamily(Targets::Microchip::Avr::Avr8Bit::Family family) override {
|
||||||
|
this->family = family;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts target parameters from the AVR8 target instance and sends the necessary target parameters to the
|
||||||
|
* debug tool.
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
void setTargetParameters(const Targets::Microchip::Avr::Avr8Bit::TargetParameters& config) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the AVR8 Generic protocol interface by setting the appropriate parameters on the debug tool.
|
||||||
|
*/
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "stop" command to the debug tool, halting target execution.
|
||||||
|
*/
|
||||||
|
void stop() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "run" command to the debug tool, resuming execution on the target.
|
||||||
|
*/
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "run to" command to the debug tool, resuming execution on the target, up to a specific byte
|
||||||
|
* address. The target will dispatch an AVR BREAK event once it reaches the specified address.
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* The (byte) address to run to.
|
||||||
|
*/
|
||||||
|
void runTo(std::uint32_t address) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "step" command to the debug tool, stepping the execution on the target. The stepping can be
|
||||||
|
* configured to step in, out or over. But currently we only support stepping in. The target will dispatch
|
||||||
|
* an AVR BREAK event once it reaches the next instruction.
|
||||||
|
*/
|
||||||
|
void step() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "reset" command to the debug tool, resetting target execution.
|
||||||
|
*/
|
||||||
|
void reset() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the physical interface and starts a debug session on the target (via attach()).
|
||||||
|
*/
|
||||||
|
void activate() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates any active debug session on the target and severs the connection between the debug tool and
|
||||||
|
* the target (by deactivating the physical interface).
|
||||||
|
*/
|
||||||
|
void deactivate() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "PC Read" command to the debug tool, to extract the current program counter.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::uint32_t getProgramCounter() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "PC Write" command to the debug tool, setting the program counter on the target.
|
||||||
|
*
|
||||||
|
* @param programCounter
|
||||||
|
* The byte address to set as the program counter.
|
||||||
|
*/
|
||||||
|
void setProgramCounter(std::uint32_t programCounter) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "Get ID" command to the debug tool, to extract the signature from the target.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Targets::Microchip::Avr::TargetSignature getDeviceId() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "Software Breakpoint Set" command to the debug tool, setting a software breakpoint at the given
|
||||||
|
* byte address.
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* The byte address to position the breakpoint.
|
||||||
|
*/
|
||||||
|
void setBreakpoint(std::uint32_t address) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any breakpoint at the given
|
||||||
|
* byte address.
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* The byte address of the breakpoint to clear.
|
||||||
|
*/
|
||||||
|
void clearBreakpoint(std::uint32_t address) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
|
||||||
|
* that were set *in the current debug session*.
|
||||||
|
*
|
||||||
|
* If the debug session ended before any of the set breakpoints were cleared, this will *not* clear them.
|
||||||
|
*/
|
||||||
|
void clearAllBreakpoints() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads registers from the target.
|
||||||
|
*
|
||||||
|
* @param descriptors
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptors& descriptors) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes registers to target.
|
||||||
|
*
|
||||||
|
* @param registers
|
||||||
|
*/
|
||||||
|
void writeRegisters(const Targets::TargetRegisters& registers) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an overloaded method.
|
||||||
|
*
|
||||||
|
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory().
|
||||||
|
*
|
||||||
|
* @param memoryType
|
||||||
|
* @param startAddress
|
||||||
|
* @param bytes
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Targets::TargetMemoryBuffer readMemory(
|
||||||
|
Targets::TargetMemoryType memoryType,
|
||||||
|
std::uint32_t startAddress,
|
||||||
|
std::uint32_t bytes
|
||||||
|
) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an overloaded method.
|
||||||
|
*
|
||||||
|
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls writeMemory().
|
||||||
|
*
|
||||||
|
* @param memoryType
|
||||||
|
* @param startAddress
|
||||||
|
* @param buffer
|
||||||
|
*/
|
||||||
|
void writeMemory(
|
||||||
|
Targets::TargetMemoryType memoryType,
|
||||||
|
std::uint32_t startAddress,
|
||||||
|
const Targets::TargetMemoryBuffer& buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current state of the target.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Targets::TargetState getTargetState() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The AVR8 Generic protocol is a sub-protocol of the EDBG AVR protocol, which is served via CMSIS-DAP vendor
|
* The AVR8 Generic protocol is a sub-protocol of the EDBG AVR protocol, which is served via CMSIS-DAP vendor
|
||||||
@@ -459,197 +651,5 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
* This should only be used when a BreakEvent is always expected.
|
* This should only be used when a BreakEvent is always expected.
|
||||||
*/
|
*/
|
||||||
void waitForStoppedEvent();
|
void waitForStoppedEvent();
|
||||||
|
|
||||||
public:
|
|
||||||
explicit EdbgAvr8Interface(EdbgInterface& edbgInterface)
|
|
||||||
: edbgInterface(edbgInterface) {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables use of the masked read memory EDBG command. Masking will be performed at the driver-side.
|
|
||||||
*
|
|
||||||
* @param avoidMaskedMemoryRead
|
|
||||||
*/
|
|
||||||
void setAvoidMaskedMemoryRead(bool avoidMaskedMemoryRead) {
|
|
||||||
this->avoidMaskedMemoryRead = avoidMaskedMemoryRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The public methods below implement the interface defined by the Avr8Interface class.
|
|
||||||
* See the comments in that class for more info on the expected behaviour of each method.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As already mentioned in numerous comments above, the EdbgAvr8Interface requires some configuration from
|
|
||||||
* the user. This is supplied via the user's Bloom configuration.
|
|
||||||
*
|
|
||||||
* @param targetConfig
|
|
||||||
*/
|
|
||||||
void configure(const TargetConfig& targetConfig) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the target family. For some physical interfaces, the target family is required in order
|
|
||||||
* properly configure the EDBG tool. See EdbgAvr8Interface::resolveConfigVariant() for more.
|
|
||||||
*
|
|
||||||
* @param family
|
|
||||||
*/
|
|
||||||
void setFamily(Targets::Microchip::Avr::Avr8Bit::Family family) override {
|
|
||||||
this->family = family;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts target parameters from the AVR8 target instance and sends the necessary target parameters to the
|
|
||||||
* debug tool.
|
|
||||||
*
|
|
||||||
* @param config
|
|
||||||
*/
|
|
||||||
void setTargetParameters(const Targets::Microchip::Avr::Avr8Bit::TargetParameters& config) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the AVR8 Generic protocol interface by setting the appropriate parameters on the debug tool.
|
|
||||||
*/
|
|
||||||
void init() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "stop" command to the debug tool, halting target execution.
|
|
||||||
*/
|
|
||||||
void stop() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "run" command to the debug tool, resuming execution on the target.
|
|
||||||
*/
|
|
||||||
void run() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "run to" command to the debug tool, resuming execution on the target, up to a specific byte
|
|
||||||
* address. The target will dispatch an AVR BREAK event once it reaches the specified address.
|
|
||||||
*
|
|
||||||
* @param address
|
|
||||||
* The (byte) address to run to.
|
|
||||||
*/
|
|
||||||
void runTo(std::uint32_t address) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "step" command to the debug tool, stepping the execution on the target. The stepping can be
|
|
||||||
* configured to step in, out or over. But currently we only support stepping in. The target will dispatch
|
|
||||||
* an AVR BREAK event once it reaches the next instruction.
|
|
||||||
*/
|
|
||||||
void step() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "reset" command to the debug tool, resetting target execution.
|
|
||||||
*/
|
|
||||||
void reset() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activates the physical interface and starts a debug session on the target (via attach()).
|
|
||||||
*/
|
|
||||||
void activate() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Terminates any active debug session on the target and severs the connection between the debug tool and
|
|
||||||
* the target (by deactivating the physical interface).
|
|
||||||
*/
|
|
||||||
void deactivate() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "PC Read" command to the debug tool, to extract the current program counter.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
std::uint32_t getProgramCounter() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "PC Write" command to the debug tool, setting the program counter on the target.
|
|
||||||
*
|
|
||||||
* @param programCounter
|
|
||||||
* The byte address to set as the program counter.
|
|
||||||
*/
|
|
||||||
void setProgramCounter(std::uint32_t programCounter) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "Get ID" command to the debug tool, to extract the signature from the target.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Targets::Microchip::Avr::TargetSignature getDeviceId() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "Software Breakpoint Set" command to the debug tool, setting a software breakpoint at the given
|
|
||||||
* byte address.
|
|
||||||
*
|
|
||||||
* @param address
|
|
||||||
* The byte address to position the breakpoint.
|
|
||||||
*/
|
|
||||||
void setBreakpoint(std::uint32_t address) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any breakpoint at the given
|
|
||||||
* byte address.
|
|
||||||
*
|
|
||||||
* @param address
|
|
||||||
* The byte address of the breakpoint to clear.
|
|
||||||
*/
|
|
||||||
void clearBreakpoint(std::uint32_t address) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
|
|
||||||
* that were set *in the current debug session*.
|
|
||||||
*
|
|
||||||
* If the debug session ended before any of the set breakpoints were cleared, this will *not* clear them.
|
|
||||||
*/
|
|
||||||
void clearAllBreakpoints() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads registers from the target.
|
|
||||||
*
|
|
||||||
* @param descriptors
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptors& descriptors) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes registers to target.
|
|
||||||
*
|
|
||||||
* @param registers
|
|
||||||
*/
|
|
||||||
void writeRegisters(const Targets::TargetRegisters& registers) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an overloaded method.
|
|
||||||
*
|
|
||||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory().
|
|
||||||
*
|
|
||||||
* @param memoryType
|
|
||||||
* @param startAddress
|
|
||||||
* @param bytes
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Targets::TargetMemoryBuffer readMemory(
|
|
||||||
Targets::TargetMemoryType memoryType,
|
|
||||||
std::uint32_t startAddress,
|
|
||||||
std::uint32_t bytes
|
|
||||||
) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an overloaded method.
|
|
||||||
*
|
|
||||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls writeMemory().
|
|
||||||
*
|
|
||||||
* @param memoryType
|
|
||||||
* @param startAddress
|
|
||||||
* @param buffer
|
|
||||||
*/
|
|
||||||
void writeMemory(
|
|
||||||
Targets::TargetMemoryType memoryType,
|
|
||||||
std::uint32_t startAddress,
|
|
||||||
const Targets::TargetMemoryBuffer& buffer
|
|
||||||
) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current state of the target.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Targets::TargetState getTargetState() override;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "BreakEvent.hpp"
|
#include "BreakEvent.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
{
|
{
|
||||||
class BreakEvent: public AvrEvent
|
class BreakEvent: public AvrEvent
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::uint32_t programCounter = 0;
|
|
||||||
Targets::TargetBreakCause breakCause = Targets::TargetBreakCause::UNKNOWN;
|
|
||||||
|
|
||||||
void init(const AvrEvent& event);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BreakEvent(const AvrEvent& event) {
|
explicit BreakEvent(const AvrEvent& event) {
|
||||||
this->init(event);
|
this->init(event);
|
||||||
@@ -27,5 +21,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
[[nodiscard]] Targets::TargetBreakCause getBreakCause() const {
|
[[nodiscard]] Targets::TargetBreakCause getBreakCause() const {
|
||||||
return this->breakCause;
|
return this->breakCause;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint32_t programCounter = 0;
|
||||||
|
Targets::TargetBreakCause breakCause = Targets::TargetBreakCause::UNKNOWN;
|
||||||
|
|
||||||
|
void init(const AvrEvent& event);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,27 @@ namespace Bloom::Exceptions
|
|||||||
{
|
{
|
||||||
class Avr8CommandFailure: public TargetOperationFailure
|
class Avr8CommandFailure: public TargetOperationFailure
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
explicit Avr8CommandFailure(const std::string& message): TargetOperationFailure(message) {
|
||||||
|
this->message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) {
|
||||||
|
this->message = std::string(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Avr8CommandFailure(
|
||||||
|
const std::string& message,
|
||||||
|
DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::ResponseFrames::Avr8Generic::Avr8GenericResponseFrame& responseFrame
|
||||||
|
): TargetOperationFailure(message) {
|
||||||
|
this->message = message;
|
||||||
|
|
||||||
|
auto responsePayload = responseFrame.getPayload();
|
||||||
|
if (responsePayload.size() == 3 && this->failureCodeToDescription.contains(responsePayload[2])) {
|
||||||
|
this->message += " - Failure reason: " + this->failureCodeToDescription.find(responsePayload[2])->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline auto failureCodeToDescription = std::map<unsigned char, std::string>({
|
static inline auto failureCodeToDescription = std::map<unsigned char, std::string>({
|
||||||
{0x10, "debugWIRE physical error"},
|
{0x10, "debugWIRE physical error"},
|
||||||
@@ -57,26 +78,5 @@ namespace Bloom::Exceptions
|
|||||||
{0x91, "Command has not been implemented"},
|
{0x91, "Command has not been implemented"},
|
||||||
{0xFF, "Unknown error reported by EDBG device"},
|
{0xFF, "Unknown error reported by EDBG device"},
|
||||||
});
|
});
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Avr8CommandFailure(const std::string& message): TargetOperationFailure(message) {
|
|
||||||
this->message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) {
|
|
||||||
this->message = std::string(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit Avr8CommandFailure(
|
|
||||||
const std::string& message,
|
|
||||||
DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::ResponseFrames::Avr8Generic::Avr8GenericResponseFrame& responseFrame
|
|
||||||
): TargetOperationFailure(message) {
|
|
||||||
this->message = message;
|
|
||||||
|
|
||||||
auto responsePayload = responseFrame.getPayload();
|
|
||||||
if (responsePayload.size() == 3 && this->failureCodeToDescription.contains(responsePayload[2])) {
|
|
||||||
this->message += " - Failure reason: " + this->failureCodeToDescription.find(responsePayload[2])->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Avr8GenericResponseFrame.hpp"
|
#include "Avr8GenericResponseFrame.hpp"
|
||||||
|
|
||||||
#include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp"
|
#include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp"
|
||||||
|
|
||||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::ResponseFrames::Avr8Generic
|
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||||
|
|||||||
@@ -27,5 +27,4 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::ResponseFrame
|
|||||||
return static_cast<std::uint32_t>(payload[5] << 24 | payload[4] << 16 | payload[3] << 8 | payload[2]) * 2;
|
return static_cast<std::uint32_t>(payload[5] << 24 | payload[4] << 16 | payload[3] << 8 | payload[2]) * 2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "AvrResponseFrame.hpp"
|
#include "AvrResponseFrame.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
||||||
|
|||||||
@@ -13,40 +13,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
{
|
{
|
||||||
class AvrResponseFrame
|
class AvrResponseFrame
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
unsigned char SOF = 0x0E;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Incrementing from 0x00
|
|
||||||
*/
|
|
||||||
std::uint16_t sequenceID = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destination sub-protocol handler ID
|
|
||||||
*/
|
|
||||||
ProtocolHandlerId protocolHandlerID = ProtocolHandlerId::AVR8_GENERIC;
|
|
||||||
|
|
||||||
std::vector<unsigned char> payload;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void initFromRawFrame(const std::vector<unsigned char>& rawFrame);
|
|
||||||
|
|
||||||
void setSequenceId(std::uint16_t sequenceId) {
|
|
||||||
this->sequenceID = sequenceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProtocolHandlerId(ProtocolHandlerId protocolHandlerId) {
|
|
||||||
this->protocolHandlerID = protocolHandlerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProtocolHandlerId(unsigned char protocolHandlerId) {
|
|
||||||
this->protocolHandlerID = static_cast<ProtocolHandlerId>(protocolHandlerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPayload(const std::vector<unsigned char>& payload) {
|
|
||||||
this->payload = payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AvrResponseFrame() = default;
|
explicit AvrResponseFrame() = default;
|
||||||
|
|
||||||
@@ -82,5 +48,39 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
[[nodiscard]] virtual std::vector<unsigned char> getPayloadData() {
|
[[nodiscard]] virtual std::vector<unsigned char> getPayloadData() {
|
||||||
return this->payload;
|
return this->payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void initFromRawFrame(const std::vector<unsigned char>& rawFrame);
|
||||||
|
|
||||||
|
void setSequenceId(std::uint16_t sequenceId) {
|
||||||
|
this->sequenceID = sequenceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProtocolHandlerId(ProtocolHandlerId protocolHandlerId) {
|
||||||
|
this->protocolHandlerID = protocolHandlerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProtocolHandlerId(unsigned char protocolHandlerId) {
|
||||||
|
this->protocolHandlerID = static_cast<ProtocolHandlerId>(protocolHandlerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPayload(const std::vector<unsigned char>& payload) {
|
||||||
|
this->payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char SOF = 0x0E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incrementing from 0x00
|
||||||
|
*/
|
||||||
|
std::uint16_t sequenceID = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination sub-protocol handler ID
|
||||||
|
*/
|
||||||
|
ProtocolHandlerId protocolHandlerID = ProtocolHandlerId::AVR8_GENERIC;
|
||||||
|
|
||||||
|
std::vector<unsigned char> payload;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "EdbgInterface.hpp"
|
#include "EdbgInterface.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||||
|
|||||||
@@ -39,11 +39,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg
|
|||||||
|
|
||||||
Protocols::CmsisDap::Edbg::Avr::AvrResponse getAvrResponse();
|
Protocols::CmsisDap::Edbg::Avr::AvrResponse getAvrResponse();
|
||||||
|
|
||||||
virtual std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> requestAvrResponses();
|
|
||||||
|
|
||||||
virtual std::optional<Protocols::CmsisDap::Edbg::Avr::AvrEvent> requestAvrEvent();
|
|
||||||
|
|
||||||
|
|
||||||
template<class CommandFrameType>
|
template<class CommandFrameType>
|
||||||
typename CommandFrameType::ResponseFrameType sendAvrCommandFrameAndWaitForResponseFrame(
|
typename CommandFrameType::ResponseFrameType sendAvrCommandFrameAndWaitForResponseFrame(
|
||||||
const CommandFrameType& avrCommandFrame
|
const CommandFrameType& avrCommandFrame
|
||||||
@@ -72,5 +67,10 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg
|
|||||||
responseFrame.initFromAvrResponses(responses);
|
responseFrame.initFromAvrResponses(responses);
|
||||||
return responseFrame;
|
return responseFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual std::optional<Protocols::CmsisDap::Edbg::Avr::AvrEvent> requestAvrEvent();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> requestAvrResponses();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,13 @@
|
|||||||
#include "HidInterface.hpp"
|
#include "HidInterface.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "hidapi.hpp"
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||||
|
|
||||||
using namespace Bloom::Usb;
|
using namespace Bloom::Usb;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
|
|
||||||
hid_device_info* hidDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId());
|
|
||||||
|
|
||||||
while (hidDeviceInfoList != nullptr) {
|
|
||||||
if (hidDeviceInfoList->interface_number == interfaceNumber) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hidDeviceInfoList = hidDeviceInfoList->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidDeviceInfoList == nullptr) {
|
|
||||||
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto path = std::string(hidDeviceInfoList->path);
|
|
||||||
hid_free_enumeration(hidDeviceInfoList);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HidInterface::init() {
|
void HidInterface::init() {
|
||||||
if (this->libUsbDevice == nullptr) {
|
if (this->libUsbDevice == nullptr) {
|
||||||
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
|
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
|
||||||
@@ -72,20 +49,24 @@ void HidInterface::close() {
|
|||||||
Interface::close();
|
Interface::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
|
std::vector<unsigned char> HidInterface::read(unsigned int timeout) {
|
||||||
int transferred;
|
std::vector<unsigned char> output;
|
||||||
|
auto readSize = this->getInputReportSize();
|
||||||
|
|
||||||
if ((transferred = hid_read_timeout(
|
// Attempt to read the first HID report packet, and whatever is left after that.
|
||||||
this->hidDevice,
|
output.resize(readSize);
|
||||||
buffer,
|
auto transferredByteCount = this->read(output.data(), readSize, timeout);
|
||||||
maxLength,
|
auto totalByteCount = transferredByteCount;
|
||||||
timeout == 0 ? -1 : static_cast<int>(timeout))
|
|
||||||
) == -1
|
while (transferredByteCount >= readSize) {
|
||||||
) {
|
output.resize(totalByteCount + readSize);
|
||||||
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
|
||||||
|
transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1);
|
||||||
|
totalByteCount += transferredByteCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<std::size_t>(transferred);
|
output.resize(totalByteCount);
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HidInterface::write(std::vector<unsigned char> buffer) {
|
void HidInterface::write(std::vector<unsigned char> buffer) {
|
||||||
@@ -107,27 +88,43 @@ void HidInterface::write(std::vector<unsigned char> buffer) {
|
|||||||
|
|
||||||
if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) {
|
if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) {
|
||||||
Logger::debug("Attempted to write " + std::to_string(length)
|
Logger::debug("Attempted to write " + std::to_string(length)
|
||||||
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
|
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
|
||||||
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
|
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> HidInterface::read(unsigned int timeout) {
|
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
|
||||||
std::vector<unsigned char> output;
|
int transferred;
|
||||||
auto readSize = this->getInputReportSize();
|
|
||||||
|
|
||||||
// Attempt to read the first HID report packet, and whatever is left after that.
|
if ((transferred = hid_read_timeout(
|
||||||
output.resize(readSize);
|
this->hidDevice,
|
||||||
auto transferredByteCount = this->read(output.data(), readSize, timeout);
|
buffer,
|
||||||
auto totalByteCount = transferredByteCount;
|
maxLength,
|
||||||
|
timeout == 0 ? -1 : static_cast<int>(timeout))
|
||||||
while (transferredByteCount >= readSize) {
|
) == -1
|
||||||
output.resize(totalByteCount + readSize);
|
) {
|
||||||
|
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
||||||
transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1);
|
|
||||||
totalByteCount += transferredByteCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output.resize(totalByteCount);
|
return static_cast<std::size_t>(transferred);
|
||||||
return output;
|
}
|
||||||
|
|
||||||
|
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
|
||||||
|
hid_device_info* hidDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId());
|
||||||
|
|
||||||
|
while (hidDeviceInfoList != nullptr) {
|
||||||
|
if (hidDeviceInfoList->interface_number == interfaceNumber) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hidDeviceInfoList = hidDeviceInfoList->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidDeviceInfoList == nullptr) {
|
||||||
|
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto path = std::string(hidDeviceInfoList->path);
|
||||||
|
hid_free_enumeration(hidDeviceInfoList);
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,54 @@ namespace Bloom::Usb
|
|||||||
*/
|
*/
|
||||||
class HidInterface: public Interface
|
class HidInterface: public Interface
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
std::size_t getInputReportSize() const {
|
||||||
|
return this->inputReportSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claims the USB HID interface and obtains a hid_device instance
|
||||||
|
*/
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the hid_device and releases any claimed interfaces (via hid_close())
|
||||||
|
*/
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads as much data as the device has to offer, into a vector.
|
||||||
|
*
|
||||||
|
* If `timeout` is set to 0, this method will block until at least one HID report
|
||||||
|
* packet is received.
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A vector of the data received from the device.
|
||||||
|
*/
|
||||||
|
std::vector<unsigned char> read(unsigned int timeout = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes buffer to HID output endpoint.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
*/
|
||||||
|
void write(std::vector<unsigned char> buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a device path from a USB interface number.
|
||||||
|
*
|
||||||
|
* @param interfaceNumber
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::string getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
hid_device* getHidDevice() const {
|
||||||
|
return this->hidDevice;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The HIDAPI library provides a hid_device data structure to represent a USB HID interface.
|
* The HIDAPI library provides a hid_device data structure to represent a USB HID interface.
|
||||||
@@ -58,53 +106,5 @@ namespace Bloom::Usb
|
|||||||
* Number of bytes read.
|
* Number of bytes read.
|
||||||
*/
|
*/
|
||||||
std::size_t read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout);
|
std::size_t read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout);
|
||||||
|
|
||||||
protected:
|
|
||||||
hid_device* getHidDevice() const {
|
|
||||||
return this->hidDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::size_t getInputReportSize() const {
|
|
||||||
return this->inputReportSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Claims the USB HID interface and obtains a hid_device instance
|
|
||||||
*/
|
|
||||||
void init() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the hid_device and releases any claimed interfaces (via hid_close())
|
|
||||||
*/
|
|
||||||
void close() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes buffer to HID output endpoint.
|
|
||||||
*
|
|
||||||
* @param buffer
|
|
||||||
*/
|
|
||||||
void write(std::vector<unsigned char> buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads as much data as the device has to offer, into a vector.
|
|
||||||
*
|
|
||||||
* If `timeout` is set to 0, this method will block until at least one HID report
|
|
||||||
* packet is received.
|
|
||||||
*
|
|
||||||
* @param timeout
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* A vector of the data received from the device.
|
|
||||||
*/
|
|
||||||
std::vector<unsigned char> read(unsigned int timeout = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves a device path from a USB interface number.
|
|
||||||
*
|
|
||||||
* @param interfaceNumber
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
std::string getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,6 @@ namespace Bloom::Usb
|
|||||||
{
|
{
|
||||||
class Interface
|
class Interface
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
libusb_device* libUsbDevice = nullptr;
|
|
||||||
libusb_device_handle* libUsbDeviceHandle = nullptr;
|
|
||||||
|
|
||||||
std::uint16_t vendorId = 0;
|
|
||||||
std::uint16_t productId = 0;
|
|
||||||
|
|
||||||
std::uint8_t number = 0;
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
bool initialised = false;
|
|
||||||
bool claimed = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Interface(const std::uint8_t& interfaceNumber = 0) {
|
explicit Interface(const std::uint8_t& interfaceNumber = 0) {
|
||||||
this->setNumber(interfaceNumber);
|
this->setNumber(interfaceNumber);
|
||||||
@@ -112,5 +99,18 @@ namespace Bloom::Usb
|
|||||||
*/
|
*/
|
||||||
virtual int read(unsigned char* buffer, unsigned char endPoint, std::size_t length, std::size_t timeout);
|
virtual int read(unsigned char* buffer, unsigned char endPoint, std::size_t length, std::size_t timeout);
|
||||||
virtual void write(unsigned char* buffer, unsigned char endPoint, int length);
|
virtual void write(unsigned char* buffer, unsigned char endPoint, int length);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
libusb_device* libUsbDevice = nullptr;
|
||||||
|
libusb_device_handle* libUsbDeviceHandle = nullptr;
|
||||||
|
|
||||||
|
std::uint16_t vendorId = 0;
|
||||||
|
std::uint16_t productId = 0;
|
||||||
|
|
||||||
|
std::uint8_t number = 0;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
bool initialised = false;
|
||||||
|
bool claimed = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,61 @@
|
|||||||
#include "UsbDevice.hpp"
|
#include "UsbDevice.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <libusb-1.0/libusb.h>
|
|
||||||
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||||
|
|
||||||
using Bloom::Usb::UsbDevice;
|
using Bloom::Usb::UsbDevice;
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
|
void UsbDevice::init() {
|
||||||
|
libusb_init(&this->libUsbContext);
|
||||||
|
// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
|
||||||
|
auto devices = this->findMatchingDevices();
|
||||||
|
|
||||||
|
if (devices.empty()) {
|
||||||
|
throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID.");
|
||||||
|
|
||||||
|
} else if (devices.size() > 1) {
|
||||||
|
// TODO: implement support for multiple devices (maybe via serial number?)
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Numerous devices of matching vendor & product ID found.\n"
|
||||||
|
"Please ensure that only one debug tool is connected and then try again."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, just use the first device found.
|
||||||
|
auto device = devices.front();
|
||||||
|
this->setLibUsbDevice(device);
|
||||||
|
|
||||||
|
int libUsbStatusCode;
|
||||||
|
|
||||||
|
// Obtain a device handle from libusb
|
||||||
|
if ((libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle)) < 0) {
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDevice::setConfiguration(int configIndex) {
|
||||||
|
libusb_config_descriptor* configDescriptor = {};
|
||||||
|
int libUsbStatusCode;
|
||||||
|
|
||||||
|
if ((libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor))) {
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode)
|
||||||
|
+ " returned."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue))) {
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_config_descriptor(configDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<libusb_device*> UsbDevice::findMatchingDevices(
|
std::vector<libusb_device*> UsbDevice::findMatchingDevices(
|
||||||
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
|
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
|
||||||
) {
|
) {
|
||||||
@@ -45,36 +92,6 @@ std::vector<libusb_device*> UsbDevice::findMatchingDevices(
|
|||||||
return matchedDevices;
|
return matchedDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UsbDevice::init() {
|
|
||||||
libusb_init(&this->libUsbContext);
|
|
||||||
// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
|
|
||||||
auto devices = this->findMatchingDevices();
|
|
||||||
|
|
||||||
if (devices.empty()) {
|
|
||||||
throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID.");
|
|
||||||
|
|
||||||
} else if (devices.size() > 1) {
|
|
||||||
// TODO: implement support for multiple devices (maybe via serial number?)
|
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Numerous devices of matching vendor & product ID found.\n"
|
|
||||||
"Please ensure that only one debug tool is connected and then try again."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, just use the first device found.
|
|
||||||
auto device = devices.front();
|
|
||||||
this->setLibUsbDevice(device);
|
|
||||||
|
|
||||||
int libUsbStatusCode;
|
|
||||||
|
|
||||||
// Obtain a device handle from libusb
|
|
||||||
if ((libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle)) < 0) {
|
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UsbDevice::close() {
|
void UsbDevice::close() {
|
||||||
if (this->libUsbDeviceHandle != nullptr) {
|
if (this->libUsbDeviceHandle != nullptr) {
|
||||||
libusb_close(this->libUsbDeviceHandle);
|
libusb_close(this->libUsbDeviceHandle);
|
||||||
@@ -85,23 +102,3 @@ void UsbDevice::close() {
|
|||||||
libusb_exit(this->libUsbContext);
|
libusb_exit(this->libUsbContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UsbDevice::setConfiguration(int configIndex) {
|
|
||||||
libusb_config_descriptor* configDescriptor = {};
|
|
||||||
int libUsbStatusCode;
|
|
||||||
|
|
||||||
if ((libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor))) {
|
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode)
|
|
||||||
+ " returned."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue))) {
|
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_free_config_descriptor(configDescriptor);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,25 +12,8 @@ namespace Bloom::Usb
|
|||||||
{
|
{
|
||||||
class UsbDevice
|
class UsbDevice
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
libusb_context* libUsbContext = nullptr;
|
|
||||||
libusb_device* libUsbDevice = nullptr;
|
|
||||||
libusb_device_handle* libUsbDeviceHandle = nullptr;
|
|
||||||
std::uint16_t vendorId;
|
|
||||||
std::uint16_t productId;
|
|
||||||
|
|
||||||
std::vector<libusb_device*> findMatchingDevices(
|
|
||||||
std::optional<std::uint16_t> vendorId = std::nullopt, std::optional<std::uint16_t> productId = std::nullopt
|
|
||||||
);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UsbDevice(std::uint16_t vendorId, std::uint16_t productId) {
|
UsbDevice(std::uint16_t vendorId, std::uint16_t productId): vendorId(vendorId), productId(productId) {};
|
||||||
this->vendorId = vendorId;
|
|
||||||
this->productId = productId;
|
|
||||||
};
|
|
||||||
|
|
||||||
~UsbDevice() = default;
|
~UsbDevice() = default;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
@@ -57,5 +40,18 @@ namespace Bloom::Usb
|
|||||||
* @param configIndex
|
* @param configIndex
|
||||||
*/
|
*/
|
||||||
virtual void setConfiguration(int configIndex);
|
virtual void setConfiguration(int configIndex);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
libusb_context* libUsbContext = nullptr;
|
||||||
|
libusb_device* libUsbDevice = nullptr;
|
||||||
|
libusb_device_handle* libUsbDeviceHandle = nullptr;
|
||||||
|
std::uint16_t vendorId;
|
||||||
|
std::uint16_t productId;
|
||||||
|
|
||||||
|
std::vector<libusb_device*> findMatchingDevices(
|
||||||
|
std::optional<std::uint16_t> vendorId = std::nullopt, std::optional<std::uint16_t> productId = std::nullopt
|
||||||
|
);
|
||||||
|
|
||||||
|
void close();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "EventListener.hpp"
|
#include "EventListener.hpp"
|
||||||
|
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
using namespace Bloom;
|
using namespace Bloom;
|
||||||
using namespace Bloom::Events;
|
using namespace Bloom::Events;
|
||||||
|
|
||||||
@@ -7,11 +9,6 @@ std::set<Events::EventType> EventListener::getRegisteredEventTypes() {
|
|||||||
return this->registeredEventTypes.getValue();
|
return this->registeredEventTypes.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventListener::clearAllCallbacks() {
|
|
||||||
auto lock = this->eventTypeToCallbacksMapping.acquireLock();
|
|
||||||
this->eventTypeToCallbacksMapping.getReference().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventListener::registerEvent(SharedGenericEventPointer event) {
|
void EventListener::registerEvent(SharedGenericEventPointer event) {
|
||||||
Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id)
|
Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id)
|
||||||
+ ") registered for listener " + this->name);
|
+ ") registered for listener " + this->name);
|
||||||
@@ -26,45 +23,6 @@ void EventListener::registerEvent(SharedGenericEventPointer event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
|
|
||||||
auto queueLock = this->eventQueueByEventType.acquireLock();
|
|
||||||
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
|
||||||
std::vector<SharedGenericEventPointer> output;
|
|
||||||
|
|
||||||
for (auto& eventQueue: eventQueueByType) {
|
|
||||||
if (!eventQueue.second.empty()) {
|
|
||||||
output.push_back(std::move(eventQueue.second.front()));
|
|
||||||
eventQueue.second.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(output.begin(), output.end(), [](const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
|
|
||||||
return a->id < b->id;
|
|
||||||
});
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
|
|
||||||
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
|
|
||||||
// Dispatch the event to all registered handlers
|
|
||||||
auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock();
|
|
||||||
auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second;
|
|
||||||
mappingLock.unlock();
|
|
||||||
|
|
||||||
for (auto& callback : callbacks) {
|
|
||||||
callback(*(event.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventListener::dispatchCurrentEvents() {
|
|
||||||
auto events = this->getEvents();
|
|
||||||
|
|
||||||
for (auto const& event: events) {
|
|
||||||
dispatchEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventListener::waitAndDispatch(int msTimeout) {
|
void EventListener::waitAndDispatch(int msTimeout) {
|
||||||
auto queueLock = this->eventQueueByEventType.acquireLock();
|
auto queueLock = this->eventQueueByEventType.acquireLock();
|
||||||
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
||||||
@@ -95,3 +53,47 @@ void EventListener::waitAndDispatch(int msTimeout) {
|
|||||||
|
|
||||||
this->dispatchCurrentEvents();
|
this->dispatchCurrentEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
|
||||||
|
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
|
||||||
|
// Dispatch the event to all registered handlers
|
||||||
|
auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock();
|
||||||
|
auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second;
|
||||||
|
mappingLock.unlock();
|
||||||
|
|
||||||
|
for (auto& callback : callbacks) {
|
||||||
|
callback(*(event.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventListener::dispatchCurrentEvents() {
|
||||||
|
auto events = this->getEvents();
|
||||||
|
|
||||||
|
for (auto const& event: events) {
|
||||||
|
dispatchEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
|
||||||
|
auto queueLock = this->eventQueueByEventType.acquireLock();
|
||||||
|
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
||||||
|
std::vector<SharedGenericEventPointer> output;
|
||||||
|
|
||||||
|
for (auto& eventQueue: eventQueueByType) {
|
||||||
|
if (!eventQueue.second.empty()) {
|
||||||
|
output.push_back(std::move(eventQueue.second.front()));
|
||||||
|
eventQueue.second.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(output.begin(), output.end(), [](const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
|
||||||
|
return a->id < b->id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventListener::clearAllCallbacks() {
|
||||||
|
auto lock = this->eventTypeToCallbacksMapping.acquireLock();
|
||||||
|
this->eventTypeToCallbacksMapping.getReference().clear();
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "src/EventManager/Events/Events.hpp"
|
#include "src/EventManager/Events/Events.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
|
||||||
#include "src/Helpers/SyncSafe.hpp"
|
#include "src/Helpers/SyncSafe.hpp"
|
||||||
#include "src/Helpers/EventNotifier.hpp"
|
#include "src/Helpers/EventNotifier.hpp"
|
||||||
|
|
||||||
@@ -42,41 +41,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class EventListener
|
class EventListener
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Human readable name for event listeners.
|
|
||||||
*
|
|
||||||
* TODO: This was useful during development, but may no longer be needed.
|
|
||||||
*/
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
static inline std::atomic<std::size_t> lastId = 0;
|
|
||||||
std::size_t id = ++(EventListener::lastId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds all events registered to this listener.
|
|
||||||
*
|
|
||||||
* Events are grouped by event type, and removed from their queue just *before* the dispatching to
|
|
||||||
* registered handlers begins.
|
|
||||||
*/
|
|
||||||
SyncSafe<std::map<Events::EventType, std::queue<Events::SharedGenericEventPointer>>> eventQueueByEventType;
|
|
||||||
std::condition_variable eventQueueByEventTypeCV;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mapping of event types to a vector of callback functions. Events will be dispatched to these
|
|
||||||
* callback functions, during a call to EventListener::dispatchEvent().
|
|
||||||
*
|
|
||||||
* Each callback will be passed a reference to the event (we wrap all registered callbacks in a lambda, where
|
|
||||||
* we perform a downcast before invoking the callback. See EventListener::registerCallbackForEventType()
|
|
||||||
* for more)
|
|
||||||
*/
|
|
||||||
SyncSafe<std::map<Events::EventType, std::vector<std::function<void(const Events::Event&)>>>> eventTypeToCallbacksMapping;
|
|
||||||
SyncSafe<std::set<Events::EventType>> registeredEventTypes;
|
|
||||||
|
|
||||||
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
|
||||||
|
|
||||||
std::vector<Events::SharedGenericEventPointer> getEvents();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EventListener(std::string name): name(std::move(name)) {};
|
explicit EventListener(std::string name): name(std::move(name)) {};
|
||||||
|
|
||||||
@@ -370,6 +334,41 @@ namespace Bloom
|
|||||||
* Removes all callbacks registered for the event listener.
|
* Removes all callbacks registered for the event listener.
|
||||||
*/
|
*/
|
||||||
void clearAllCallbacks();
|
void clearAllCallbacks();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Human readable name for event listeners.
|
||||||
|
*
|
||||||
|
* TODO: This was useful during development, but may no longer be needed.
|
||||||
|
*/
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
static inline std::atomic<std::size_t> lastId = 0;
|
||||||
|
std::size_t id = ++(EventListener::lastId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds all events registered to this listener.
|
||||||
|
*
|
||||||
|
* Events are grouped by event type, and removed from their queue just *before* the dispatching to
|
||||||
|
* registered handlers begins.
|
||||||
|
*/
|
||||||
|
SyncSafe<std::map<Events::EventType, std::queue<Events::SharedGenericEventPointer>>> eventQueueByEventType;
|
||||||
|
std::condition_variable eventQueueByEventTypeCV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping of event types to a vector of callback functions. Events will be dispatched to these
|
||||||
|
* callback functions, during a call to EventListener::dispatchEvent().
|
||||||
|
*
|
||||||
|
* Each callback will be passed a reference to the event (we wrap all registered callbacks in a lambda, where
|
||||||
|
* we perform a downcast before invoking the callback. See EventListener::registerCallbackForEventType()
|
||||||
|
* for more)
|
||||||
|
*/
|
||||||
|
SyncSafe<std::map<Events::EventType, std::vector<std::function<void(const Events::Event&)>>>> eventTypeToCallbacksMapping;
|
||||||
|
SyncSafe<std::set<Events::EventType>> registeredEventTypes;
|
||||||
|
|
||||||
|
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
||||||
|
|
||||||
|
std::vector<Events::SharedGenericEventPointer> getEvents();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,13 +19,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class EventManager
|
class EventManager
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* A mapping of listener IDs to registered listeners. Each registered listener is given an interger ID.
|
|
||||||
*/
|
|
||||||
std::map<size_t, std::shared_ptr<EventListener>> registeredListeners;
|
|
||||||
std::mutex registerListenerMutex;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Registers an EventListener instance with this manager.
|
* Registers an EventListener instance with this manager.
|
||||||
@@ -62,5 +55,12 @@ namespace Bloom
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
bool isEventTypeListenedFor(Events::EventType eventType);
|
bool isEventTypeListenedFor(Events::EventType eventType);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* A mapping of listener IDs to registered listeners. Each registered listener is given an interger ID.
|
||||||
|
*/
|
||||||
|
std::map<size_t, std::shared_ptr<EventListener>> registeredListeners;
|
||||||
|
std::mutex registerListenerMutex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ namespace Bloom::Events
|
|||||||
{
|
{
|
||||||
class DebugServerThreadStateChanged: public Event
|
class DebugServerThreadStateChanged: public Event
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
ThreadState state;
|
|
||||||
public:
|
public:
|
||||||
static inline EventType type = EventType::DEBUG_SERVER_THREAD_STATE_CHANGED;
|
static inline EventType type = EventType::DEBUG_SERVER_THREAD_STATE_CHANGED;
|
||||||
static inline const std::string name = "DebugServerThreadStateChanged";
|
static inline const std::string name = "DebugServerThreadStateChanged";
|
||||||
@@ -28,5 +26,8 @@ namespace Bloom::Events
|
|||||||
[[nodiscard]] ThreadState getState() const {
|
[[nodiscard]] ThreadState getState() const {
|
||||||
return this->state;
|
return this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThreadState state;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,6 @@ namespace Bloom::Events
|
|||||||
|
|
||||||
class Event
|
class Event
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
static inline std::atomic<int> lastEventId = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int id = ++(Event::lastEventId);
|
int id = ++(Event::lastEventId);
|
||||||
QDateTime createdTimestamp = DateTime::currentDateTime();
|
QDateTime createdTimestamp = DateTime::currentDateTime();
|
||||||
@@ -73,5 +70,8 @@ namespace Bloom::Events
|
|||||||
[[nodiscard]] virtual EventType getType() const {
|
[[nodiscard]] virtual EventType getType() const {
|
||||||
return Event::type;
|
return Event::type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline std::atomic<int> lastEventId = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ namespace Bloom::Events
|
|||||||
{
|
{
|
||||||
class InsightThreadStateChanged: public Event
|
class InsightThreadStateChanged: public Event
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
ThreadState state;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InsightThreadStateChanged(ThreadState state): state(state) {};
|
explicit InsightThreadStateChanged(ThreadState state): state(state) {};
|
||||||
|
|
||||||
@@ -29,5 +26,8 @@ namespace Bloom::Events
|
|||||||
ThreadState getState() const {
|
ThreadState getState() const {
|
||||||
return this->state;
|
return this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThreadState state;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ namespace Bloom::Events
|
|||||||
{
|
{
|
||||||
class TargetControllerThreadStateChanged: public Event
|
class TargetControllerThreadStateChanged: public Event
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
ThreadState state;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static inline EventType type = EventType::TARGET_CONTROLLER_THREAD_STATE_CHANGED;
|
static inline EventType type = EventType::TARGET_CONTROLLER_THREAD_STATE_CHANGED;
|
||||||
static inline const std::string name = "TargetControllerThreadStateChanged";
|
static inline const std::string name = "TargetControllerThreadStateChanged";
|
||||||
@@ -29,5 +26,8 @@ namespace Bloom::Events
|
|||||||
ThreadState getState() const {
|
ThreadState getState() const {
|
||||||
return this->state;
|
return this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThreadState state;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Bloom::Exceptions
|
|||||||
{
|
{
|
||||||
class Exception: public std::runtime_error
|
class Exception: public std::runtime_error
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
std::string message;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Exception(): std::runtime_error("") {}
|
explicit Exception(): std::runtime_error("") {}
|
||||||
|
|
||||||
@@ -27,5 +24,8 @@ namespace Bloom::Exceptions
|
|||||||
std::string getMessage() const {
|
std::string getMessage() const {
|
||||||
return this->message;
|
return this->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string message;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ namespace Bloom
|
|||||||
template<typename TypeA, typename TypeB>
|
template<typename TypeA, typename TypeB>
|
||||||
class BiMap
|
class BiMap
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::unordered_map<TypeA, TypeB> map = {};
|
|
||||||
std::unordered_map<TypeB, typename std::unordered_map<TypeA, TypeB>::iterator> flippedMap = {};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BiMap(std::initializer_list<std::pair<TypeA, TypeB>> elements) {
|
BiMap(std::initializer_list<std::pair<TypeA, TypeB>> elements) {
|
||||||
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
||||||
@@ -75,5 +71,9 @@ namespace Bloom
|
|||||||
insertResultPair.first
|
insertResultPair.first
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<TypeA, TypeB> map = {};
|
||||||
|
std::unordered_map<TypeB, typename std::unordered_map<TypeA, TypeB>::iterator> flippedMap = {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class DateTime
|
class DateTime
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
static inline std::mutex currentDateTimeMutex;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The QDateTime::currentDateTime() static function is not thread-safe. This may be caused by the
|
* The QDateTime::currentDateTime() static function is not thread-safe. This may be caused by the
|
||||||
@@ -38,5 +35,8 @@ namespace Bloom
|
|||||||
auto lock = std::unique_lock(DateTime::currentDateTimeMutex);
|
auto lock = std::unique_lock(DateTime::currentDateTimeMutex);
|
||||||
return dateTime.timeZoneAbbreviation();
|
return dateTime.timeZoneAbbreviation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline std::mutex currentDateTimeMutex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,26 +25,6 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class EventNotifier
|
class EventNotifier
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
int fileDescriptor = -1;
|
|
||||||
std::atomic<bool> initialised = false;
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
this->fileDescriptor = ::eventfd(0, EFD_NONBLOCK);
|
|
||||||
|
|
||||||
if (this->fileDescriptor < -1) {
|
|
||||||
throw Exceptions::Exception("Failed to create new eventfd object - error number: "
|
|
||||||
+ std::to_string(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
this->initialised = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
::close(this->fileDescriptor);
|
|
||||||
this->initialised = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EventNotifier() {
|
EventNotifier() {
|
||||||
this->init();
|
this->init();
|
||||||
@@ -78,5 +58,25 @@ namespace Bloom
|
|||||||
"error number: " + std::to_string(errno));
|
"error number: " + std::to_string(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fileDescriptor = -1;
|
||||||
|
std::atomic<bool> initialised = false;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
this->fileDescriptor = ::eventfd(0, EFD_NONBLOCK);
|
||||||
|
|
||||||
|
if (this->fileDescriptor < -1) {
|
||||||
|
throw Exceptions::Exception("Failed to create new eventfd object - error number: "
|
||||||
|
+ std::to_string(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
::close(this->fileDescriptor);
|
||||||
|
this->initialised = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ namespace Bloom
|
|||||||
template<typename Type>
|
template<typename Type>
|
||||||
class SyncSafe
|
class SyncSafe
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::mutex mutex;
|
|
||||||
Type value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SyncSafe() = default;
|
SyncSafe() = default;
|
||||||
|
|
||||||
@@ -51,5 +47,9 @@ namespace Bloom
|
|||||||
std::unique_lock<std::mutex> acquireLock() {
|
std::unique_lock<std::mutex> acquireLock() {
|
||||||
return std::unique_lock(this->mutex);
|
return std::unique_lock(this->mutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex;
|
||||||
|
Type value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ namespace Bloom
|
|||||||
|
|
||||||
class Thread
|
class Thread
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
SyncSafe<ThreadState> state = SyncSafe<ThreadState>(ThreadState::UNINITIALISED);
|
virtual ThreadState getThreadState() {
|
||||||
|
return this->state.getValue();
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setThreadState(ThreadState state) {
|
virtual void setThreadState(ThreadState state) {
|
||||||
@@ -42,9 +44,7 @@ namespace Bloom
|
|||||||
pthread_setname_np(pthread_self(), name.c_str());
|
pthread_setname_np(pthread_self(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
private:
|
||||||
virtual ThreadState getThreadState() {
|
SyncSafe<ThreadState> state = SyncSafe<ThreadState>(ThreadState::UNINITIALISED);
|
||||||
return this->state.getValue();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "InsightWorker.hpp"
|
#include "InsightWorker.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -12,6 +11,16 @@ using namespace Bloom::Exceptions;
|
|||||||
|
|
||||||
using Bloom::Targets::TargetState;
|
using Bloom::Targets::TargetState;
|
||||||
|
|
||||||
|
InsightWorker::InsightWorker(EventManager& eventManager): eventManager(eventManager) {}
|
||||||
|
|
||||||
|
void InsightWorker::queueTask(InsightWorkerTask* task) {
|
||||||
|
auto taskQueueLock = this->queuedTasks.acquireLock();
|
||||||
|
task->moveToThread(this->thread());
|
||||||
|
task->setParent(this);
|
||||||
|
this->queuedTasks.getReference().push(task);
|
||||||
|
emit this->taskQueued();
|
||||||
|
}
|
||||||
|
|
||||||
void InsightWorker::startup() {
|
void InsightWorker::startup() {
|
||||||
Logger::debug("Starting InsightWorker thread");
|
Logger::debug("Starting InsightWorker thread");
|
||||||
this->eventManager.registerListener(this->eventListener);
|
this->eventManager.registerListener(this->eventListener);
|
||||||
@@ -43,12 +52,8 @@ void InsightWorker::startup() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsightWorker::queueTask(InsightWorkerTask* task) {
|
void InsightWorker::requestPinStates(int variantId) {
|
||||||
auto taskQueueLock = this->queuedTasks.acquireLock();
|
this->targetControllerConsole.requestPinStates(variantId);
|
||||||
task->moveToThread(this->thread());
|
|
||||||
task->setParent(this);
|
|
||||||
this->queuedTasks.getReference().push(task);
|
|
||||||
emit this->taskQueued();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
|
std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
|
||||||
@@ -65,18 +70,6 @@ std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
|
|||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsightWorker::executeTasks() {
|
|
||||||
auto task = std::optional<InsightWorkerTask*>();
|
|
||||||
|
|
||||||
while ((task = this->getQueuedTask()).has_value()) {
|
|
||||||
task.value()->execute(this->targetControllerConsole);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InsightWorker::requestPinStates(int variantId) {
|
|
||||||
this->targetControllerConsole.requestPinStates(variantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) {
|
void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) {
|
||||||
/*
|
/*
|
||||||
* When we report a target halt to Insight, Insight will immediately seek more data from the target (such as GPIO
|
* When we report a target halt to Insight, Insight will immediately seek more data from the target (such as GPIO
|
||||||
@@ -135,3 +128,11 @@ void InsightWorker::onTargetControllerStateReportedEvent(const Events::TargetCon
|
|||||||
}
|
}
|
||||||
this->lastTargetControllerState = event.state;
|
this->lastTargetControllerState = event.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InsightWorker::executeTasks() {
|
||||||
|
auto task = std::optional<InsightWorkerTask*>();
|
||||||
|
|
||||||
|
while ((task = this->getQueuedTask()).has_value()) {
|
||||||
|
task.value()->execute(this->targetControllerConsole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,29 @@ namespace Bloom
|
|||||||
*/
|
*/
|
||||||
class InsightWorker: public QObject
|
class InsightWorker: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit InsightWorker(EventManager& eventManager);
|
||||||
|
|
||||||
|
void queueTask(InsightWorkerTask* task);
|
||||||
|
|
||||||
|
void dispatchEvents() {
|
||||||
|
this->eventListener->dispatchCurrentEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startup();
|
||||||
|
void requestPinStates(int variantId);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void taskQueued();
|
||||||
|
void targetStateUpdated(Bloom::Targets::TargetState newState);
|
||||||
|
void targetProgramCounterUpdated(quint32 programCounter);
|
||||||
|
void targetControllerSuspended();
|
||||||
|
void targetControllerResumed(const Bloom::Targets::TargetDescriptor& targetDescriptor);
|
||||||
|
void targetRegistersWritten(const Bloom::Targets::TargetRegisters& targetRegisters, const QDateTime& timestamp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventManager& eventManager;
|
EventManager& eventManager;
|
||||||
EventListenerPointer eventListener = std::make_shared<EventListener>("InsightWorkerEventListener");
|
EventListenerPointer eventListener = std::make_shared<EventListener>("InsightWorkerEventListener");
|
||||||
@@ -44,26 +66,5 @@ namespace Bloom
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void executeTasks();
|
void executeTasks();
|
||||||
|
|
||||||
public:
|
|
||||||
explicit InsightWorker(EventManager& eventManager): eventManager(eventManager) {};
|
|
||||||
|
|
||||||
void dispatchEvents() {
|
|
||||||
this->eventListener->dispatchCurrentEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
void queueTask(InsightWorkerTask* task);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void startup();
|
|
||||||
void requestPinStates(int variantId);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void taskQueued();
|
|
||||||
void targetStateUpdated(Bloom::Targets::TargetState newState);
|
|
||||||
void targetProgramCounterUpdated(quint32 programCounter);
|
|
||||||
void targetControllerSuspended();
|
|
||||||
void targetControllerResumed(const Bloom::Targets::TargetDescriptor& targetDescriptor);
|
|
||||||
void targetRegistersWritten(const Bloom::Targets::TargetRegisters& targetRegisters, const QDateTime& timestamp);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "InsightWorkerTask.hpp"
|
#include "InsightWorkerTask.hpp"
|
||||||
|
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
using namespace Bloom;
|
using namespace Bloom;
|
||||||
|
|
||||||
void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) {
|
void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user