Start working on remote control server
This commit is contained in:
parent
66050aca5b
commit
ea5d3c3a1d
|
@ -1,5 +1,6 @@
|
||||||
devicetree/devicetree.dtb
|
devicetree/devicetree.dtb
|
||||||
buildroot_overlay/opt/puzzlefw/bin/puzzlecmd
|
buildroot_overlay/opt/puzzlefw/bin/puzzlecmd
|
||||||
|
buildroot_overlay/opt/puzzlefw/bin/remotectl
|
||||||
buildroot_overlay/opt/puzzlefw/driver/
|
buildroot_overlay/opt/puzzlefw/driver/
|
||||||
downloads/
|
downloads/
|
||||||
buildroot-2023.02.8/
|
buildroot-2023.02.8/
|
||||||
|
|
|
@ -8,17 +8,19 @@ CFLAGS = -Wall -O2
|
||||||
CXXFLAGS = -std=c++17 -Wall -Wno-psabi -O2
|
CXXFLAGS = -std=c++17 -Wall -Wno-psabi -O2
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: puzzlecmd
|
all: puzzlecmd remotectl
|
||||||
|
|
||||||
puzzlecmd: puzzlecmd.o puzzlefw.o
|
puzzlecmd: puzzlecmd.o puzzlefw.o
|
||||||
$(CXX) -o $@ $(LDFLAGS) $^
|
$(CXX) -o $@ $(LDFLAGS) $^
|
||||||
|
|
||||||
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
|
remotectl: remotectl.o puzzlefw.o
|
||||||
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
|
$(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
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
$(RM) -f -- puzzlecmd testje *.o
|
$(RM) -f -- puzzlecmd remotectl *.o
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
@ -82,11 +83,17 @@ public:
|
||||||
m_dma_stream.disable_interrupt();
|
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.
|
* Start the server.
|
||||||
*
|
*
|
||||||
* This method must not be called directly while a multi-threaded Asio
|
* If a multi-threaded Asio event loop is running, this method may only
|
||||||
* event loop is running. In that case, use "async_start_server()".
|
* be called through the strand returned by "get_executor()".
|
||||||
*/
|
*/
|
||||||
void start_server()
|
void start_server()
|
||||||
{
|
{
|
||||||
|
@ -113,7 +120,7 @@ public:
|
||||||
|
|
||||||
// Asynchronously accept connections.
|
// Asynchronously accept connections.
|
||||||
m_acceptor.async_accept(
|
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);
|
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.
|
* Stop the server and close current connections.
|
||||||
*
|
*
|
||||||
* This method must not be called directly while a multi-threaded Asio
|
* If a multi-threaded Asio event loop is running, this method may only
|
||||||
* event loop is running. In that case, use "async_stop_server()".
|
* be called through the strand returned by "get_executor()".
|
||||||
*/
|
*/
|
||||||
void stop_server()
|
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.
|
* Called when an FPGA interrupt occurs.
|
||||||
*
|
*
|
||||||
|
@ -206,7 +173,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Re-initialize DMA stream and discard stale data from the DMA buffer.
|
* 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()
|
void discard_stale_data()
|
||||||
{
|
{
|
||||||
|
@ -228,7 +195,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Accept completion handler. */
|
/** 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)
|
asio::ip::tcp::socket conn)
|
||||||
{
|
{
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -256,7 +223,7 @@ private:
|
||||||
// Retry accept call.
|
// Retry accept call.
|
||||||
if (m_acceptor.is_open()) {
|
if (m_acceptor.is_open()) {
|
||||||
m_acceptor.async_accept(
|
m_acceptor.async_accept(
|
||||||
[this](auto& ec, auto s) {
|
[this](auto ec, auto s) {
|
||||||
handle_accept(ec, std::move(s));
|
handle_accept(ec, std::move(s));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -281,7 +248,7 @@ private:
|
||||||
if (m_acceptor.is_open()) {
|
if (m_acceptor.is_open()) {
|
||||||
// Continue accepting connections.
|
// Continue accepting connections.
|
||||||
m_acceptor.async_accept(
|
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()) {
|
if (m_connection.is_open()) {
|
||||||
|
@ -302,7 +269,7 @@ private:
|
||||||
// closed remotely (or client writes unexpected data).
|
// closed remotely (or client writes unexpected data).
|
||||||
m_connection.async_receive(
|
m_connection.async_receive(
|
||||||
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
|
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.
|
// Clear buffer, then enable DMA stream.
|
||||||
|
@ -314,7 +281,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Receive completion handler. */
|
/** 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) {
|
if (m_stale_receive) {
|
||||||
// This completion refers to an old, already closed connection.
|
// This completion refers to an old, already closed connection.
|
||||||
|
@ -324,7 +291,7 @@ private:
|
||||||
// Initiate async receive for the new connection.
|
// Initiate async receive for the new connection.
|
||||||
m_connection.async_receive(
|
m_connection.async_receive(
|
||||||
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
|
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;
|
return;
|
||||||
|
@ -359,10 +326,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send completion handler. */
|
/** 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_in_progress);
|
||||||
assert(m_send_size > 0);
|
|
||||||
|
|
||||||
m_send_in_progress = false;
|
m_send_in_progress = false;
|
||||||
|
|
||||||
|
@ -394,22 +360,14 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < m_send_buffer.size()) {
|
// Send operation completed.
|
||||||
// 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.
|
// Release the completed part of the DMA buffer.
|
||||||
m_dma_stream.consume_data(m_send_size);
|
assert(len == m_send_buffer.size());
|
||||||
|
m_dma_stream.consume_data(m_send_buffer.size());
|
||||||
|
|
||||||
// Try to send more data.
|
// Try to send more data.
|
||||||
transmit_data(false);
|
transmit_data(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to send data from the DMA buffer to the TCP connection.
|
* Try to send data from the DMA buffer to the TCP connection.
|
||||||
|
@ -447,7 +405,7 @@ private:
|
||||||
// If timeout occurs, we will send whatever data is available.
|
// If timeout occurs, we will send whatever data is available.
|
||||||
m_timer.expires_after(WAIT_TIMEOUT);
|
m_timer.expires_after(WAIT_TIMEOUT);
|
||||||
m_timer.async_wait(
|
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.
|
// Done. We will be notified for interrupt or timeout.
|
||||||
return;
|
return;
|
||||||
|
@ -467,16 +425,15 @@ private:
|
||||||
// Initiate async send.
|
// Initiate async send.
|
||||||
// Limit the block size so we can release that part of the buffer
|
// Limit the block size so we can release that part of the buffer
|
||||||
// as soon as it completes.
|
// as soon as it completes.
|
||||||
m_send_size = std::min(data_available, SEND_MAX_BLOCK);
|
size_t block_size = std::min(data_available, SEND_MAX_BLOCK);
|
||||||
m_send_buffer = asio::buffer(data, m_send_size);
|
m_send_buffer = asio::buffer(data, block_size);
|
||||||
m_send_in_progress = true;
|
m_send_in_progress = true;
|
||||||
m_connection.async_send(
|
asio::async_write(m_connection, m_send_buffer,
|
||||||
m_send_buffer,
|
[this](auto ec, size_t n){ handle_send(ec, n); });
|
||||||
[this](auto& ec, size_t n){ handle_send(ec, n); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Timeout handler. */
|
/** Timeout handler. */
|
||||||
void handle_timer(const boost::system::error_code& error)
|
void handle_timer(boost::system::error_code error)
|
||||||
{
|
{
|
||||||
if (error) {
|
if (error) {
|
||||||
// Ignore error due to cancellation.
|
// Ignore error due to cancellation.
|
||||||
|
@ -520,7 +477,6 @@ private:
|
||||||
bool m_stale_receive;
|
bool m_stale_receive;
|
||||||
bool m_stale_send;
|
bool m_stale_send;
|
||||||
bool m_send_in_progress;
|
bool m_send_in_progress;
|
||||||
size_t m_send_size;
|
|
||||||
asio::const_buffer m_send_buffer;
|
asio::const_buffer m_send_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -541,7 +497,7 @@ public:
|
||||||
, m_interval(interval)
|
, m_interval(interval)
|
||||||
{
|
{
|
||||||
m_timer.expires_after(m_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.
|
// Delete copy constructor and assignment operator.
|
||||||
|
@ -567,7 +523,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Timeout handler. */
|
/** Timeout handler. */
|
||||||
void handle_timer(const boost::system::error_code& error)
|
void handle_timer(boost::system::error_code error)
|
||||||
{
|
{
|
||||||
if (error) {
|
if (error) {
|
||||||
// Ignore error due to cancellation.
|
// Ignore error due to cancellation.
|
||||||
|
@ -580,7 +536,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
m_timer.expires_after(m_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); });
|
||||||
|
|
||||||
check_dma_error();
|
check_dma_error();
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Called when an FPGA interrupt occurs. */
|
/** 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) {
|
if (error) {
|
||||||
// Ignore error due to cancellation.
|
// Ignore error due to cancellation.
|
||||||
|
@ -100,7 +100,7 @@ private:
|
||||||
|
|
||||||
// Wait for next interrupt.
|
// Wait for next interrupt.
|
||||||
m_uio_fd.async_wait(asio::posix::descriptor_base::wait_read,
|
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;
|
PuzzleFwDevice& m_device;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* puzzlecmd.cpp
|
* puzzlecmd.cpp
|
||||||
*
|
*
|
||||||
* Command-line program to test PuzzleFW firmware.
|
* Command-line program to test PuzzleFW firmware.
|
||||||
|
*
|
||||||
|
* Joris van Rantwijk 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -120,7 +122,7 @@ void run_data_server(puzzlefw::PuzzleFwDevice& device)
|
||||||
// Catch Ctrl-C for controlled shut down.
|
// Catch Ctrl-C for controlled shut down.
|
||||||
asio::signal_set signals(io, SIGINT);
|
asio::signal_set signals(io, SIGINT);
|
||||||
signals.async_wait(
|
signals.async_wait(
|
||||||
[&io](auto& ec, int sig) {
|
[&io](auto ec, int sig) {
|
||||||
log(LOG_INFO, "Got SIGINT, stopping server");
|
log(LOG_INFO, "Got SIGINT, stopping server");
|
||||||
io.stop();
|
io.stop();
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
||||||
|
#define PUZZLEFW_SW_MAJOR 0
|
||||||
|
#define PUZZLEFW_SW_MINOR 1
|
Loading…
Reference in New Issue