blob: 90abba8c826d92f5167901cf828f4a8e68a781d0 [file] [log] [blame]
/*
*
* Copyright (c) 2014-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file contains the implementation of
* WoBle Control Path and Troughput Test
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <BuildConfig.h>
#if CONFIG_NETWORK_LAYER_BLE
#include <string.h>
#include <Weave/Core/WeaveConfig.h>
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/FlagUtils.hpp>
#include <BleLayer/BleConfig.h>
#include <BleLayer/BLEEndPoint.h>
#include <BleLayer/BleLayer.h>
#include <BleLayer/WoBle.h>
#if WEAVE_ENABLE_WOBLE_TEST
#include "WoBleTest.h"
#endif
// Define below to enable extremely verbose, BLE end point-specific debug logging.
#undef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
#ifdef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
#define WeaveLogDebugBleEndPoint(MOD, MSG, ...) WeaveLogError(MOD, MSG, ## __VA_ARGS__)
#else
#define WeaveLogDebugBleEndPoint(MOD, MSG, ...)
#endif
namespace nl {
namespace Ble {
#if WEAVE_ENABLE_WOBLE_TEST
BLE_ERROR HandleCommandTest(void *ble, BLE_CONNECTION_OBJECT connObj, uint32_t packetCount,
uint32_t duration, uint16_t txGap, uint8_t needAck, uint16_t payloadSize, bool reverse)
{
BLE_ERROR err = BLE_NO_ERROR;
BLEEndPoint *endPoint = ((nl::Ble::BleLayer *)ble)->mTestBleEndPoint;
if (endPoint == NULL)
{
WeaveLogError(Ble, "no endpoint for BLE sent data");
return BLE_ERROR_BAD_ARGS;
}
WeaveLogDebugBleEndPoint(Ble, "%s: Start count %u, duration %u, ack %u, size %u, reverse %u\n",
__FUNCTION__, packetCount, duration, needAck, payloadSize, reverse);
endPoint->mWoBleTest.mCommandTestRequest.PacketCount = packetCount;
endPoint->mWoBleTest.mCommandTestRequest.Duration = duration;
endPoint->mWoBleTest.mCommandTestRequest.TxGap = txGap;
endPoint->mWoBleTest.mCommandTestRequest.NeedAck = needAck;
if (payloadSize < COMMAND_TESTDATA_HDR_LEN)
payloadSize = COMMAND_TESTDATA_HDR_LEN;
// Actual payload includes the test data header
endPoint->mWoBleTest.mCommandTestRequest.PayloadSize = payloadSize - COMMAND_TESTDATA_HDR_LEN;
if (reverse == true)
{
err = endPoint->mWoBleTest.DoCommandTestRequest(endPoint);
}
else
{
err = endPoint->mWoBleTest.HandleCommandTest(endPoint);
}
return err;
}
BLE_ERROR HandleCommandTestResult(void *ble, BLE_CONNECTION_OBJECT connObj, bool local)
{
BLE_ERROR err = BLE_NO_ERROR;
BLEEndPoint *ep = ((nl::Ble::BleLayer *)ble)->mTestBleEndPoint;
if (ep == NULL)
{
WeaveLogError(Ble, "no endpoint for BLE sent data");
return BLE_ERROR_BAD_ARGS;
}
if (local == false)
err = ep->mWoBleTest.DoCommandTestResult(kBleCommandTestResult_Request, 0);
else
WoBleTest::LogBleTestResult(&ep->mWoBleTest.mCommandTestResult);
return err;
}
BLE_ERROR HandleCommandTestAbort(void *ble, BLE_CONNECTION_OBJECT connObj)
{
BLE_ERROR err = BLE_NO_ERROR;
BLEEndPoint *endPoint = ((nl::Ble::BleLayer *)ble)->mTestBleEndPoint;
if (endPoint == NULL)
{
WeaveLogError(Ble, "no endpoint for BLE sent ABORT");
return err;
}
err = endPoint->mWoBleTest.DoCommandTestAbort(-1);
return err;
}
BLE_ERROR HandleCommandTxTiming(void *ble, BLE_CONNECTION_OBJECT connObj, bool enabled, bool remote)
{
BLE_ERROR err = BLE_NO_ERROR;
BLEEndPoint *ep = ((nl::Ble::BleLayer *)ble)->mTestBleEndPoint;
if (ep == NULL)
{
WeaveLogError(Ble, "no endpoint for BLE sent data");
return BLE_ERROR_BAD_ARGS;
}
if (remote)
err = ep->mWoBleTest.DoCommandTxTiming(enabled);
else
err = ep->mWoBleTest.HandleCommandTxTiming(ep, enabled);
return err;
}
// BleTransportCommandMessage implementation:
BLE_ERROR BleTransportCommandMessage::Encode(PacketBuffer *msgBuf, BleTransportCommandMessage& cmd) const
{
uint8_t *p = msgBuf->Start();
BLE_ERROR err = BLE_NO_ERROR;
// Verify we can write the fixed-length request without running into the end of the buffer.
VerifyOrExit(msgBuf->MaxDataLength() > COMMAND_HEADER_LEN, err = BLE_ERROR_NO_MEMORY);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.CmdHdr.PacketLength);
nl::Weave::Encoding::Write8(p, cmd.CmdHdr.Version);
nl::Weave::Encoding::Write8(p, cmd.CmdHdr.PacketType);
switch (cmd.CmdHdr.PacketType) {
case kBleCommandType_TestAck:
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTestAck.Type);
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTestAck.SequenceNumber);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestAck.ResultCode);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_ACK_HDR_LEN);
break;
case kBleCommandType_TestData:
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTestData.Type);
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTestData.NeedAck);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestData.Length);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestData.Sequence);
VerifyOrExit(Data != NULL && cmd.Payload.MsgTestData.Length <= BLE_TEST_DATA_MAX_LEN, err = BLE_ERROR_NO_MEMORY);
memcpy(p, Data, cmd.Payload.MsgTestData.Length);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_DATA_HDR_LEN + cmd.Payload.MsgTestData.Length);
break;
case kBleCommandType_TestRequest:
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestRequest.PacketCount);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestRequest.Duration);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestRequest.TxGap);
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTestRequest.NeedAck);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestRequest.PayloadSize);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_TESTREQ_HDR_LEN);
break;
case kBleCommandType_TestResult:
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TestResultOp);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.TestResult);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.PacketCount);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.Duration);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.AckCount);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.TxDrops);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxGap);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.PayloadSize);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxPktCount);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.TxTimeMs);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxTimeMax);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxTimeMin);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxAckCount);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.TxAckTimeMs);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxAckTimeMax);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxAckTimeMin);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.TxTimeLastMs);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgTestResult.PayloadLast);
nl::Weave::Encoding::LittleEndian::Write32(p, cmd.Payload.MsgTestResult.PayloadBytes);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_TESTRESULT_HDR_LEN);
break;
case kBleCommandType_WobleMTU:
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgWobleMTU.Op);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgWobleMTU.TxFragmentSize);
nl::Weave::Encoding::LittleEndian::Write16(p, cmd.Payload.MsgWobleMTU.RxFragmentSize);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_WOBLEMTU_HDR_LEN);
break;
case kBleCommandType_WobleWindowSize:
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgWobleWindowSize.Op);
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgWobleWindowSize.TxWindowSize);
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgWobleWindowSize.RxWindowSize);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_WINDOWSIZE_HDR_LEN);
break;
case kBleCommandType_TxTiming:
nl::Weave::Encoding::Write8(p, cmd.Payload.MsgTxTiming.Enable);
msgBuf->SetDataLength(COMMAND_HEADER_LEN + COMMAND_TXTIMING_HDR_LEN);
break;
default:
WeaveLogError(Ble, "%s: Not yet support", __FUNCTION__);
break;
}
exit:
return err;
}
BLE_ERROR BleTransportCommandMessage::Decode(const PacketBuffer &msgBuf, BleTransportCommandMessage& cmd)
{
const uint8_t *p = msgBuf.Start();
BLE_ERROR err = BLE_NO_ERROR;
// Verify we can read the fixed-length response without running into the end of the buffer.
VerifyOrExit(msgBuf.DataLength() >= COMMAND_HEADER_LEN, err = BLE_ERROR_MESSAGE_INCOMPLETE);
cmd.CmdHdr.PacketLength = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.CmdHdr.Version = nl::Weave::Encoding::Read8(p);
cmd.CmdHdr.PacketType = nl::Weave::Encoding::Read8(p);
switch (cmd.CmdHdr.PacketType) {
case kBleCommandType_TestAck:
cmd.Payload.MsgTestAck.Type = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgTestAck.SequenceNumber = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgTestAck.ResultCode = nl::Weave::Encoding::LittleEndian::Read32(p);
break;
case kBleCommandType_TestData:
cmd.Payload.MsgTestData.Type = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgTestData.NeedAck = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgTestData.Length = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestData.Sequence = nl::Weave::Encoding::LittleEndian::Read32(p);
VerifyOrExit(cmd.Data != NULL && cmd.Payload.MsgTestData.Length <= BLE_TEST_DATA_MAX_LEN,
err = BLE_ERROR_NO_MEMORY);
memcpy(cmd.Data, p, cmd.Payload.MsgTestData.Length);
break;
case kBleCommandType_TestRequest:
cmd.Payload.MsgTestRequest.PacketCount = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestRequest.Duration = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestRequest.TxGap = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestRequest.NeedAck = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgTestRequest.PayloadSize = nl::Weave::Encoding::LittleEndian::Read16(p);
break;
case kBleCommandType_TestResult:
cmd.Payload.MsgTestResult.TestResultOp = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TestResult = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.PacketCount = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.Duration = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.AckCount = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.TxDrops = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.TxGap = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.PayloadSize = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxPktCount = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxTimeMs = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.TxTimeMax = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxTimeMin = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxAckCount = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxAckTimeMs = nl::Weave::Encoding::LittleEndian::Read32(p);
cmd.Payload.MsgTestResult.TxAckTimeMax = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxAckTimeMin = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.TxTimeLastMs = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.PayloadLast = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgTestResult.PayloadBytes = nl::Weave::Encoding::LittleEndian::Read32(p);
break;
case kBleCommandType_WobleMTU:
cmd.Payload.MsgWobleMTU.Op = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgWobleMTU.TxFragmentSize = nl::Weave::Encoding::LittleEndian::Read16(p);
cmd.Payload.MsgWobleMTU.RxFragmentSize = nl::Weave::Encoding::LittleEndian::Read16(p);
break;
case kBleCommandType_WobleWindowSize:
cmd.Payload.MsgWobleWindowSize.Op = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgWobleWindowSize.TxWindowSize = nl::Weave::Encoding::Read8(p);
cmd.Payload.MsgWobleWindowSize.RxWindowSize = nl::Weave::Encoding::Read8(p);
break;
case kBleCommandType_TxTiming:
cmd.Payload.MsgTxTiming.Enable = nl::Weave::Encoding::Read8(p);
break;
default:
WeaveLogError(Ble, "%s: Not yet support", __FUNCTION__);
break;
}
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: ERROR = %d", __FUNCTION__, err);
return err;
}
BLE_ERROR WoBleTest::Init(BLEEndPoint *ep)
{
BLE_ERROR err = BLE_NO_ERROR;
mCommandUnderTest = WOBLE_TEST_NONE;
mCommandTxTiming = false;
mCommandReceiveQueue = NULL;
mCommandSendQueue = NULL;
mCommandAckToSend = NULL;
memset(&mTestTxThread, 0, sizeof(mTestTxThread));
memset(&mTxHistogram, 0, sizeof(mTxHistogram));
mMainThread = pthread_self();
mEp = ep;
WeaveLogError(Ble, "%s: Initialize WoBleTest, ep->%#p, thread %x", __FUNCTION__, ep, mMainThread);
// Register the command handler
ep->SetOnCommandReceivedCB(HandleCommandReceived);
return err;
}
void WoBleTest::Decode(uint8_t *src, uint8_t *dst, size_t size)
{
switch (size)
{
case sizeof(int8_t):
*dst = *src;
break;
case sizeof(int16_t):
{
int16_t i = nl::Weave::Encoding::LittleEndian::Read16(src);
memcpy(dst, &i, size);
}
break;
case sizeof(int32_t):
{
int32_t i = nl::Weave::Encoding::LittleEndian::Read32(src);
memcpy(dst, &i, size);
}
break;
case sizeof(int64_t):
{
int64_t i = nl::Weave::Encoding::LittleEndian::Read64(src);
memcpy(dst, &i, size);
}
break;
default:
WeaveLogError(Ble, "%s: unsupported size %u\n", __FUNCTION__, size);
break;
}
}
// This is the WoBle Command Handler
void WoBleTest::HandleCommandReceived(BLEEndPoint *ep, PacketBuffer *data)
{
BLE_ERROR err = BLE_NO_ERROR;
Weave::System::Error timerErr;
// Fail if any data missing
VerifyOrExit(ep != NULL && data != NULL, err = BLE_ERROR_BAD_ARGS);
// Add new message to send queue.
if (ep->mWoBleTest.mCommandReceiveQueue == NULL)
{
WeaveLogDebugBleEndPoint(Ble, "set data as new mCommandReceiveQueue");
ep->mWoBleTest.mCommandReceiveQueue = data;
}
else
{
WeaveLogDebugBleEndPoint(Ble, "added data to end");
ep->mWoBleTest.mCommandReceiveQueue->AddToEnd(data);
}
data = NULL;
// Handle BTP command in timer
timerErr = ep->mBle->mSystemLayer->StartTimer(0, HandleCommandPacket, ep);
VerifyOrExit(timerErr == BLE_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: Error %d", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return;
}
// This is the WoBle TxTiming Handler
void WoBleTest::DoTxTiming(PacketBuffer *data, int stage)
{
if (mCommandUnderTest == WOBLE_TEST_NONE && mCommandTxTiming == false)
return; // TxTiming is not enabled
WeaveLogDebugBleEndPoint(Ble, "%s: stage %d, mode %u:%u, data->%p, type %u, len %u",
__FUNCTION__, stage, mCommandUnderTest, mCommandTxTiming, data,
mEp->mWoBle.TxPacketType(), data->DataLength());
if (!mCommandTxTiming && (mCommandUnderTest && mEp->mWoBle.TxPacketType() != kType_Control))
return; // TxTiming is not required
switch(stage) {
case WOBLE_TX_START:
mTimeStats.mTxStartMs = nl::Weave::System::Timer::GetCurrentEpoch();
mTimeStats.mTxPayload = data->DataLength();
break;
case WOBLE_TX_DONE:
if (mTimeStats.mTxStartMs)
{
// Compute Tx time and collect the statistics
int diff = nl::Weave::System::Timer::GetCurrentEpoch() - mTimeStats.mTxStartMs;
AddTxRecord(mTimeStats.mTxStartMs, diff, mTimeStats.mTxPayload);
// Always keep the TxTime of last WoBle packet
mCommandTestResult.TxTimeLastMs = diff;
mCommandTestResult.PayloadLast = mTimeStats.mTxPayload;
mCommandTestResult.TxTimeMs += diff;
if (mCommandTestResult.TxTimeMax == 0)
mCommandTestResult.TxTimeMax =
mCommandTestResult.TxTimeMin = diff;
else
if (diff > mCommandTestResult.TxTimeMax)
mCommandTestResult.TxTimeMax = diff;
else
if (diff < mCommandTestResult.TxTimeMin)
mCommandTestResult.TxTimeMin = diff;
mCommandTestResult.TxPktCount++;
mCommandTestResult.PayloadBytes += mCommandTestResult.PayloadLast;
// Check if we're done with this packet
if (mCommandTestRequest.NeedAck == true)
mTimeStats.mTxAckStartMs = mTimeStats.mTxStartMs;
mTimeStats.mTxStartMs = 0;
WeaveLogDebugBleEndPoint(Ble, "%s: TxTimeMs %u, TxPktCount %u, PayloadBytes %u",
__FUNCTION__, mCommandTestResult.TxTimeMs,
mCommandTestResult.TxPktCount, mCommandTestResult.PayloadBytes);
WeaveLogDebugBleEndPoint(Ble, "%s: TxTimeLastMs %u, PayloadLast %u",
__FUNCTION__, mCommandTestResult.TxTimeLastMs, mCommandTestResult.PayloadLast);
// Send the next packet if it's already past due, so
// no need to wait for the next Tx time. It's to eliminate the Tx idle gap.
if (mCommandUnderTest == WOBLE_TEST_TX && diff >= mCommandTestRequest.TxGap)
mEp->mBle->mSystemLayer->StartTimer(0, DoTestDataSend, (void *)mEp);
}
if (mCommandUnderTest == WOBLE_TEST_TX && mEp->mWoBleTest.mCommand.CommandTest_Duration <= 0
&& mEp->mWoBleTest.mCommand.CommandTest_PacketCount == 0)
{
WeaveLogError(Ble, "%s: *** Finished sending last Tx test packet", __FUNCTION__);
// We just finished sending the last test packet
mCommandUnderTest = WOBLE_TEST_NONE;
}
break;
case WOBLE_TX_DATA_ACK:
if (mTimeStats.mTxAckStartMs)
{
// Compute Tx+Ack time and collect the statistics
int diff = nl::Weave::System::Timer::GetCurrentEpoch() -
mTimeStats.mTxAckStartMs;
mCommandTestResult.TxAckTimeMs += diff;
if (mCommandTestResult.TxAckTimeMax == 0)
mCommandTestResult.TxAckTimeMax =
mCommandTestResult.TxAckTimeMin = diff;
else
if (diff > mCommandTestResult.TxAckTimeMax)
mCommandTestResult.TxAckTimeMax = diff;
else
if (diff < mCommandTestResult.TxAckTimeMin)
mCommandTestResult.TxAckTimeMin = diff;
mCommandTestResult.TxAckCount++;
// Done with this packet
mTimeStats.mTxAckStartMs = 0;
WeaveLogDebugBleEndPoint(Ble, "%s: TxAckTimeMs %u, TxAckCount %u", __FUNCTION__,
mCommandTestResult.TxAckTimeMs, mCommandTestResult.TxAckCount);
}
break;
default:
break;
}
}
/*
* Check whether it's a null pointer or containing all 0s
*/
bool isEmptyData(char *data, size_t size)
{
if (data == NULL) return true;
while (size-- > 0)
if (*data++ != 0)
return false;
return true;
}
void WoBleTest::DoTestDataSend(Weave::System::Layer *systemLayer, void *appState, Weave::System::Error err)
{
BLEEndPoint *ep = static_cast<BLEEndPoint *>(appState);
PacketBuffer *data = NULL;
uint8_t type;
pthread_t curThread = pthread_self();
if (ep == NULL || ep->mState == BLEEndPoint::kState_Closed
|| ep->mWoBleTest.mCommandUnderTest != WOBLE_TEST_TX)
{
WeaveLogDebugBleEndPoint(Ble, "%s: State = %d, mCommandUnderTest %d, thread %x",
__FUNCTION__, ep? ep->mState : -1, ep->mWoBleTest.mCommandUnderTest, curThread);
ExitNow();
}
if (!(isEmptyData((char *)&ep->mWoBleTest.mTestTxThread, sizeof(pthread_t))
|| pthread_equal(curThread, ep->mWoBleTest.mTestTxThread)
|| pthread_equal(curThread, ep->mWoBleTest.mMainThread)))
{
WeaveLogDebugBleEndPoint(Ble, "%s: Keep Tx thread (id %x) and stop %x",
__FUNCTION__ , ep->mWoBleTest.mTestTxThread, curThread);
pthread_exit(NULL); // Suicide is the safest way to kill a thread
}
if (ep->mWoBleTest.mCommandTestResult.PacketCount++ == 0)
{
memcpy(&ep->mWoBleTest.mTestTxThread, &curThread, sizeof(pthread_t));
WeaveLogDebugBleEndPoint(Ble, "%s: Tx thread started (id %x)", __FUNCTION__, ep->mWoBleTest.mTestTxThread);
ep->mWoBleTest.mCommand.CommandTest_Start = true;
SetFlag(ep->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung, true);
ep->mWoBleTest.mCommand.CommandTest_Duration = (int32_t)ep->mWoBleTest.mCommandTestRequest.Duration;
ep->mWoBleTest.mCommand.CommandTest_PacketCount = (int32_t)ep->mWoBleTest.mCommandTestRequest.PacketCount;
ep->mWoBleTest.mCommandTestResult.TxGap = ep->mWoBleTest.mCommandTestRequest.TxGap;
ep->mWoBleTest.mCommandTestResult.PayloadSize = ep->mWoBleTest.mCommandTestRequest.PayloadSize;
WeaveLogDebugBleEndPoint(Ble, "\n%s: Count %d, Duration %u, TxGap %u, Ack %d, Size %u",
__FUNCTION__, ep->mWoBleTest.mCommandTestRequest.PacketCount,
ep->mWoBleTest.mCommandTestRequest.Duration, ep->mWoBleTest.mCommandTestRequest.TxGap,
ep->mWoBleTest.mCommandTestRequest.NeedAck, ep->mWoBleTest.mCommandTestRequest.PayloadSize);
}
else
type = kDataType_CONTINUE;
// Check if it's the last test packet
if (ep->mWoBleTest.mCommand.CommandTest_Duration > 0)
{
ep->mWoBleTest.mCommand.CommandTest_Duration -= ep->mWoBleTest.mCommandTestRequest.TxGap;
if ((int)ep->mWoBleTest.mCommand.CommandTest_Duration <= 0)
type = kDataType_END;
}
if (ep->mWoBleTest.mCommand.CommandTest_PacketCount > 0)
{
if (--(ep->mWoBleTest.mCommand.CommandTest_PacketCount) == 0)
type = kDataType_END;
}
if (ep->mSendQueue == NULL) // Allow Tx test data only when Tx queue is empty
{
if (ep->mWoBleTest.mCommand.CommandTest_Start == true)
{
type = kDataType_START;
ep->mWoBleTest.mCommand.CommandTest_Start = false;
}
data = PacketBuffer::New();
WeaveLogDebugBleEndPoint(Ble, "%s: Tx pkt# %d, data->%p, TxGap %d, duration %d, count %d",
__FUNCTION__, ep->mWoBleTest.mCommandTestResult.PacketCount, data,
ep->mWoBleTest.mCommandTestRequest.TxGap, ep->mWoBleTest.mCommand.CommandTest_Duration,
ep->mWoBleTest.mCommand.CommandTest_PacketCount);
VerifyOrExit(data != NULL, err = BLE_ERROR_NO_MEMORY);
ep->mWoBleTest.mCommand.CmdHdr.PacketLength =
sizeof(ep->mWoBleTest.mCommand.Payload.MsgTestData) +
ep->mWoBleTest.mCommandTestRequest.PayloadSize;
ep->mWoBleTest.mCommand.CmdHdr.Version = 0;
ep->mWoBleTest.mCommand.CmdHdr.PacketType = kBleCommandType_TestData;
ep->mWoBleTest.mCommand.Payload.MsgTestData.Type = type;
ep->mWoBleTest.mCommand.Payload.MsgTestData.NeedAck = ep->mWoBleTest.mCommandTestRequest.NeedAck;
ep->mWoBleTest.mCommand.Payload.MsgTestData.Length = ep->mWoBleTest.mCommandTestRequest.PayloadSize;
ep->mWoBleTest.mCommand.Payload.MsgTestData.Sequence = ep->mWoBleTest.mCommandTestResult.PacketCount;
err = ep->mWoBleTest.mCommand.Encode(data, ep->mWoBleTest.mCommand);
SuccessOrExit(err);
// Add new data to send queue.
ep->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = ep->DriveSending();
SuccessOrExit(err);
}
else
{
WeaveLogDebugBleEndPoint(Ble, "%s: GATT ON, Dropping pkt %d, TxGap %d, duration %d",
__FUNCTION__, ep->mWoBleTest.mCommandTestResult.PacketCount,
ep->mWoBleTest.mCommandTestRequest.TxGap, ep->mWoBleTest.mCommand.CommandTest_Duration);
ep->mWoBleTest.mCommandTestResult.TxDrops++;
}
ep->mWoBleTest.mCommandTestResult.Duration += ep->mWoBleTest.mCommandTestRequest.TxGap; // in ms
// Check if it's the last packet
if (type == kDataType_END)
{
SetFlag(ep->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung, false);
ep->mWoBleTest.mCommand.CommandTest_Duration = ep->mWoBleTest.mCommand.CommandTest_PacketCount = 0;
WeaveLogDebugBleEndPoint(Ble, "%s: Tx Test Done (id %x)", __FUNCTION__, curThread);
}
systemLayer->StartTimer(ep->mWoBleTest.mCommandTestRequest.TxGap, DoTestDataSend, (void *)ep);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return;
}
void * WoBleTest::StartTxThread(void * data)
{
BLEEndPoint *ep = static_cast<BLEEndPoint *>(data);
BLE_ERROR err = BLE_NO_ERROR;
Weave::System::Error timerErr;
timerErr = ep->mBle->mSystemLayer->StartTimer(0, DoTestDataSend, ep);
VerifyOrExit(timerErr == BLE_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: Error %d", __FUNCTION__, err);
WeaveLogDebugBleEndPoint(Ble, "%s: Thread exited (id %x)", __FUNCTION__, pthread_self());
return NULL;
}
BLE_ERROR WoBleTest::HandleCommandTest(BLEEndPoint *ep)
{
BLE_ERROR err = BLE_NO_ERROR;
pthread_t newThread;
ep->mWoBleTest.mCommandUnderTest = WOBLE_TEST_TX;
memset(&ep->mWoBleTest.mCommandTestResult, 0, sizeof(BTCommandTypeTestResult));
memset((char *)&ep->mWoBleTest.mTimeStats, 0, sizeof(ep->mWoBleTest.mTimeStats));
// Start the test
memset(ep->mWoBleTest.mCommand.Data, 0xff, BLE_TEST_DATA_MAX_LEN);
err = (BLE_ERROR)pthread_create(&newThread, NULL, StartTxThread, (void *)ep);
WeaveLogDebugBleEndPoint(Ble, "%s: Started thread (id %x), err %d", __FUNCTION__, newThread, err);
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: Error %d", __FUNCTION__, err);
return err;
}
// This function enables/disables the Tx Timing and Histogram
BLE_ERROR WoBleTest::HandleCommandTxTiming(BLEEndPoint *ep, bool enabled)
{
WeaveLogDebugBleEndPoint(Ble, "%s: enabled = %d", __FUNCTION__, enabled);
ep->mWoBleTest.mCommandTxTiming = enabled;
if (enabled)
{
memset(&ep->mWoBleTest.mCommandTestResult, 0, sizeof(BTCommandTypeTestResult));
memset((char *)&ep->mWoBleTest.mTimeStats, 0, sizeof(ep->mWoBleTest.mTimeStats));
if (ep->mWoBleTest.InitTxHistogram(WOBLE_TX_HISTOGRAM_FILE, WOBLE_TX_RECORD_COUNT, true) < 0)
WeaveLogError(Ble, "%s: Warning - No Tx Histogram\n", __FUNCTION__);
}
else
ep->mWoBleTest.DoneTxHistogram(true);
return BLE_NO_ERROR;
}
BLE_ERROR WoBleTest::DoCommandSendAck(SequenceNumber_t seqNum, int32_t result)
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTestAck);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TestAck;
mCommand.Payload.MsgTestAck.Type = result? kAckType_OK : kAckType_NOK;
mCommand.Payload.MsgTestAck.SequenceNumber = seqNum;
mCommand.Payload.MsgTestAck.ResultCode = result;
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
mEp->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = mEp->DriveSending();
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
BLE_ERROR WoBleTest::DoCommandTestResult(int32_t op, int32_t result)
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTestResult);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TestResult;
mCommand.Payload.MsgTestResult.TestResultOp = op;
if (op == kBleCommandTestResult_Reply)
{
mCommand.Payload.MsgTestResult = mCommandTestResult;
mCommand.Payload.MsgTestResult.TestResult = result;
}
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
// Add packet to send queue head
mEp->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = mEp->DriveSending();
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
BLE_ERROR WoBleTest::DoCommandTxTiming(bool enable)
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTxTiming);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TxTiming;
mCommand.Payload.MsgTxTiming.Enable = enable;
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
// Add packet to send queue head
mEp->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = mEp->DriveSending();
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
BLE_ERROR WoBleTest::DoCommandTestAbort(int32_t result)
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
WeaveLogDebugBleEndPoint(Ble, "%s: result %d\n", __FUNCTION__, result);
if (mCommandUnderTest == WOBLE_TEST_NONE)
return err;
StopTestTimer();
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTestData);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TestData;
mCommand.Payload.MsgTestData.Type = kDataType_ABORT;
mCommand.Payload.MsgTestData.NeedAck = false;
mCommand.Payload.MsgTestData.Length = 0;
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
mEp->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = mEp->DriveSending();
SuccessOrExit(err);
exit:
mCommandUnderTest = WOBLE_TEST_NONE; // always terminate local test
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
BLE_ERROR WoBleTest::DoCommandRequestTestResult()
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTestResult);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TestResult;
mCommand.Payload.MsgTestResult.TestResultOp = kBleCommandTestResult_Request;
mCommand.Payload.MsgTestResult.PacketCount = 0;
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
// Add packet to send queue head
mEp->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = mEp->DriveSending();
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
BLE_ERROR WoBleTest::DoCommandTestRequest(BLEEndPoint *ep)
{
BLE_ERROR err = BLE_NO_ERROR;
PacketBuffer *data;
WeaveLogDebugBleEndPoint(Ble, "%s: Sending TestRequest to device...\n", __FUNCTION__);
data = PacketBuffer::New();
mCommand.CmdHdr.PacketLength = sizeof(mCommand.Payload.MsgTestRequest);
mCommand.CmdHdr.Version = 0;
mCommand.CmdHdr.PacketType = kBleCommandType_TestRequest;
mCommand.Payload.MsgTestRequest.PacketCount = mCommandTestRequest.PacketCount;
mCommand.Payload.MsgTestRequest.Duration = mCommandTestRequest.Duration;
mCommand.Payload.MsgTestRequest.TxGap = mCommandTestRequest.TxGap;
mCommand.Payload.MsgTestRequest.NeedAck = mCommandTestRequest.NeedAck;
mCommand.Payload.MsgTestRequest.PayloadSize = mCommandTestRequest.PayloadSize;
err = mCommand.Encode(data, mCommand);
SuccessOrExit(err);
ep->QueueTx(data, kType_Control);
data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
err = ep->DriveSending();
SuccessOrExit(err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return err;
}
void WoBleTest::LogBleTestResult(BTCommandTypeTestResult *result)
{
WeaveLogDebugBleEndPoint(Ble, "%s: PacketCount %u, TxPktCount %u, TxAckCount %u", __FUNCTION__,
result->PacketCount, result->TxPktCount, result->TxAckCount);
if (result->PacketCount > 0)
{
WeaveLogError(Ble, "TestResult : %d", result->TestResult);
WeaveLogError(Ble, "PacketCount : %u", result->PacketCount);
WeaveLogError(Ble, "Duration : %u", result->Duration);
WeaveLogError(Ble, "AckCount : %u", result->AckCount);
WeaveLogError(Ble, "TxDrops : %u", result->TxDrops);
WeaveLogError(Ble, "TxGap : %u", result->TxGap);
// Actual payload includes the test data header
WeaveLogError(Ble, "PayloadSize : %u", result->PayloadSize + COMMAND_TESTDATA_HDR_LEN);
}
if (result->TxPktCount > 0)
{
WeaveLogError(Ble, "=========================");
WeaveLogError(Ble, "Last Tx time : %u", result->TxTimeLastMs);
WeaveLogError(Ble, "Last Payload Bytes : %u", result->PayloadLast);
WeaveLogError(Ble, "=========================");
WeaveLogError(Ble, "Tx Packet Count : %u", result->TxPktCount);
WeaveLogError(Ble, "Total Payload Bytes : %u", result->PayloadBytes);
WeaveLogError(Ble, "Average Tx time/pkt : %u", result->TxTimeMs / result->TxPktCount);
WeaveLogError(Ble, "Max Tx time : %u", result->TxTimeMax);
WeaveLogError(Ble, "Min Tx time : %u", result->TxTimeMin);
if (result->TxAckCount)
{
WeaveLogError(Ble, "Ack Packet Count : %u", result->TxAckCount);
WeaveLogError(Ble, "Average Tx+Ack time : %u", result->TxAckTimeMs / result->TxAckCount);
WeaveLogError(Ble, "Max Tx+Ack time : %u", result->TxAckTimeMax);
WeaveLogError(Ble, "Min Tx+Ack time : %u", result->TxAckTimeMin);
}
}
}
void WoBleTest::HandleCommandPacket(Weave::System::Layer *systemLayer, void *appState, Weave::System::Error err)
{
BLEEndPoint *ep = static_cast<BLEEndPoint *>(appState);
BleTransportCommandMessage cmd;
bool needAck = false;
PacketBuffer *data = NULL;
Weave::System::Error timerErr;
if (ep == NULL)
{
WeaveLogError(Ble, "%s: ep->%p", __FUNCTION__, ep);
err = BLE_ERROR_NO_ENDPOINTS;
SuccessOrExit(err);
}
data = ep->mWoBleTest.mCommandReceiveQueue;
ep->mWoBleTest.mCommandReceiveQueue = ep->mWoBleTest.mCommandReceiveQueue->DetachTail();
if (data == NULL)
{
WeaveLogError(Ble, "%s: ep->%p, data->%p", __FUNCTION__, ep, data);
err = BLE_ERROR_NO_ENDPOINTS;
SuccessOrExit(err);
}
err = BleTransportCommandMessage::Decode(*data, cmd);
SuccessOrExit(err);
WeaveLogDebugBleEndPoint(Ble, "%s: packet seq# %u, type %u, len %u", __FUNCTION__,
ep->mWoBle.RxPacketSeq(), cmd.CmdHdr.PacketType, data->DataLength());
switch (cmd.CmdHdr.PacketType) {
case kBleCommandType_TestAck:
ep->mWoBleTest.mCommandTestResult.AckCount++;
if (ep->mWoBleTest.mCommandUnderTest == WOBLE_TEST_TX)
{
ep->mWoBleTest.DoTxTiming(data, WOBLE_TX_DATA_ACK);
// Sender sends the next data packet
timerErr = systemLayer->StartTimer(0, DoTestDataSend, (void *)ep);
VerifyOrExit(timerErr == BLE_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
}
break;
case kBleCommandType_TestData:
switch (cmd.Payload.MsgTestData.Type)
{
case kDataType_START:
// reset counters if sender restarted
if (ep->mWoBleTest.mCommandUnderTest == WOBLE_TEST_RX)
{
memset(&ep->mWoBleTest.mCommandTestResult, 0, sizeof(BTCommandTypeTestResult));
ep->mWoBleTest.mCommandTestResult.PayloadSize = cmd.Payload.MsgTestData.Length;
}
// falls thru
case kDataType_CONTINUE:
if (ep->mWoBleTest.mCommandUnderTest == WOBLE_TEST_NONE)
{
memset(&ep->mWoBleTest.mCommandTestResult, 0, sizeof(BTCommandTypeTestResult));
ep->mWoBleTest.mCommandTestResult.PayloadSize = cmd.Payload.MsgTestData.Length;
ep->mWoBleTest.mCommandUnderTest = WOBLE_TEST_RX;
}
ep->mWoBleTest.mCommandTestResult.PacketCount++;
break;
case kDataType_END:
ep->mWoBleTest.mCommandTestResult.PacketCount++;
// falls thru
case kDataType_ABORT:
default:
WeaveLogDebugBleEndPoint(Ble, "%s: Test Ended with test data type %d",
__FUNCTION__, cmd.Payload.MsgTestData.Type);
err = ep->mWoBleTest.DoCommandTestResult(kBleCommandTestResult_Reply,
ep->mWoBleTest.mCommandTestResult.TestResult);
ep->mWoBleTest.mCommandUnderTest = WOBLE_TEST_NONE;
break;
}
needAck = cmd.Payload.MsgTestData.NeedAck;
break;
case kBleCommandType_TestRequest:
memcpy(&ep->mWoBleTest.mCommandTestRequest, &cmd.Payload.MsgTestRequest, sizeof(BTCommandTypeTestRequest));
WeaveLogError(Ble, "%s: PacketCount %lu, Duration %lu, TxGap %d, NeedAck %d, PayLoadSize %u",
__FUNCTION__, ep->mWoBleTest.mCommandTestRequest.PacketCount, ep->mWoBleTest.mCommandTestRequest.Duration,
ep->mWoBleTest.mCommandTestRequest.TxGap, ep->mWoBleTest.mCommandTestRequest.NeedAck,
ep->mWoBleTest.mCommandTestRequest.PayloadSize);
// Start the test
err = ep->mWoBleTest.HandleCommandTest(ep);
SuccessOrExit(err);
break;
case kBleCommandType_TestResult:
WeaveLogError(Ble, "\nIncoming TestResultOp : %d", cmd.Payload.MsgTestResult.TestResultOp);
if (cmd.Payload.MsgTestResult.TestResultOp == kBleCommandTestResult_Request)
{
err = ep->mWoBleTest.DoCommandTestResult(kBleCommandTestResult_Reply,
ep->mWoBleTest.mCommandTestResult.TestResult);
LogBleTestResult(&ep->mWoBleTest.mCommandTestResult);
SuccessOrExit(err);
}
else
LogBleTestResult(&cmd.Payload.MsgTestResult);
break;
case kBleCommandType_WobleMTU:
WeaveLogError(Ble, "\nIncoming WobleMTU : Op %u, Tx/Rx fragment size %u/%u", cmd.Payload.MsgWobleMTU.Op,
cmd.Payload.MsgWobleMTU.TxFragmentSize, cmd.Payload.MsgWobleMTU.RxFragmentSize);
if (cmd.Payload.MsgWobleMTU.Op == kCmdType_Set)
{
if (cmd.Payload.MsgWobleMTU.TxFragmentSize > 0)
ep->mWoBle.SetTxFragmentSize(cmd.Payload.MsgWobleMTU.TxFragmentSize);
if (cmd.Payload.MsgWobleMTU.RxFragmentSize > 0)
ep->mWoBle.SetRxFragmentSize(cmd.Payload.MsgWobleMTU.RxFragmentSize);
}
break;
case kBleCommandType_WobleWindowSize:
WeaveLogError(Ble, "\nIncoming WobleWindowSize : Op %u, Tx/Rx Window Sizes %u/%u",
cmd.Payload.MsgWobleWindowSize.Op,
cmd.Payload.MsgWobleWindowSize.TxWindowSize, cmd.Payload.MsgWobleWindowSize.RxWindowSize);
if (cmd.Payload.MsgWobleWindowSize.Op == kCmdType_Set)
{
if (cmd.Payload.MsgWobleWindowSize.TxWindowSize > 0)
ep->SetTxWindowSize(cmd.Payload.MsgWobleWindowSize.TxWindowSize);
if (cmd.Payload.MsgWobleWindowSize.RxWindowSize > 0)
ep->SetRxWindowSize(cmd.Payload.MsgWobleWindowSize.RxWindowSize);
}
break;
case kBleCommandType_TxTiming:
err = ep->mWoBleTest.HandleCommandTxTiming(ep, cmd.Payload.MsgTxTiming.Enable);
SuccessOrExit(err);
break;
default:
WeaveLogError(Ble, "%s: Control type %d is not yet supported", __FUNCTION__, cmd.CmdHdr.PacketType);
break;
}
if (needAck)
err = ep->mWoBleTest.DoCommandSendAck(ep->mWoBle.RxPacketSeq(), err);
exit:
if (err != BLE_NO_ERROR)
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (data != NULL)
{
PacketBuffer::Free(data);
}
return;
}
void WoBleTest::StopTestTimer()
{
if (GetFlag(mEp->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung) == true)
{
// Cancel any existing test timer.
mEp->mBle->mSystemLayer->CancelTimer(HandleTestClose, mEp);
SetFlag(mEp->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung, false);
}
}
void WoBleTest::HandleTestClose(Weave::System::Layer *systemLayer, void *appState, Weave::System::Error err)
{
BLEEndPoint *ep = static_cast<BLEEndPoint *>(appState);
// Check for event-based timer race condition.
if (GetFlag(ep->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung) == true)
{
WeaveLogError(Ble, "Test closed, ble ep %p", ep);
SetFlag(ep->mTimerStateFlags, BLEEndPoint::kTimerState_UnderTestTimerRunnung, false);
}
ep->mWoBleTest.mCommandUnderTest = WOBLE_TEST_NONE;
ep->mWoBleTest.mCommandTxTiming = false;
}
// Initialize the Tx histogram table, if reset is true, the old content is discarded
int WoBleTest::InitTxHistogram(char *file, int count, bool reset)
{
int err = 0;
VerifyOrExit(count > 0, err = -1);
// Check if it's called already
if (mTxHistogram.File != NULL)
fclose(mTxHistogram.File);
mTxHistogram.File = fopen(file, reset? "w" : "a");
VerifyOrExit(mTxHistogram.File, err = -2);
mTxHistogram.Record = (WoBleTxRecord *)malloc(count * sizeof(WoBleTxRecord));
VerifyOrExit(mTxHistogram.Record, err = -3);
mTxHistogram.Total = count;
mTxHistogram.Idx = 0;
exit:
if (err)
{
WeaveLogError(Ble, "%s: err %d\n", __FUNCTION__, err);
if (mTxHistogram.File)
{
fclose(mTxHistogram.File);
mTxHistogram.File = NULL;
}
}
return err;
}
// This function saves up to N records
void WoBleTest::SaveTxRecords(uint8_t N)
{
if (N == 0 || mTxHistogram.Total == 0 || mTxHistogram.File == NULL)
{
WeaveLogDebugBleEndPoint(Ble, "%s: Nothing to write", __FUNCTION__);
return;
}
if (N > mTxHistogram.Total)
N = mTxHistogram.Total;
WoBleTxRecord *record = &mTxHistogram.Record[0];
while (N-- && record->TxStart)
{
// The records in histogram file: "TxStartTime PayloadSize TxTime"
fprintf(mTxHistogram.File, "%u\t%u\t%u\n",
record->TxStart, record->Payload, record->TxTime);
record++;
}
// restart the recording
mTxHistogram.Idx = 0;
}
void WoBleTest::AddTxRecord(uint32_t txStart, uint16_t txTime, uint16_t size)
{
if (mTxHistogram.Total == 0)
{
WeaveLogDebugBleEndPoint(Ble, "%s: Tx Histogram was not enabled", __FUNCTION__);
return;
}
WoBleTxRecord *record = &mTxHistogram.Record[mTxHistogram.Idx++];
record->TxStart = txStart;
record->TxTime = txTime;
record->Payload = size;
if (mTxHistogram.Idx == 0 || mTxHistogram.Idx >= mTxHistogram.Total)
SaveTxRecords(mTxHistogram.Total);
}
void WoBleTest::DoneTxHistogram(bool final)
{
// Save the last records
if (mTxHistogram.Idx > 0)
SaveTxRecords(mTxHistogram.Idx);
if (final)
{
if (mTxHistogram.File)
fclose(mTxHistogram.File);
if (mTxHistogram.Record)
free((void *)mTxHistogram.Record);
memset(&mTxHistogram, 0, sizeof(mTxHistogram));
}
else
mTxHistogram.Idx = 0;
}
#endif /* WEAVE_ENABLE_WOBLE_TEST */
} /* namespace Ble */
} /* namespace nl */
#endif /* CONFIG_NETWORK_LAYER_BLE */