Start working on remote control server

This commit is contained in:
Joris van Rantwijk 2024-09-27 19:30:52 +02:00
parent 66050aca5b
commit ea5d3c3a1d
7 changed files with 1146 additions and 87 deletions

1
sw/.gitignore vendored
View File

@ -1,5 +1,6 @@
devicetree/devicetree.dtb
buildroot_overlay/opt/puzzlefw/bin/puzzlecmd
buildroot_overlay/opt/puzzlefw/bin/remotectl
buildroot_overlay/opt/puzzlefw/driver/
downloads/
buildroot-2023.02.8/

View File

@ -8,17 +8,19 @@ CFLAGS = -Wall -O2
CXXFLAGS = -std=c++17 -Wall -Wno-psabi -O2
.PHONY: all
all: puzzlecmd
all: puzzlecmd remotectl
puzzlecmd: puzzlecmd.o puzzlefw.o
$(CXX) -o $@ $(LDFLAGS) $^
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
remotectl: remotectl.o puzzlefw.o
$(CXX) -o $@ $(LDFLAGS) $^
testje: testje.c
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
remotectl.o: remotectl.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp version.hpp
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
.PHONY: clean
clean:
$(RM) -f -- puzzlecmd testje *.o
$(RM) -f -- puzzlecmd remotectl *.o

View File

@ -13,6 +13,7 @@
#include <stdint.h>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <system_error>
#include <thread>
@ -82,11 +83,17 @@ public:
m_dma_stream.disable_interrupt();
}
/** Return the Asio strand that runs all handlers for this object. */
asio::strand<asio::io_context::executor_type> get_executor()
{
return m_strand;
}
/**
* Start the server.
*
* This method must not be called directly while a multi-threaded Asio
* event loop is running. In that case, use "async_start_server()".
* If a multi-threaded Asio event loop is running, this method may only
* be called through the strand returned by "get_executor()".
*/
void start_server()
{
@ -113,7 +120,7 @@ public:
// Asynchronously accept connections.
m_acceptor.async_accept(
[this](auto& ec, auto s){ handle_accept(ec, std::move(s)); });
[this](auto ec, auto s){ handle_accept(ec, std::move(s)); });
log(LOG_INFO, "Ready for TCP connections to port %d", m_tcp_port);
}
@ -121,8 +128,8 @@ public:
/**
* Stop the server and close current connections.
*
* This method must not be called directly while a multi-threaded Asio
* event loop is running. In that case, use "async_stop_server()".
* If a multi-threaded Asio event loop is running, this method may only
* be called through the strand returned by "get_executor()".
*/
void stop_server()
{
@ -144,46 +151,6 @@ public:
}
}
/**
* Submit a call to "start_server()" to be executed in the strand
* of this server instance.
*
* After starting the server, the specified completion handler will
* be submitted for execution.
*
* This method may safely be called from any thread.
*/
template <typename Handler>
void async_start_server(Handler&& handler)
{
asio::post(
m_strand,
[this, handler] () mutable {
start_server();
asio::post(m_strand, handler);
});
}
/**
* Submit a call to "stop_server()" to be executed in the strand
* of this server instance.
*
* After stopping the server, the specified completion handler will
* be submitted for execution.
*
* This method may safely be called from any thread.
*/
template <typename Handler>
void async_stop_server(Handler&& handler)
{
asio::post(
m_strand,
[this, handler] () mutable {
stop_server();
asio::post(m_strand, handler);
});
}
/**
* Called when an FPGA interrupt occurs.
*
@ -206,7 +173,7 @@ private:
/**
* Re-initialize DMA stream and discard stale data from the DMA buffer.
*
* Do not call this while an async_send() operation is in progress.
* Do not call this while an async_write() operation is in progress.
*/
void discard_stale_data()
{
@ -228,7 +195,7 @@ private:
}
/** Accept completion handler. */
void handle_accept(const boost::system::error_code& error,
void handle_accept(boost::system::error_code error,
asio::ip::tcp::socket conn)
{
if (error) {
@ -256,7 +223,7 @@ private:
// Retry accept call.
if (m_acceptor.is_open()) {
m_acceptor.async_accept(
[this](auto& ec, auto s) {
[this](auto ec, auto s) {
handle_accept(ec, std::move(s));
});
}
@ -281,7 +248,7 @@ private:
if (m_acceptor.is_open()) {
// Continue accepting connections.
m_acceptor.async_accept(
[this](auto& ec, auto s){ handle_accept(ec, std::move(s)); });
[this](auto ec, auto s){ handle_accept(ec, std::move(s)); });
}
if (m_connection.is_open()) {
@ -302,7 +269,7 @@ private:
// closed remotely (or client writes unexpected data).
m_connection.async_receive(
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
[this](auto& ec, size_t n){ handle_receive(ec, n); });
[this](auto ec, size_t n){ handle_receive(ec, n); });
}
// Clear buffer, then enable DMA stream.
@ -314,7 +281,7 @@ private:
}
/** Receive completion handler. */
void handle_receive(const boost::system::error_code& error, size_t len)
void handle_receive(boost::system::error_code error, size_t len)
{
if (m_stale_receive) {
// This completion refers to an old, already closed connection.
@ -324,7 +291,7 @@ private:
// Initiate async receive for the new connection.
m_connection.async_receive(
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
[this](auto& ec, size_t n){ handle_receive(ec, n); });
[this](auto ec, size_t n){ handle_receive(ec, n); });
}
return;
@ -359,10 +326,9 @@ private:
}
/** Send completion handler. */
void handle_send(const boost::system::error_code& error, size_t len)
void handle_send(boost::system::error_code error, size_t len)
{
assert(m_send_in_progress);
assert(m_send_size > 0);
m_send_in_progress = false;
@ -394,21 +360,13 @@ private:
return;
}
if (len < m_send_buffer.size()) {
// Partially completed. Send the rest.
m_send_buffer += len;
m_send_in_progress = true;
m_connection.async_send(
m_send_buffer,
[this](auto& ec, size_t n){ handle_send(ec, n); });
} else {
// Fully completed.
// Release the completed part of the DMA buffer.
m_dma_stream.consume_data(m_send_size);
// Send operation completed.
// Release the completed part of the DMA buffer.
assert(len == m_send_buffer.size());
m_dma_stream.consume_data(m_send_buffer.size());
// Try to send more data.
transmit_data(false);
}
// Try to send more data.
transmit_data(false);
}
/**
@ -447,7 +405,7 @@ private:
// If timeout occurs, we will send whatever data is available.
m_timer.expires_after(WAIT_TIMEOUT);
m_timer.async_wait(
[this](auto& ec){ handle_timer(ec); });
[this](auto ec){ handle_timer(ec); });
// Done. We will be notified for interrupt or timeout.
return;
@ -467,16 +425,15 @@ private:
// Initiate async send.
// Limit the block size so we can release that part of the buffer
// as soon as it completes.
m_send_size = std::min(data_available, SEND_MAX_BLOCK);
m_send_buffer = asio::buffer(data, m_send_size);
size_t block_size = std::min(data_available, SEND_MAX_BLOCK);
m_send_buffer = asio::buffer(data, block_size);
m_send_in_progress = true;
m_connection.async_send(
m_send_buffer,
[this](auto& ec, size_t n){ handle_send(ec, n); });
asio::async_write(m_connection, m_send_buffer,
[this](auto ec, size_t n){ handle_send(ec, n); });
}
/** Timeout handler. */
void handle_timer(const boost::system::error_code& error)
void handle_timer(boost::system::error_code error)
{
if (error) {
// Ignore error due to cancellation.
@ -520,7 +477,6 @@ private:
bool m_stale_receive;
bool m_stale_send;
bool m_send_in_progress;
size_t m_send_size;
asio::const_buffer m_send_buffer;
};
@ -541,7 +497,7 @@ public:
, m_interval(interval)
{
m_timer.expires_after(m_interval);
m_timer.async_wait([this](auto& ec){ handle_timer(ec); });
m_timer.async_wait([this](auto ec){ handle_timer(ec); });
}
// Delete copy constructor and assignment operator.
@ -567,7 +523,7 @@ public:
private:
/** Timeout handler. */
void handle_timer(const boost::system::error_code& error)
void handle_timer(boost::system::error_code error)
{
if (error) {
// Ignore error due to cancellation.
@ -580,7 +536,7 @@ private:
}
m_timer.expires_after(m_interval);
m_timer.async_wait([this](auto& ec){ handle_timer(ec); });
m_timer.async_wait([this](auto ec){ handle_timer(ec); });
check_dma_error();
}

View File

@ -74,7 +74,7 @@ public:
private:
/** Called when an FPGA interrupt occurs. */
void handle_wait(const boost::system::error_code& error)
void handle_wait(boost::system::error_code error)
{
if (error) {
// Ignore error due to cancellation.
@ -100,7 +100,7 @@ private:
// Wait for next interrupt.
m_uio_fd.async_wait(asio::posix::descriptor_base::wait_read,
[this](auto& ec){ handle_wait(ec); });
[this](auto ec){ handle_wait(ec); });
}
PuzzleFwDevice& m_device;

View File

@ -2,6 +2,8 @@
* puzzlecmd.cpp
*
* Command-line program to test PuzzleFW firmware.
*
* Joris van Rantwijk 2024
*/
#include <limits.h>
@ -120,7 +122,7 @@ void run_data_server(puzzlefw::PuzzleFwDevice& device)
// Catch Ctrl-C for controlled shut down.
asio::signal_set signals(io, SIGINT);
signals.async_wait(
[&io](auto& ec, int sig) {
[&io](auto ec, int sig) {
log(LOG_INFO, "Got SIGINT, stopping server");
io.stop();
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
#define PUZZLEFW_SW_MAJOR 0
#define PUZZLEFW_SW_MINOR 1