Start working on remote control server
This commit is contained in:
		
							parent
							
								
									66050aca5b
								
							
						
					
					
						commit
						ea5d3c3a1d
					
				|  | @ -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/ | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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
											
										
									
								
							|  | @ -0,0 +1,2 @@ | |||
| #define PUZZLEFW_SW_MAJOR 0 | ||||
| #define PUZZLEFW_SW_MINOR 1 | ||||
		Loading…
	
		Reference in New Issue