From ec0754ec19801f43b5ab032baa5dc476fe123237 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 20 May 2026 10:49:08 +0200 Subject: [PATCH 01/10] Fix TimeSlave for Linux and extend logging --- score/time_slave/src/gptp/details/BUILD | 2 + score/time_slave/src/gptp/gptp_engine.cpp | 18 +++++--- .../src/gptp/platform/linux/raw_socket.cpp | 46 +++++++++++++++++-- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/score/time_slave/src/gptp/details/BUILD b/score/time_slave/src/gptp/details/BUILD index d15b0be5..5587a3d8 100644 --- a/score/time_slave/src/gptp/details/BUILD +++ b/score/time_slave/src/gptp/details/BUILD @@ -67,6 +67,8 @@ cc_library( ":os_syscalls", ":ptp_types", ":raw_socket", + "//score/TimeSlave/code/common:logging_contexts", + "@score_baselibs//score/mw/log:frontend", ], ) diff --git a/score/time_slave/src/gptp/gptp_engine.cpp b/score/time_slave/src/gptp/gptp_engine.cpp index 93161cae..de32164c 100644 --- a/score/time_slave/src/gptp/gptp_engine.cpp +++ b/score/time_slave/src/gptp/gptp_engine.cpp @@ -32,7 +32,7 @@ namespace details namespace { -constexpr int kRxTimeoutMs = 100; // poll timeout; keeps RxLoop responsive to shutdown +constexpr int kRxTimeoutMs = 500; // poll timeout; keeps RxLoop responsive to shutdown constexpr int kRxBufferSize = 2048; } // namespace @@ -70,13 +70,17 @@ GptpEngine::~GptpEngine() noexcept bool GptpEngine::Initialize() { + mw::log::LogInfo(kTimeSlaveAppContext) << "GptpEngine: Initializing on " << opts_.iface_name; if (running_.load(std::memory_order_acquire)) + { + mw::log::LogWarn(kTimeSlaveAppContext) << "GptpEngine::Initialize: Engine is already running"; return true; + } if (!identity_->Resolve(opts_.iface_name)) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine: failed to resolve ClockIdentity for " << opts_.iface_name; + << "GptpEngine::Initialize: Failed to resolve ClockIdentity"; return false; } @@ -85,14 +89,14 @@ bool GptpEngine::Initialize() if (!socket_->Open(opts_.iface_name)) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine: failed to open raw socket on " << opts_.iface_name; + << "GptpEngine::Initialize: Failed to open raw socket"; return false; } if (!socket_->EnableHwTimestamping()) { score::mw::log::LogWarn(kTimeSlaveAppContext) - << "GptpEngine: HW timestamping not available on " << opts_.iface_name << ", falling back to SW timestamps"; + << "GptpEngine::Initialize: HW timestamping not available, falling back to SW timestamps"; } running_.store(true, std::memory_order_release); @@ -108,7 +112,7 @@ bool GptpEngine::Initialize() catch (const std::system_error& e) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine: failed to create RxThread: " << std::string_view{e.what()}; + << "GptpEngine::Initialize: Failed to create RxThread: " << std::string_view{e.what()}; running_.store(false, std::memory_order_release); socket_->Close(); return false; @@ -123,12 +127,12 @@ bool GptpEngine::Initialize() catch (const std::system_error& e) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine: failed to create PdelayThread: " << std::string_view{e.what()}; + << "GptpEngine::Initialize: Failed to create PdelayThread: " << std::string_view{e.what()}; Deinitialize(); return false; } - score::mw::log::LogInfo(kTimeSlaveAppContext) << "GptpEngine initialized on " << opts_.iface_name; + score::mw::log::LogInfo(kTimeSlaveAppContext) << "GptpEngine initialized"; return true; } diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index b1b5a5ed..d523158f 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -12,7 +12,11 @@ ********************************************************************************/ #include "score/time_slave/src/gptp/details/raw_socket_impl.h" +#include "score/TimeSlave/code/common/logging_contexts.h" +#include "score/mw/log/logging.h" + #include +#include #include #include #include @@ -64,9 +68,12 @@ bool RawSocketImpl::Open(const std::string& iface) { Close(); - const int fd = sys_->socket_call(AF_PACKET, SOCK_RAW, htons(ETH_P_1588)); - if (fd < 0) + const int fd = sys_->socket_call(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (fd < 0) { + score::mw::log::LogError(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to create raw socket endpoint (errno=" << errno << ")"; return false; + } ::ifreq ifr{}; std::strncpy(ifr.ifr_name, iface.c_str(), IFNAMSIZ - 1); @@ -74,22 +81,55 @@ bool RawSocketImpl::Open(const std::string& iface) if (sys_->ioctl_call(fd, SIOCGIFINDEX, &ifr) < 0) { sys_->close_call(fd); + score::mw::log::LogError(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to manipulate raw socket endpoint (errno=" << errno << ")"; return false; } ::sockaddr_ll sa{}; sa.sll_family = AF_PACKET; - sa.sll_protocol = htons(ETH_P_1588); + sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = ifr.ifr_ifindex; if (sys_->bind_call(fd, reinterpret_cast<::sockaddr*>(&sa), sizeof(sa)) < 0) { sys_->close_call(fd); + score::mw::log::LogError(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to bind raw socket endpoint to interface " << iface << " (errno=" << errno << ")"; return false; } // SO_BINDTODEVICE: best-effort, don't fail if it doesn't work (void)sys_->setsockopt_call(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), static_cast(iface.size())); + // Enable promiscuous mode so the NIC passes all frames (including PTP multicast) to the kernel. + ::packet_mreq mr{}; + mr.mr_ifindex = ifr.ifr_ifindex; + mr.mr_type = PACKET_MR_PROMISC; + if (sys_->setsockopt_call(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) + { + score::mw::log::LogWarn(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to set promiscuous mode (errno=" << errno << ")"; + } + + // BPF filter: pass only frames with EtherType 0x88F7 (PTP/gPTP). + // ETH_P_ALL is used for socket/bind because ETH_P_1588 misses VLAN-tagged PTP frames + // (outer EtherType is 0x8100, not 0x88F7). The BPF filter runs in-kernel before delivery + // to userspace, so non-PTP frames are dropped with zero overhead in the application. + static const ::sock_filter kPtpBpfCode[] = { + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETH_P_1588, 1, 0), // == 0x88F7 → PASS + BPF_STMT(BPF_RET | BPF_K, 0U), // FAIL: drop + BPF_STMT(BPF_RET | BPF_K, static_cast(-1)), // PASS: accept + }; + ::sock_fprog prog{}; + prog.len = static_cast(sizeof(kPtpBpfCode) / sizeof(kPtpBpfCode[0])); + prog.filter = const_cast<::sock_filter*>(kPtpBpfCode); + if (sys_->setsockopt_call(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) + { + score::mw::log::LogWarn(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to set BPF filter (errno=" << errno << ")"; + } + fd_.store(fd, std::memory_order_release); iface_ = iface; return true; From a4c80780b355fd49b3bde3b96d5b7e682ee771a3 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 20 May 2026 10:51:30 +0200 Subject: [PATCH 02/10] Use gptp_shm_machine instead of stub --- score/time_daemon/src/ptp_machine/BUILD | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/score/time_daemon/src/ptp_machine/BUILD b/score/time_daemon/src/ptp_machine/BUILD index 32dd2840..2794e981 100644 --- a/score/time_daemon/src/ptp_machine/BUILD +++ b/score/time_daemon/src/ptp_machine/BUILD @@ -19,6 +19,12 @@ alias( visibility = ["//score/time_daemon:__subpackages__"], ) +alias( + name = "shm_ptp_machine", + actual = "//score/TimeDaemon/code/ptp_machine/shm:gptp_shm_machine", + visibility = ["//score/TimeDaemon:__subpackages__"], +) + cc_unit_test_suites_for_host_and_qnx( name = "unit_test_suite", test_suites_from_sub_packages = [ From f5ba6f560c781aa1fad3f0ee720ea2a0d11c2865 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Thu, 21 May 2026 14:35:59 +0200 Subject: [PATCH 03/10] Log incoming PTP messages --- score/time_slave/src/gptp/gptp_engine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/score/time_slave/src/gptp/gptp_engine.cpp b/score/time_slave/src/gptp/gptp_engine.cpp index de32164c..556bd111 100644 --- a/score/time_slave/src/gptp/gptp_engine.cpp +++ b/score/time_slave/src/gptp/gptp_engine.cpp @@ -262,11 +262,13 @@ void GptpEngine::HandlePacket(const std::uint8_t* frame, int len, const ::timesp switch (msg.msgtype) { case kPtpMsgtypePdelayReq: + mw::log::LogDebug(kGPtpMachineContext) << "PdelayReq message received, hw_ts=" << hw_ts.ns << " ns"; if (msg.ptpHdr.domainNumber == opts_.domain_number) SendPDelayResponseAndFollowUp(msg, hw_ts); break; case kPtpMsgtypeSync: + mw::log::LogDebug(kGPtpMachineContext) << "Sync message received, hw_ts=" << hw_ts.ns << " ns"; if (msg.ptpHdr.domainNumber != opts_.domain_number) break; msg.recvHardwareTS = hw_ts; @@ -275,6 +277,7 @@ void GptpEngine::HandlePacket(const std::uint8_t* frame, int len, const ::timesp break; case kPtpMsgtypeFollowUp: + mw::log::LogDebug(kGPtpMachineContext) << "FollowUp message received, hw_ts=" << hw_ts.ns << " ns"; if (msg.ptpHdr.domainNumber != opts_.domain_number) break; msg.parseMessageTs = TimestampToTmv(msg.follow_up.preciseOriginTimestamp); @@ -299,6 +302,7 @@ void GptpEngine::HandlePacket(const std::uint8_t* frame, int len, const ::timesp break; case kPtpMsgtypePdelayResp: + mw::log::LogDebug(kGPtpMachineContext) << "PdelayResp message received, hw_ts=" << hw_ts.ns << " ns"; msg.recvHardwareTS = hw_ts; msg.parseMessageTs = TimestampToTmv(msg.pdelay_resp.requestReceiptTimestamp); if (pdelay_) @@ -306,12 +310,14 @@ void GptpEngine::HandlePacket(const std::uint8_t* frame, int len, const ::timesp break; case kPtpMsgtypePdelayRespFollowUp: + mw::log::LogDebug(kGPtpMachineContext) << "PdelayRespFollowUp message received, hw_ts=" << hw_ts.ns << " ns"; msg.parseMessageTs = TimestampToTmv(msg.pdelay_resp_fup.responseOriginReceiptTimestamp); if (pdelay_) pdelay_->OnResponseFollowUp(msg); break; default: + mw::log::LogDebug(kGPtpMachineContext) << "Unhandled message received: " << static_cast(msg.msgtype) << ", hw_ts=" << hw_ts.ns << " ns"; break; } } From 5757b25dcec713ffa91b5190be68abaad63ffff2 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Thu, 21 May 2026 14:36:52 +0200 Subject: [PATCH 04/10] Use sw kernel timestamps as fallback to hw timestamps --- .../src/gptp/platform/linux/raw_socket.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index d523158f..6562d731 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -130,6 +130,17 @@ bool RawSocketImpl::Open(const std::string& iface) << "RawSocket::Open: Failed to set BPF filter (errno=" << errno << ")"; } + // Enable kernel layer software timestamps as a baseline, so we get at least some timestamp + // even if hw timestamping isn't available or fails to configure. The HW timestamp will come + // in the same control message but different field, so the caller can use it if present and + // fall back to SW timestamp if not. + const int enable = 1; + if (sys_->setsockopt_call(fd, SOL_SOCKET, SO_TIMESTAMPNS, &enable, sizeof(enable)) < 0) + { + score::mw::log::LogWarn(kTimeSlaveAppContext) + << "RawSocket::Open: Failed to enable SO_TIMESTAMPNS (errno=" << errno << ")"; + } + fd_.store(fd, std::memory_order_release); iface_ = iface; return true; @@ -208,7 +219,14 @@ int RawSocketImpl::Recv(std::uint8_t* buf, std::size_t buf_len, ::timespec& hwts continue; const auto* ts = reinterpret_cast(CMSG_DATA(cm)); if (ts[2].tv_sec != 0 || ts[2].tv_nsec != 0) + { hwts = ts[2]; + break; // Prefer raw HW timestamp if available + } + } + else if (cm->cmsg_type == SO_TIMESTAMPNS) + { + memcpy(&hwts, CMSG_DATA(cm), sizeof(hwts)); } } return len; From 23a48f746ceb8e1b0a13364acb1d0d399f11715e Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Mon, 15 Jun 2026 11:56:41 +0200 Subject: [PATCH 05/10] Fix rebase misses --- score/time_daemon/src/ptp_machine/BUILD | 4 ++-- score/time_slave/src/gptp/details/BUILD | 2 +- score/time_slave/src/gptp/platform/linux/raw_socket.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/score/time_daemon/src/ptp_machine/BUILD b/score/time_daemon/src/ptp_machine/BUILD index 2794e981..e293d085 100644 --- a/score/time_daemon/src/ptp_machine/BUILD +++ b/score/time_daemon/src/ptp_machine/BUILD @@ -21,8 +21,8 @@ alias( alias( name = "shm_ptp_machine", - actual = "//score/TimeDaemon/code/ptp_machine/shm:gptp_shm_machine", - visibility = ["//score/TimeDaemon:__subpackages__"], + actual = "//score/time_daemon/src/ptp_machine/shm:gptp_shm_machine", + visibility = ["//score/time_daemon:__subpackages__"], ) cc_unit_test_suites_for_host_and_qnx( diff --git a/score/time_slave/src/gptp/details/BUILD b/score/time_slave/src/gptp/details/BUILD index 5587a3d8..f8035663 100644 --- a/score/time_slave/src/gptp/details/BUILD +++ b/score/time_slave/src/gptp/details/BUILD @@ -67,7 +67,7 @@ cc_library( ":os_syscalls", ":ptp_types", ":raw_socket", - "//score/TimeSlave/code/common:logging_contexts", + "//score/time_slave/src/common:logging_contexts", "@score_baselibs//score/mw/log:frontend", ], ) diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index 6562d731..ff271b11 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -12,7 +12,7 @@ ********************************************************************************/ #include "score/time_slave/src/gptp/details/raw_socket_impl.h" -#include "score/TimeSlave/code/common/logging_contexts.h" +#include "score/time_slave/src/common/logging_contexts.h" #include "score/mw/log/logging.h" #include From 0f9fe8a3db90e5bb0309d367c22b24d68266750f Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 8 Jul 2026 14:23:16 +0200 Subject: [PATCH 06/10] Some fixes --- score/time_slave/src/gptp/gptp_engine.cpp | 8 ++++---- score/time_slave/src/gptp/platform/linux/raw_socket.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/score/time_slave/src/gptp/gptp_engine.cpp b/score/time_slave/src/gptp/gptp_engine.cpp index 556bd111..eb6e9fdd 100644 --- a/score/time_slave/src/gptp/gptp_engine.cpp +++ b/score/time_slave/src/gptp/gptp_engine.cpp @@ -80,7 +80,7 @@ bool GptpEngine::Initialize() if (!identity_->Resolve(opts_.iface_name)) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine::Initialize: Failed to resolve ClockIdentity"; + << "GptpEngine::Initialize: Failed to resolve ClockIdentity for " << opts_.iface_name; return false; } @@ -89,14 +89,14 @@ bool GptpEngine::Initialize() if (!socket_->Open(opts_.iface_name)) { score::mw::log::LogError(kTimeSlaveAppContext) - << "GptpEngine::Initialize: Failed to open raw socket"; + << "GptpEngine::Initialize: Failed to open raw socket on " << opts_.iface_name; return false; } if (!socket_->EnableHwTimestamping()) { score::mw::log::LogWarn(kTimeSlaveAppContext) - << "GptpEngine::Initialize: HW timestamping not available, falling back to SW timestamps"; + << "GptpEngine::Initialize: HW timestamping not available on " << opts_.iface_name << ", falling back to SW timestamps"; } running_.store(true, std::memory_order_release); @@ -132,7 +132,7 @@ bool GptpEngine::Initialize() return false; } - score::mw::log::LogInfo(kTimeSlaveAppContext) << "GptpEngine initialized"; + score::mw::log::LogInfo(kTimeSlaveAppContext) << "GptpEngine initialized on " << opts_.iface_name; return true; } diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index ff271b11..1742e769 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -116,10 +116,10 @@ bool RawSocketImpl::Open(const std::string& iface) // (outer EtherType is 0x8100, not 0x88F7). The BPF filter runs in-kernel before delivery // to userspace, so non-PTP frames are dropped with zero overhead in the application. static const ::sock_filter kPtpBpfCode[] = { - BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETH_P_1588, 1, 0), // == 0x88F7 → PASS - BPF_STMT(BPF_RET | BPF_K, 0U), // FAIL: drop - BPF_STMT(BPF_RET | BPF_K, static_cast(-1)), // PASS: accept + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETH_P_1588, 1, 0), // == 0x88F7 → PASS + BPF_STMT(BPF_RET | BPF_K, 0U), // FAIL: drop + BPF_STMT(BPF_RET | BPF_K, static_cast(-1)), // PASS: accept }; ::sock_fprog prog{}; prog.len = static_cast(sizeof(kPtpBpfCode) / sizeof(kPtpBpfCode[0])); From a93e5aa669ad4ac6c4a126fad8ba1093ebe7d4d4 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 8 Jul 2026 14:23:55 +0200 Subject: [PATCH 07/10] Add cmdline parser for ethernet interface --- .../time_slave/src/application/time_slave.cpp | 50 ++++++++++++++++++- score/time_slave/src/application/time_slave.h | 2 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/score/time_slave/src/application/time_slave.cpp b/score/time_slave/src/application/time_slave.cpp index 9742556a..a09fa4f3 100644 --- a/score/time_slave/src/application/time_slave.cpp +++ b/score/time_slave/src/application/time_slave.cpp @@ -15,6 +15,7 @@ #include "score/mw/log/logging.h" #include "score/time_slave/src/common/logging_contexts.h" +#include #include namespace score @@ -28,12 +29,26 @@ namespace constexpr std::int32_t kInitSuccess = 0; constexpr std::int32_t kInitFailure = -1; +void PrintUsage(const std::string& app_name) +{ + std::cout + << "Usage: " << app_name + << " [options]\n" + "Options:\n" + " -h, --help Print this message and exit.\n" + " -i, --interface Define the ethernet interface to be used (default is \"" + << details::GptpEngineOptions().iface_name <<"\").\n"; +} + } // namespace TimeSlave::TimeSlave() = default; -std::int32_t TimeSlave::Initialize(const score::mw::lifecycle::ApplicationContext& /*context*/) +std::int32_t TimeSlave::Initialize(const score::mw::lifecycle::ApplicationContext& context) { + if (!ParseCmdLineArgs(context)) + return kInitFailure; + engine_ = std::make_unique(opts_); if (!engine_->Initialize()) @@ -52,6 +67,39 @@ std::int32_t TimeSlave::Initialize(const score::mw::lifecycle::ApplicationContex return kInitSuccess; } +bool TimeSlave::ParseCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context) +{ + const auto& args = context.get_arguments(); + auto iter = args.cbegin(); + const auto& app_name = *iter; + + for (++iter; iter != args.cend(); ++iter) + { + if (*iter == "-h" || *iter == "--help") + { + PrintUsage(app_name); + return false; + } + else if (*iter == "-i" || *iter == "--interface") + { + ++iter; + if (iter == args.cend()) + { + PrintUsage(app_name); + return false; + } + opts_.iface_name = *iter; + } + else + { + std::cerr << "Unknown argument \"" << *iter << "\"." << std::endl; + PrintUsage(app_name); + return false; + } + } + return true; +} + std::int32_t TimeSlave::Run(const score::cpp::stop_token& token) { constexpr auto kPublishInterval = std::chrono::milliseconds{50}; diff --git a/score/time_slave/src/application/time_slave.h b/score/time_slave/src/application/time_slave.h index c385ddb3..f690aafd 100644 --- a/score/time_slave/src/application/time_slave.h +++ b/score/time_slave/src/application/time_slave.h @@ -48,6 +48,8 @@ class TimeSlave final : public score::mw::lifecycle::Application std::int32_t Run(const score::cpp::stop_token& token) override; private: + bool ParseCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context); + details::GptpEngineOptions opts_; std::unique_ptr engine_; details::GptpIpcPublisher publisher_; From 50f3ce93e87c78027f2b22bb836e3ae4bc2f4129 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 8 Jul 2026 15:24:47 +0200 Subject: [PATCH 08/10] Clang-format --- .../time_slave/src/application/time_slave.cpp | 13 +++++------ score/time_slave/src/gptp/gptp_engine.cpp | 10 +++++---- .../src/gptp/platform/linux/raw_socket.cpp | 22 ++++++++++--------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/score/time_slave/src/application/time_slave.cpp b/score/time_slave/src/application/time_slave.cpp index a09fa4f3..daea355c 100644 --- a/score/time_slave/src/application/time_slave.cpp +++ b/score/time_slave/src/application/time_slave.cpp @@ -31,13 +31,12 @@ constexpr std::int32_t kInitFailure = -1; void PrintUsage(const std::string& app_name) { - std::cout - << "Usage: " << app_name - << " [options]\n" - "Options:\n" - " -h, --help Print this message and exit.\n" - " -i, --interface Define the ethernet interface to be used (default is \"" - << details::GptpEngineOptions().iface_name <<"\").\n"; + std::cout << "Usage: " << app_name + << " [options]\n" + "Options:\n" + " -h, --help Print this message and exit.\n" + " -i, --interface Define the ethernet interface to be used (default is \"" + << details::GptpEngineOptions().iface_name << "\").\n"; } } // namespace diff --git a/score/time_slave/src/gptp/gptp_engine.cpp b/score/time_slave/src/gptp/gptp_engine.cpp index eb6e9fdd..5fe0ad5a 100644 --- a/score/time_slave/src/gptp/gptp_engine.cpp +++ b/score/time_slave/src/gptp/gptp_engine.cpp @@ -95,8 +95,8 @@ bool GptpEngine::Initialize() if (!socket_->EnableHwTimestamping()) { - score::mw::log::LogWarn(kTimeSlaveAppContext) - << "GptpEngine::Initialize: HW timestamping not available on " << opts_.iface_name << ", falling back to SW timestamps"; + score::mw::log::LogWarn(kTimeSlaveAppContext) << "GptpEngine::Initialize: HW timestamping not available on " + << opts_.iface_name << ", falling back to SW timestamps"; } running_.store(true, std::memory_order_release); @@ -310,14 +310,16 @@ void GptpEngine::HandlePacket(const std::uint8_t* frame, int len, const ::timesp break; case kPtpMsgtypePdelayRespFollowUp: - mw::log::LogDebug(kGPtpMachineContext) << "PdelayRespFollowUp message received, hw_ts=" << hw_ts.ns << " ns"; + mw::log::LogDebug(kGPtpMachineContext) + << "PdelayRespFollowUp message received, hw_ts=" << hw_ts.ns << " ns"; msg.parseMessageTs = TimestampToTmv(msg.pdelay_resp_fup.responseOriginReceiptTimestamp); if (pdelay_) pdelay_->OnResponseFollowUp(msg); break; default: - mw::log::LogDebug(kGPtpMachineContext) << "Unhandled message received: " << static_cast(msg.msgtype) << ", hw_ts=" << hw_ts.ns << " ns"; + mw::log::LogDebug(kGPtpMachineContext) + << "Unhandled message received: " << static_cast(msg.msgtype) << ", hw_ts=" << hw_ts.ns << " ns"; break; } } diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index 1742e769..a5e6bbc5 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -12,8 +12,8 @@ ********************************************************************************/ #include "score/time_slave/src/gptp/details/raw_socket_impl.h" -#include "score/time_slave/src/common/logging_contexts.h" #include "score/mw/log/logging.h" +#include "score/time_slave/src/common/logging_contexts.h" #include #include @@ -69,7 +69,8 @@ bool RawSocketImpl::Open(const std::string& iface) Close(); const int fd = sys_->socket_call(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (fd < 0) { + if (fd < 0) + { score::mw::log::LogError(kTimeSlaveAppContext) << "RawSocket::Open: Failed to create raw socket endpoint (errno=" << errno << ")"; return false; @@ -94,7 +95,8 @@ bool RawSocketImpl::Open(const std::string& iface) { sys_->close_call(fd); score::mw::log::LogError(kTimeSlaveAppContext) - << "RawSocket::Open: Failed to bind raw socket endpoint to interface " << iface << " (errno=" << errno << ")"; + << "RawSocket::Open: Failed to bind raw socket endpoint to interface " << iface << " (errno=" << errno + << ")"; return false; } @@ -104,7 +106,7 @@ bool RawSocketImpl::Open(const std::string& iface) // Enable promiscuous mode so the NIC passes all frames (including PTP multicast) to the kernel. ::packet_mreq mr{}; mr.mr_ifindex = ifr.ifr_ifindex; - mr.mr_type = PACKET_MR_PROMISC; + mr.mr_type = PACKET_MR_PROMISC; if (sys_->setsockopt_call(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) { score::mw::log::LogWarn(kTimeSlaveAppContext) @@ -116,13 +118,13 @@ bool RawSocketImpl::Open(const std::string& iface) // (outer EtherType is 0x8100, not 0x88F7). The BPF filter runs in-kernel before delivery // to userspace, so non-PTP frames are dropped with zero overhead in the application. static const ::sock_filter kPtpBpfCode[] = { - BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETH_P_1588, 1, 0), // == 0x88F7 → PASS - BPF_STMT(BPF_RET | BPF_K, 0U), // FAIL: drop - BPF_STMT(BPF_RET | BPF_K, static_cast(-1)), // PASS: accept + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETH_P_1588, 1, 0), // == 0x88F7 → PASS + BPF_STMT(BPF_RET | BPF_K, 0U), // FAIL: drop + BPF_STMT(BPF_RET | BPF_K, static_cast(-1)), // PASS: accept }; ::sock_fprog prog{}; - prog.len = static_cast(sizeof(kPtpBpfCode) / sizeof(kPtpBpfCode[0])); + prog.len = static_cast(sizeof(kPtpBpfCode) / sizeof(kPtpBpfCode[0])); prog.filter = const_cast<::sock_filter*>(kPtpBpfCode); if (sys_->setsockopt_call(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) { @@ -221,7 +223,7 @@ int RawSocketImpl::Recv(std::uint8_t* buf, std::size_t buf_len, ::timespec& hwts if (ts[2].tv_sec != 0 || ts[2].tv_nsec != 0) { hwts = ts[2]; - break; // Prefer raw HW timestamp if available + break; // Prefer raw HW timestamp if available } } else if (cm->cmsg_type == SO_TIMESTAMPNS) From 949c02a939eab328cddef4f6d386ac87b61db5cf Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Wed, 8 Jul 2026 15:44:11 +0200 Subject: [PATCH 09/10] Improve method name --- score/time_daemon/src/application/svt/BUILD | 4 ++-- score/time_slave/src/application/time_slave.cpp | 4 ++-- score/time_slave/src/application/time_slave.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/score/time_daemon/src/application/svt/BUILD b/score/time_daemon/src/application/svt/BUILD index 44f97731..b39ac90a 100644 --- a/score/time_daemon/src/application/svt/BUILD +++ b/score/time_daemon/src/application/svt/BUILD @@ -41,7 +41,7 @@ load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER "svt_handler", False, [ - "//score/time_daemon/src/ptp_machine/shm:gptp_shm_machine", + "//score/time_daemon/src/ptp_machine:shm_ptp_machine", "//score/time_daemon/src/verification_machine/svt:svt_verification_machine", ], ), @@ -49,7 +49,7 @@ load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER "svt_handler_for_utests", True, [ - "//score/time_daemon/src/ptp_machine/shm:gptp_shm_machine", + "//score/time_daemon/src/ptp_machine:shm_ptp_machine", "//score/time_daemon/src/verification_machine/svt:svt_verification_machine_for_utests", ], ), diff --git a/score/time_slave/src/application/time_slave.cpp b/score/time_slave/src/application/time_slave.cpp index daea355c..dddab9ba 100644 --- a/score/time_slave/src/application/time_slave.cpp +++ b/score/time_slave/src/application/time_slave.cpp @@ -45,7 +45,7 @@ TimeSlave::TimeSlave() = default; std::int32_t TimeSlave::Initialize(const score::mw::lifecycle::ApplicationContext& context) { - if (!ParseCmdLineArgs(context)) + if (!ParseAndApplyCmdLineArgs(context)) return kInitFailure; engine_ = std::make_unique(opts_); @@ -66,7 +66,7 @@ std::int32_t TimeSlave::Initialize(const score::mw::lifecycle::ApplicationContex return kInitSuccess; } -bool TimeSlave::ParseCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context) +bool TimeSlave::ParseAndApplyCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context) { const auto& args = context.get_arguments(); auto iter = args.cbegin(); diff --git a/score/time_slave/src/application/time_slave.h b/score/time_slave/src/application/time_slave.h index f690aafd..2cea1ce4 100644 --- a/score/time_slave/src/application/time_slave.h +++ b/score/time_slave/src/application/time_slave.h @@ -48,7 +48,7 @@ class TimeSlave final : public score::mw::lifecycle::Application std::int32_t Run(const score::cpp::stop_token& token) override; private: - bool ParseCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context); + bool ParseAndApplyCmdLineArgs(const score::mw::lifecycle::ApplicationContext& context); details::GptpEngineOptions opts_; std::unique_ptr engine_; From b9c5a749b79aa633740d4427de3aa061e7e384a9 Mon Sep 17 00:00:00 2001 From: "Hornburg Bjoern (ETAS-ECM XPC-Hi)" Date: Fri, 10 Jul 2026 15:18:42 +0200 Subject: [PATCH 10/10] Fix BPF comment --- score/time_slave/src/gptp/platform/linux/raw_socket.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp index a5e6bbc5..4b6036b9 100644 --- a/score/time_slave/src/gptp/platform/linux/raw_socket.cpp +++ b/score/time_slave/src/gptp/platform/linux/raw_socket.cpp @@ -113,9 +113,8 @@ bool RawSocketImpl::Open(const std::string& iface) << "RawSocket::Open: Failed to set promiscuous mode (errno=" << errno << ")"; } - // BPF filter: pass only frames with EtherType 0x88F7 (PTP/gPTP). - // ETH_P_ALL is used for socket/bind because ETH_P_1588 misses VLAN-tagged PTP frames - // (outer EtherType is 0x8100, not 0x88F7). The BPF filter runs in-kernel before delivery + // BPF filter: pass only frames with EtherType 0x88F7 (PTP/gPTP). This is fine ss gPTP frames + // are n9ot VLAN (nor priority) tagged. The BPF filter runs in-kernel before delivery // to userspace, so non-PTP frames are dropped with zero overhead in the application. static const ::sock_filter kPtpBpfCode[] = { BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), // load EtherType