4040 /* !!! START-INCLUDE-SECTION !!!*/
4141 #include " xcp.h"
4242 #include " xcp_hw.h"
43+ #include " xcp_tl_timeout.h"
4344/* !!! END-INCLUDE-SECTION !!!*/
4445
4546const unsigned long READ_TIMEOUT_MS = 2000 ; // Timeout when reading a frame
@@ -52,6 +53,7 @@ class ClientWrapper {
5253 virtual size_t write_bytes (const uint8_t * buf, size_t len) = 0;
5354 virtual bool connected () = 0;
5455 virtual void stop () = 0;
56+ virtual bool is_datagram () const = 0;
5557
5658 virtual ~ClientWrapper () {
5759 }
@@ -104,6 +106,10 @@ class EthernetClientWrapper : public ClientWrapper {
104106 }
105107 }
106108
109+ bool is_datagram () const override {
110+ return false ;
111+ }
112+
107113 private:
108114
109115 EthernetClient m_client;
@@ -173,14 +179,18 @@ class EthernetUdpClientWrapper : public ClientWrapper {
173179 // no persistent connection to stop for UDP
174180 }
175181
182+ bool is_datagram () const override {
183+ return true ;
184+ }
185+
176186 private:
177187
178- EthernetUDP* m_udp;
179- IPAddress m_remoteIp;
180- uint16_t m_remotePort;
181- uint8_t m_buf[XCP_COMM_BUFLEN];
182- size_t m_size;
183- size_t m_offset;
188+ EthernetUDP* m_udp;
189+ IPAddress m_remoteIp;
190+ uint16_t m_remotePort;
191+ uint8_t m_buf[XCP_COMM_BUFLEN];
192+ size_t m_size;
193+ size_t m_offset;
184194};
185195
186196class EthernetAdapter : public ArduinoNetworkIf {
@@ -197,6 +207,7 @@ class EthernetAdapter : public ArduinoNetworkIf {
197207 while (!Serial) {
198208 ; // wait for serial port to connect. Needed for native USB port only
199209 }
210+ Serial.println (" \n Blueparrot XCP (UDP) on Ethernet" );
200211 Ethernet.init (10 );
201212 delay (1000 ); // Give the hardware some time to initialize
202213
@@ -220,9 +231,6 @@ class EthernetAdapter : public ArduinoNetworkIf {
220231 Serial.println (" Failed to get a valid IP address." );
221232 return false ;
222233 }
223-
224- Serial.print (" \n Blueparrot XCP (UDP) on Ethernet -- IP: " );
225- Serial.println (Ethernet.localIP ());
226234 m_udp.begin (m_port);
227235 return true ;
228236 }
@@ -250,12 +258,12 @@ class EthernetAdapter : public ArduinoNetworkIf {
250258
251259 private:
252260
253- uint8_t m_mac_address_storage[6 ] = XCP_ON_ETHERNET_MAC_ADDRESS;
254- uint8_t * m_mac_address = m_mac_address_storage;
255- bool m_use_dhcp;
256- IPAddress m_ip;
257- uint16_t m_port;
258- EthernetUDP m_udp;
261+ uint8_t m_mac_address_storage[6 ] = XCP_ON_ETHERNET_MAC_ADDRESS;
262+ uint8_t * m_mac_address = m_mac_address_storage;
263+ bool m_use_dhcp;
264+ IPAddress m_ip;
265+ uint16_t m_port;
266+ EthernetUDP m_udp;
259267 EthernetUdpClientWrapper m_udpClientWrapper; // preallocated UDP client wrapper
260268};
261269 #endif // XCP_ON_ETHERNET_ARDUINO_DRIVER == XCP_ON_ETHERNET_DRIVER_ETHERNET
@@ -325,6 +333,10 @@ class WiFiUdpClientWrapper : public ClientWrapper {
325333 // no-op for UDP
326334 }
327335
336+ bool is_datagram () const override {
337+ return true ;
338+ }
339+
328340 private:
329341
330342 WiFiUDP* m_udp;
@@ -393,10 +405,20 @@ class WiFiAdapter : public ArduinoNetworkIf {
393405 WiFiUDP m_udp;
394406 const char * m_ssid;
395407 const char * m_pass;
396- WiFiUdpClientWrapper m_udpClientWrapper; // preallocated UDP client wrapper
408+ WiFiUdpClientWrapper m_udpClientWrapper;
397409};
398410 #endif // XCP_ON_ETHERNET_ARDUINO_DRIVER == XCP_ON_ETHERNET_DRIVER_WIFI
399411
412+ static ArduinoNetworkIf* s_net = nullptr ;
413+ static ClientWrapper* s_client = nullptr ;
414+ static bool s_connected = false ;
415+
416+ static volatile bool s_timeout_triggered = false ;
417+
418+ static void XcpTl_OnTimeout (void ) {
419+ s_timeout_triggered = true ;
420+ }
421+
400422class FrameParser {
401423 public:
402424
@@ -442,16 +464,15 @@ class FrameParser {
442464 }
443465};
444466
445- static ArduinoNetworkIf* s_net = nullptr ;
446- static ClientWrapper* s_client = nullptr ;
447- static bool s_connected = false ;
448-
449467static bool ar_read_n (uint8_t * out, size_t n, unsigned long timeout_ms) {
450468 if (!s_client)
451469 return false ;
452470 unsigned long start = millis ();
453471 size_t got = 0 ;
454472 while (got < n) {
473+ if (s_timeout_triggered) {
474+ return false ;
475+ }
455476 if (!s_client->connected () && s_client->available () == 0 ) {
456477 return false ;
457478 }
@@ -462,16 +483,22 @@ static bool ar_read_n(uint8_t* out, size_t n, unsigned long timeout_ms) {
462483 if (r <= 0 )
463484 return false ;
464485 got += (size_t )r;
486+ XcpTl_TimeoutReset ();
465487 } else {
466- if (millis () - start > timeout_ms)
488+ XcpTl_TimeoutCheck ();
489+ if (millis () - start > timeout_ms) {
490+ s_timeout_triggered = true ;
467491 return false ;
492+ }
468493 delay (1 );
469494 }
470495 }
471496 return true ;
472497}
473498
474499static int ar_read_header (uint16_t * len, uint16_t * counter) {
500+ s_timeout_triggered = false ;
501+ XcpTl_TimeoutStart ();
475502 uint8_t hdr[4 ];
476503 if (!ar_read_n (hdr, 4 , READ_TIMEOUT_MS)) {
477504 return 0 ;
@@ -489,6 +516,8 @@ static int ar_read_data(uint8_t* data, uint16_t len) {
489516extern " C" {
490517
491518 void XcpTl_Init (void ) {
519+ XcpTl_TimeoutInit ((uint16_t )READ_TIMEOUT_MS, XcpTl_OnTimeout);
520+ s_timeout_triggered = false ;
492521 /* Initialize backend network adapter and start listening. */
493522 #if XCP_ON_ETHERNET_ARDUINO_DRIVER == XCP_ON_ETHERNET_DRIVER_WIFI
494523 s_net = new WiFiAdapter (XCP_ON_ETHERNET_WIFI_SSID, XCP_ON_ETHERNET_WIFI_PASSWORD, XCP_ON_ETHERNET_PORT);
@@ -524,11 +553,14 @@ extern "C" {
524553 }
525554
526555 static void XcpTl_Accept (void ) {
527- if (!s_connected && s_net) {
528- s_net->process ();
529- ClientWrapper* c = s_net->accept_client ();
530- if (c) {
531- s_client = c;
556+ if (!s_net) {
557+ return ;
558+ }
559+ s_net->process ();
560+ ClientWrapper* c = s_net->accept_client ();
561+ if (c) {
562+ s_client = c;
563+ if (!s_connected) {
532564 s_connected = true ;
533565 XcpTl_SaveConnection ();
534566 }
@@ -537,6 +569,7 @@ extern "C" {
537569
538570 void XcpTl_MainFunction (void ) {
539571 XcpTl_Accept ();
572+ XcpTl_TimeoutCheck ();
540573 XcpTl_RxHandler ();
541574 }
542575
@@ -558,16 +591,33 @@ extern "C" {
558591
559592 int hres = ar_read_header (&dlc, &counter);
560593 if (hres <= 0 ) {
561- /* Connection lost or no data */
594+ if (s_timeout_triggered) {
595+ s_timeout_triggered = false ;
596+ XcpTl_TimeoutStop ();
597+ Serial.println (" Reception timeout (header) — aborting current frame" );
598+ return ;
599+ }
600+ /* Connection lost or other error */
601+ if (s_client && s_client->is_datagram ()) {
602+ /* For UDP, keep last endpoint; just stop TL timeout and abort this frame */
603+ XcpTl_TimeoutStop ();
604+ return ;
605+ }
562606 XcpTl_ReleaseConnection ();
563607 s_client->stop ();
564608 s_client = nullptr ;
565609 s_connected = false ;
610+ XcpTl_TimeoutStop ();
566611 return ;
567612 }
568613
569- if (dlc > XCP_TRANSPORT_LAYER_CTO_BUFFER_SIZE || dlc > (uint16_t )XCP_COMM_BUFLEN) {
614+ if (( dlc > XCP_TRANSPORT_LAYER_CTO_BUFFER_SIZE) || ( dlc > (uint16_t )XCP_COMM_BUFLEN) ) {
570615 // XcpHw_ErrorMsg((char*)"XcpTl_RxHandler: DLC too large", EINVAL);
616+ if (s_client && s_client->is_datagram ()) {
617+ /* For UDP, keep endpoint but drop this malformed frame */
618+ XcpTl_TimeoutStop ();
619+ return ;
620+ }
571621 XcpTl_ReleaseConnection ();
572622 s_client->stop ();
573623 s_client = nullptr ;
@@ -578,28 +628,37 @@ extern "C" {
578628 Xcp_CtoIn.len = dlc;
579629 int dres = ar_read_data (Xcp_CtoIn.data , dlc);
580630 if (dres <= 0 ) {
631+ if (s_timeout_triggered) {
632+ s_timeout_triggered = false ;
633+ XcpTl_TimeoutStop ();
634+ Serial.println (" Reception timeout (data) — aborting current frame" );
635+ return ;
636+ }
637+ if (s_client && s_client->is_datagram ()) {
638+ /* For UDP, keep last endpoint on short read or error; abort this frame only */
639+ XcpTl_TimeoutStop ();
640+ return ;
641+ }
581642 XcpTl_ReleaseConnection ();
582643 s_client->stop ();
583644 s_client = nullptr ;
584645 s_connected = false ;
646+ XcpTl_TimeoutStop ();
585647 return ;
586648 }
587-
588649 /* Dispatch the freshly received CTO command. */
589650 Xcp_DispatchCommand (&Xcp_CtoIn);
590-
591- /* For UDP wrappers, the packet is fully consumed now; mark connection free. */
592- if (!s_client->connected ()) {
593- XcpTl_ReleaseConnection ();
594- s_client->stop ();
595- s_client = nullptr ;
596- s_connected = false ;
597- }
651+ XcpTl_TimeoutStop ();
598652 }
599653
600654 void XcpTl_Send (uint8_t const * buf, uint16_t len) {
601655 XCP_TL_ENTER_CRITICAL ();
602656 if (s_connected && s_client) {
657+ #ifdef XCP_UDP_DEBUG
658+ Serial.print (" XCP_TL_SEND len=" ); Serial.print (len);
659+ Serial.print (" conn=" ); Serial.print (s_connected ? 1 : 0 );
660+ Serial.print (" datagram=" ); Serial.println (s_client->is_datagram () ? 1 : 0 );
661+ #endif
603662 /* Data already contains ETH header (LEN/CTR) + payload. */
604663 (void )s_client->write_bytes (buf, len);
605664 }
0 commit comments