mirror of
https://github.com/espressif/openthread.git
synced 2026-08-05 18:37:45 +00:00
[nexus] enable PCAP output (#12350)
This commit enables PCAP output in the Nexus platform by adding a Nexus::Pcap utility class and integrating it into the Nexus::Core. PCAP logging can be enabled by setting the OT_NEXUS_PCAP_FILE environment variable.
This commit is contained in:
@@ -46,6 +46,7 @@ add_library(ot-nexus-platform
|
||||
platform/nexus_mdns.cpp
|
||||
platform/nexus_misc.cpp
|
||||
platform/nexus_node.cpp
|
||||
platform/nexus_pcap.cpp
|
||||
platform/nexus_radio.cpp
|
||||
platform/nexus_settings.cpp
|
||||
platform/nexus_trel.cpp
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
*/
|
||||
|
||||
#include "nexus_core.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "nexus_node.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -40,11 +43,20 @@ Core::Core(void)
|
||||
, mCurNodeId(0)
|
||||
, mPendingAction(false)
|
||||
{
|
||||
const char *pcapFile;
|
||||
|
||||
VerifyOrQuit(!sInUse);
|
||||
sCore = this;
|
||||
sInUse = true;
|
||||
|
||||
mNextAlarmTime = mNow.GetDistantFuture();
|
||||
|
||||
pcapFile = getenv("OT_NEXUS_PCAP_FILE");
|
||||
|
||||
if ((pcapFile != nullptr) && (pcapFile[0] != '\0'))
|
||||
{
|
||||
mPcap.Open(pcapFile);
|
||||
}
|
||||
}
|
||||
|
||||
Core::~Core(void) { sInUse = false; }
|
||||
@@ -134,6 +146,8 @@ void Core::ProcessRadio(Node &aNode)
|
||||
|
||||
ackRequested = aNode.mRadio.mTxFrame.GetAckRequest();
|
||||
|
||||
mPcap.WriteFrame(aNode.mRadio.mTxFrame, mNow.GetValue() * 1000ull);
|
||||
|
||||
otPlatRadioTxStarted(&aNode.GetInstance(), &aNode.mRadio.mTxFrame);
|
||||
|
||||
for (Node &rxNode : mNodes)
|
||||
@@ -187,16 +201,15 @@ void Core::ProcessRadio(Node &aNode)
|
||||
|
||||
if (ackMode != kNoAck)
|
||||
{
|
||||
Mac::TxFrame ackFrame;
|
||||
uint8_t ackPsdu[Mac::Frame::kImmAckLength];
|
||||
|
||||
ClearAllBytes(ackFrame);
|
||||
ackFrame.mPsdu = ackPsdu;
|
||||
Radio::Frame ackFrame;
|
||||
|
||||
ackFrame.GenerateImmAck(
|
||||
static_cast<const Mac::RxFrame &>(static_cast<const Mac::Frame &>(aNode.mRadio.mTxFrame)),
|
||||
(ackMode == kSendAckFramePending));
|
||||
|
||||
ackFrame.UpdateFcs();
|
||||
mPcap.WriteFrame(ackFrame, mNow.GetValue() * 1000ull);
|
||||
|
||||
otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, &ackFrame, kErrorNone);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "instance/instance.hpp"
|
||||
|
||||
#include "nexus_alarm.hpp"
|
||||
#include "nexus_pcap.hpp"
|
||||
#include "nexus_radio.hpp"
|
||||
#include "nexus_utils.hpp"
|
||||
|
||||
@@ -85,6 +86,7 @@ private:
|
||||
static bool sInUse;
|
||||
|
||||
OwningList<Node> mNodes;
|
||||
Pcap mPcap;
|
||||
uint16_t mCurNodeId;
|
||||
bool mPendingAction;
|
||||
TimeMilli mNow;
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2026, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "nexus_pcap.hpp"
|
||||
#include "common/clearable.hpp"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/encoding.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
Pcap::Pcap(void)
|
||||
: mFile(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Pcap::~Pcap(void) { Close(); }
|
||||
|
||||
void Pcap::Open(const char *aFilename)
|
||||
{
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct PcapHeader
|
||||
{
|
||||
uint32_t mMagicNumber;
|
||||
uint16_t mVersionMajor;
|
||||
uint16_t mVersionMinor;
|
||||
int32_t mThisZone;
|
||||
uint32_t mSigFigs;
|
||||
uint32_t mSnapLen;
|
||||
uint32_t mNetwork;
|
||||
} OT_TOOL_PACKED_END header;
|
||||
|
||||
Close();
|
||||
|
||||
mFile = fopen(aFilename, "wb");
|
||||
VerifyOrExit(mFile != nullptr);
|
||||
|
||||
ClearAllBytes(header);
|
||||
header.mMagicNumber = LittleEndian::HostSwap(kPcapMagicNumber);
|
||||
header.mVersionMajor = LittleEndian::HostSwap(kPcapVersionMajor);
|
||||
header.mVersionMinor = LittleEndian::HostSwap(kPcapVersionMinor);
|
||||
header.mSnapLen = LittleEndian::HostSwap(kPcapSnapLen);
|
||||
header.mNetwork = LittleEndian::HostSwap(kPcapDlt154Tap);
|
||||
|
||||
VerifyOrExit(fwrite(&header, sizeof(header), 1, mFile) == 1, Close());
|
||||
VerifyOrExit(fflush(mFile) == 0, Close());
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Pcap::Close(void)
|
||||
{
|
||||
VerifyOrExit(mFile != nullptr);
|
||||
|
||||
fclose(mFile);
|
||||
mFile = nullptr;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Pcap::WriteFrame(const otRadioFrame &aFrame, uint64_t aTimeUs)
|
||||
{
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct PcapRecordHeader
|
||||
{
|
||||
uint32_t mTsSec;
|
||||
uint32_t mTsUsec;
|
||||
uint32_t mInclLen;
|
||||
uint32_t mOrigLen;
|
||||
} OT_TOOL_PACKED_END recordHeader;
|
||||
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct TapHeader
|
||||
{
|
||||
uint8_t mVersion;
|
||||
uint8_t mReserved;
|
||||
uint16_t mLength;
|
||||
} OT_TOOL_PACKED_END tapHeader;
|
||||
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct TapFcsTlv
|
||||
{
|
||||
uint16_t mType;
|
||||
uint16_t mLength;
|
||||
uint8_t mValue;
|
||||
uint8_t mPadding[3];
|
||||
} OT_TOOL_PACKED_END fcsTlv;
|
||||
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct TapChannelTlv
|
||||
{
|
||||
uint16_t mType;
|
||||
uint16_t mLength;
|
||||
uint8_t mPage;
|
||||
uint16_t mChannel;
|
||||
uint8_t mPadding[1];
|
||||
} OT_TOOL_PACKED_END channelTlv;
|
||||
|
||||
uint32_t tapLength;
|
||||
|
||||
VerifyOrExit(mFile != nullptr);
|
||||
|
||||
tapLength = sizeof(tapHeader) + sizeof(fcsTlv) + sizeof(channelTlv);
|
||||
|
||||
ClearAllBytes(recordHeader);
|
||||
recordHeader.mTsSec = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs / 1000000));
|
||||
recordHeader.mTsUsec = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs % 1000000));
|
||||
recordHeader.mInclLen = LittleEndian::HostSwap(tapLength + aFrame.mLength);
|
||||
recordHeader.mOrigLen = recordHeader.mInclLen;
|
||||
VerifyOrExit(fwrite(&recordHeader, sizeof(recordHeader), 1, mFile) == 1, Close());
|
||||
|
||||
ClearAllBytes(tapHeader);
|
||||
tapHeader.mVersion = LittleEndian::HostSwap(kTapVersion);
|
||||
tapHeader.mLength = LittleEndian::HostSwap(static_cast<uint16_t>(tapLength));
|
||||
VerifyOrExit(fwrite(&tapHeader, sizeof(tapHeader), 1, mFile) == 1, Close());
|
||||
|
||||
ClearAllBytes(fcsTlv);
|
||||
fcsTlv.mType = LittleEndian::HostSwap(kTapFcsType);
|
||||
fcsTlv.mLength = LittleEndian::HostSwap(kTapFcsLength);
|
||||
fcsTlv.mValue = LittleEndian::HostSwap(kTapFcsValue);
|
||||
VerifyOrExit(fwrite(&fcsTlv, sizeof(fcsTlv), 1, mFile) == 1, Close());
|
||||
|
||||
ClearAllBytes(channelTlv);
|
||||
channelTlv.mType = LittleEndian::HostSwap(kTapChannelType);
|
||||
channelTlv.mLength = LittleEndian::HostSwap(kTapChannelLength);
|
||||
channelTlv.mPage = LittleEndian::HostSwap(kTapChannelPage);
|
||||
channelTlv.mChannel = LittleEndian::HostSwap(aFrame.mChannel);
|
||||
VerifyOrExit(fwrite(&channelTlv, sizeof(channelTlv), 1, mFile) == 1, Close());
|
||||
|
||||
VerifyOrExit(fwrite(aFrame.mPsdu, aFrame.mLength, 1, mFile) == 1, Close());
|
||||
VerifyOrExit(fflush(mFile) == 0, Close());
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2026, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OT_NEXUS_PLATFORM_NEXUS_PCAP_HPP_
|
||||
#define OT_NEXUS_PLATFORM_NEXUS_PCAP_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include <openthread/platform/radio.h>
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
class Pcap
|
||||
{
|
||||
public:
|
||||
Pcap(void);
|
||||
~Pcap(void);
|
||||
|
||||
/**
|
||||
* Opens a pcap file.
|
||||
*
|
||||
* @param[in] aFilename The filename to open.
|
||||
*/
|
||||
void Open(const char *aFilename);
|
||||
|
||||
/**
|
||||
* Closes the pcap file.
|
||||
*/
|
||||
void Close(void);
|
||||
|
||||
/**
|
||||
* Writes a frame to the pcap file.
|
||||
*
|
||||
* @param[in] aFrame The frame to write.
|
||||
* @param[in] aTimeUs The timestamp in microseconds.
|
||||
*/
|
||||
void WriteFrame(const otRadioFrame &aFrame, uint64_t aTimeUs);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kPcapMagicNumber = 0xa1b2c3d4;
|
||||
static constexpr uint16_t kPcapVersionMajor = 2;
|
||||
static constexpr uint16_t kPcapVersionMinor = 4;
|
||||
static constexpr uint32_t kPcapSnapLen = 65535;
|
||||
static constexpr uint32_t kPcapDlt154Tap = 283;
|
||||
|
||||
static constexpr uint8_t kTapVersion = 0;
|
||||
|
||||
static constexpr uint16_t kTapFcsType = 0;
|
||||
static constexpr uint16_t kTapFcsLength = 1;
|
||||
static constexpr uint8_t kTapFcsValue = 1; // 16-bit CRC
|
||||
|
||||
static constexpr uint16_t kTapChannelType = 3;
|
||||
static constexpr uint16_t kTapChannelLength = 3;
|
||||
static constexpr uint8_t kTapChannelPage = 0;
|
||||
|
||||
FILE *mFile;
|
||||
};
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
#endif // OT_NEXUS_PLATFORM_NEXUS_PCAP_HPP_
|
||||
@@ -50,7 +50,7 @@ struct Radio
|
||||
static constexpr State kStateReceive = OT_RADIO_STATE_RECEIVE;
|
||||
static constexpr State kStateTransmit = OT_RADIO_STATE_TRANSMIT;
|
||||
|
||||
struct Frame : public Mac::Frame
|
||||
struct Frame : public Mac::TxFrame
|
||||
{
|
||||
Frame(void);
|
||||
explicit Frame(const Frame &aFrame);
|
||||
|
||||
Reference in New Issue
Block a user