blob: 2e94b9a3c6d0fb539c2fa333c134395f78041389 [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 implements types and an object for the Weave over
* Bluetooth Low Energy (WoBLE) byte-stream, connection-oriented
* adaptation of Weave for point-to-point Bluetooth Low Energy
* (BLE) links.
*
*/
#include <BleLayer/BleConfig.h>
#if CONFIG_NETWORK_LAYER_BLE
#include <BleLayer/WoBle.h>
#if WEAVE_ENABLE_WOBLE_TEST
#include <BleLayer/WoBleTest.h>
#endif
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/CodeUtils.h>
// Define below to enable extremely verbose BLE-specific debug logging.
#undef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
#define WeaveLogDebugBtpEngine(MOD, MSG, ...) WeaveLogError(MOD, MSG, ## __VA_ARGS__)
#else
#define WeaveLogDebugBtpEngine(MOD, MSG, ...)
#endif
#define NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE 1 // Size in bytes of enocded BTP fragment header flag bits
#define NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE 1 // Size in bytes of encoded BTP sequence number
#define NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE 1 // Size in bytes of encoded BTP fragment acknowledgement number
#define NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE 2 // Size in byte of encoded BTP total fragmented message length
#define NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE)
#define NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
#define NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + \
NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
namespace nl {
namespace Ble {
static inline void IncSeqNum(SequenceNumber_t &a_seq_num)
{
a_seq_num = 0xff & ((a_seq_num) + 1);
}
static inline bool DidReceiveData(uint8_t rx_flags)
{
return (GetFlag(rx_flags, WoBle::kHeaderFlag_StartMessage) == true ||
GetFlag(rx_flags, WoBle::kHeaderFlag_ContinueMessage) == true ||
GetFlag(rx_flags, WoBle::kHeaderFlag_EndMessage) == true);
}
static void PrintBufDebug(PacketBuffer *buf)
{
#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
uint8_t *b = buf->Start();
for (int i = 0; i < buf->DataLength(); i++)
{
WeaveLogError(Ble, "\t%02x", b[i]);
}
#endif
}
const uint16_t WoBle::sDefaultFragmentSize = 20; // 23-byte minimum ATT_MTU - 3 bytes for ATT operation header
const uint16_t WoBle::sMaxFragmentSize = 128; // Size of write and indication characteristics
BLE_ERROR WoBle::Init(void *an_app_state, bool expect_first_ack)
{
mAppState = an_app_state;
mRxState = kState_Idle;
mRxBuf = NULL;
mRxNewestUnackedSeqNum = 0;
mRxOldestUnackedSeqNum = 0;
mRxFragmentSize = sDefaultFragmentSize;
mTxState = kState_Idle;
mTxBuf = NULL;
mTxFragmentSize = sDefaultFragmentSize;
mRxCharCount = 0;
mRxPacketCount = 0;
mTxCharCount = 0;
mTxPacketCount = 0;
mTxNewestUnackedSeqNum = 0;
mTxOldestUnackedSeqNum = 0;
#if WEAVE_ENABLE_WOBLE_TEST
mTxPacketType = kType_Data; // Default WoBle Data packet
mRxPacketType = kType_Data; // Default WoBle Data packet
#endif
if (expect_first_ack == true)
{
mTxNextSeqNum = 1;
mExpectingAck = true;
mRxNextSeqNum = 0;
}
else
{
mTxNextSeqNum = 0;
mExpectingAck = false;
mRxNextSeqNum = 1;
}
return BLE_NO_ERROR;
}
SequenceNumber_t WoBle::GetAndIncrementNextTxSeqNum()
{
SequenceNumber_t ret = mTxNextSeqNum;
// If not already expecting ack...
if (mExpectingAck == false)
{
mExpectingAck = true;
mTxOldestUnackedSeqNum = mTxNextSeqNum;
}
// Update newest unacknowledged sequence number.
mTxNewestUnackedSeqNum = mTxNextSeqNum;
// Increment mTxNextSeqNum.
IncSeqNum(mTxNextSeqNum);
return ret;
}
SequenceNumber_t WoBle::GetAndRecordRxAckSeqNum()
{
SequenceNumber_t ret = mRxNewestUnackedSeqNum;
mRxNewestUnackedSeqNum = mRxNextSeqNum;
mRxOldestUnackedSeqNum = mRxNextSeqNum;
return ret;
}
bool WoBle::HasUnackedData() const
{
return (mRxOldestUnackedSeqNum != mRxNextSeqNum);
}
bool WoBle::IsValidAck(SequenceNumber_t ack_num) const
{
WeaveLogDebugBtpEngine(Ble, "entered IsValidAck, ack = %u, oldest = %u, newest = %u", ack_num, mTxOldestUnackedSeqNum, mTxNewestUnackedSeqNum);
// Return false if not awaiting any ack.
if (mExpectingAck == false)
{
WeaveLogDebugBtpEngine(Ble, "unexpected ack is invalid");
return false;
}
// Assumption: maximum valid sequence number equals maximum value of SequenceNumber_t.
if (mTxNewestUnackedSeqNum >= mTxOldestUnackedSeqNum) // If current unacked interval does NOT wrap...
{
return (ack_num <= mTxNewestUnackedSeqNum && ack_num >= mTxOldestUnackedSeqNum);
}
else // Else, if current unacked interval DOES wrap...
{
return (ack_num <= mTxNewestUnackedSeqNum || ack_num >= mTxOldestUnackedSeqNum);
}
}
BLE_ERROR WoBle::HandleAckReceived(SequenceNumber_t ack_num)
{
BLE_ERROR err = BLE_NO_ERROR;
WeaveLogDebugBtpEngine(Ble, "entered HandleAckReceived, ack_num = %u", ack_num);
// Ensure ack_num falls within range of ack values we're expecting.
VerifyOrExit(IsValidAck(ack_num) == true, err = BLE_ERROR_INVALID_ACK);
if (mTxNewestUnackedSeqNum == ack_num) // If ack is for newest outstanding unacknowledged fragment...
{
mTxOldestUnackedSeqNum = ack_num;
// All oustanding fragments have been acknowledged.
mExpectingAck = false;
}
else // If ack is valid, but not for newest oustanding unacknowledged fragment...
{
// Update newest unacknowledged fragment to one past that which was just acknowledged.
mTxOldestUnackedSeqNum = ack_num;
IncSeqNum(mTxOldestUnackedSeqNum);
}
exit:
return err;
}
// Calling convention:
// EncodeStandAloneAck may only be called if data arg is commited for immediate, synchronous subsequent transmission.
BLE_ERROR WoBle::EncodeStandAloneAck(PacketBuffer *data)
{
BLE_ERROR err = BLE_NO_ERROR;
uint8_t *characteristic;
// Ensure enough headroom exists for the lower BLE layers.
VerifyOrExit(data->EnsureReservedSize(WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE) == true, err = BLE_ERROR_NO_MEMORY);
// Ensure enough space for standalone ack payload.
VerifyOrExit(data->MaxDataLength() >= NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE, err = BLE_ERROR_NO_MEMORY);
characteristic = data->Start();
// Since there's no preexisting message payload, we can write BTP header without adjusting data start pointer.
characteristic[0] = kHeaderFlag_FragmentAck;
// Acknowledge most recently received sequence number.
characteristic[1] = GetAndRecordRxAckSeqNum();
WeaveLogDebugBtpEngine(Ble, "===> encoded stand-alone ack = %u", characteristic[1]);
// Include sequence number for stand-alone ack itself.
characteristic[2] = GetAndIncrementNextTxSeqNum();
// Set ack payload data length.
data->SetDataLength(NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE);
exit:
return err;
}
// Calling convention:
// WoBle does not retain ownership of reassembled messages, layer above needs to free when done.
//
// WoBle does not reset itself on error. Upper layer should free outbound message and inbound reassembly buffers
// if there is a problem.
// HandleCharacteristicReceived():
//
// Non-NULL characteristic data arg is always either designated as or appended to the message reassembly buffer,
// or freed if it holds a stand-alone ack. In all cases, caller must clear its reference to data arg when this
// function returns.
//
// Upper layer must immediately clean up and reinitialize protocol engine if returned err != BLE_NO_ERROR.
BLE_ERROR WoBle::HandleCharacteristicReceived(PacketBuffer *data, SequenceNumber_t &receivedAck, bool &didReceiveAck)
{
BLE_ERROR err = BLE_NO_ERROR;
uint8_t rx_flags = 0;
uint8_t cursor = 0;
uint8_t *characteristic = data->Start();
VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
mRxCharCount++;
// Get header flags, always in first byte.
rx_flags = characteristic[cursor++];
#if WEAVE_ENABLE_WOBLE_TEST
if (GetFlag(rx_flags, kHeaderFlag_CommandMessage) == true)
SetRxPacketType(kType_Control);
else
SetRxPacketType(kType_Data);
#endif
didReceiveAck = GetFlag(rx_flags, kHeaderFlag_FragmentAck);
// Get ack number, if any.
if (didReceiveAck == true)
{
receivedAck = characteristic[cursor++];
err = HandleAckReceived(receivedAck);
SuccessOrExit(err);
}
// Get sequence number.
mRxNewestUnackedSeqNum = characteristic[cursor++];
// Verify that received sequence number is the next one we'd expect.
VerifyOrExit(mRxNewestUnackedSeqNum == mRxNextSeqNum, err = BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER);
// Increment next expected rx sequence number.
IncSeqNum(mRxNextSeqNum);
// If fragment was stand-alone ack, we're done here; no payload for message reassembler.
if (DidReceiveData(rx_flags) == false)
{
// Free stand-alone ack buffer.
PacketBuffer::Free(data);
data = NULL;
ExitNow();
}
// Truncate the incoming fragment length by the mRxFragmentSize as the negotiated
// mRxFragnentSize may be smaller than the characteristic size.
data->SetDataLength(nl::Weave::min(data->DataLength(), mRxFragmentSize));
WeaveLogDebugBtpEngine(Ble, ">>> BTP reassembler received data:");
PrintBufDebug(data);
if (mRxState == kState_Idle)
{
// Verify StartMessage header flag set.
VerifyOrExit(rx_flags & kHeaderFlag_StartMessage, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
mRxLength = (characteristic[(cursor + 1)] << 8) | characteristic[cursor];
cursor += 2;
mRxState = kState_InProgress;
data->SetStart(&(characteristic[cursor]));
// Create a new buffer for use as the Rx re-assembly area.
mRxBuf = PacketBuffer::New();
VerifyOrExit(mRxBuf != NULL, err = BLE_ERROR_NO_MEMORY);
mRxBuf->AddToEnd(data);
mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
data = NULL;
}
else if (mRxState == kState_InProgress)
{
// Verify StartMessage header flag NOT set, since we're in the middle of receiving a message.
VerifyOrExit((rx_flags & kHeaderFlag_StartMessage) == 0, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
// Verify ContinueMessage or EndMessage header flag set.
VerifyOrExit((rx_flags & kHeaderFlag_ContinueMessage) || (rx_flags & kHeaderFlag_EndMessage),
err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
// Add received fragment to reassembled message buffer.
data->SetStart(&(characteristic[cursor]));
mRxBuf->AddToEnd(data);
mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
data = NULL;
// For now, limit WoBle message size to max length of 1 pbuf, as we do for Weave messages sent via IP.
// TODO add support for WoBle messages longer than 1 pbuf
VerifyOrExit(mRxBuf->Next() == NULL, err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);
}
else
{
err = BLE_ERROR_REASSEMBLER_INCORRECT_STATE;
ExitNow();
}
if (rx_flags & kHeaderFlag_EndMessage)
{
// Trim remainder, if any, of received PacketBuffer based on sender-specified length of reassembled message.
int padding = mRxBuf->DataLength() - mRxLength;
if (padding > 0)
{
mRxBuf->SetDataLength(mRxLength);
}
// Ensure all received fragments add up to sender-specified total message size.
VerifyOrExit(mRxBuf->DataLength() == mRxLength, err = BLE_ERROR_REASSEMBLER_MISSING_DATA);
// We've reassembled the entire message.
mRxState = kState_Complete;
mRxPacketCount++;
}
exit:
if (err != BLE_NO_ERROR)
{
mRxState = kState_Error;
// Dump protocol engine state, plus header flags and received data length.
WeaveLogError(Ble, "HandleCharacteristicReceived failed, err = %d, rx_flags = %u", err, rx_flags);
if (didReceiveAck == true)
{
WeaveLogError(Ble, "With rx'd ack = %u", receivedAck);
}
if (mRxBuf != NULL)
{
WeaveLogError(Ble, "With rx buf data length = %u", mRxBuf->DataLength());
}
LogState();
if (data != NULL)
{
// Tack received data onto rx buffer, to be freed when end point resets protocol engine on close.
if (mRxBuf != NULL)
{
mRxBuf->AddToEnd(data);
}
else
{
mRxBuf = data;
}
}
}
return err;
}
PacketBuffer *WoBle::RxPacket()
{
return mRxBuf;
}
bool WoBle::ClearRxPacket()
{
if (mRxState == kState_Complete)
{
mRxState = kState_Idle;
mRxBuf = NULL;
// do not reset mRxNextSeqNum
return true;
}
return false;
}
// Calling convention:
// May only be called if data arg is commited for immediate, synchronous subsequent transmission.
// Returns false on error. Caller must free data arg on error.
bool WoBle::HandleCharacteristicSend(PacketBuffer *data, bool send_ack)
{
uint8_t *characteristic;
mTxCharCount++;
if (send_ack && !HasUnackedData())
{
WeaveLogError(Ble, "HandleCharacteristicSend: send_ack true, but nothing to acknowledge.");
return false;
}
if (mTxState == kState_Idle)
{
if (data == NULL)
{
return false;
}
mTxBuf = data;
mTxState = kState_InProgress;
mTxLength = mTxBuf->DataLength();
WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send whole message:");
PrintBufDebug(data);
// Determine fragment header size.
uint8_t header_size = (send_ack == true) ? NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE :
(NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
// Ensure enough headroom exists for the BTP header, and any headroom needed by the lower BLE layers.
if (!mTxBuf->EnsureReservedSize(header_size + WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE))
{
// handle error
WeaveLogError(Ble, "HandleCharacteristicSend: not enough headroom");
mTxState = kState_Error;
mTxBuf = NULL; // Avoid double-free after assignment above, as caller frees data on error.
return false;
}
// prepend header.
characteristic = mTxBuf->Start();
characteristic -= header_size;
mTxBuf->SetStart(characteristic);
uint8_t cursor = 1; // first position past header flags byte
characteristic[0] = kHeaderFlag_StartMessage;
#if WEAVE_ENABLE_WOBLE_TEST
if (TxPacketType() == kType_Control)
SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
#endif
if (send_ack == true)
{
SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
characteristic[cursor++] = GetAndRecordRxAckSeqNum();
WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor-1]);
}
characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
characteristic[cursor++] = mTxLength & 0xff;
characteristic[cursor++] = mTxLength >> 8;
if ((mTxLength + cursor) <= mTxFragmentSize)
{
mTxBuf->SetDataLength(mTxLength + cursor);
mTxLength = 0;
SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
mTxState = kState_Complete;
mTxPacketCount++;
}
else
{
mTxBuf->SetDataLength(mTxFragmentSize);
mTxLength -= mTxFragmentSize - cursor;
}
WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send first fragment:");
PrintBufDebug(data);
}
else if (mTxState == kState_InProgress)
{
if (data != NULL)
{
return false;
}
// advance past the previous fragment
characteristic = mTxBuf->Start();
characteristic += mTxFragmentSize;
// prepend header
characteristic -= (send_ack == true) ? NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE :
(NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
mTxBuf->SetStart(characteristic);
uint8_t cursor = 1; // first position past header flags byte
characteristic[0] = kHeaderFlag_ContinueMessage;
#if WEAVE_ENABLE_WOBLE_TEST
if (TxPacketType() == kType_Control)
SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
#endif
if (send_ack == true)
{
SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
characteristic[cursor++] = GetAndRecordRxAckSeqNum();
WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor-1]);
}
characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
if ((mTxLength + cursor) <= mTxFragmentSize)
{
mTxBuf->SetDataLength(mTxLength + cursor);
mTxLength = 0;
SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
mTxState = kState_Complete;
mTxPacketCount++;
}
else
{
mTxBuf->SetDataLength(mTxFragmentSize);
mTxLength -= mTxFragmentSize - cursor;
}
WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send additional fragment:");
PrintBufDebug(mTxBuf);
}
else
{
// Invalid tx state.
return false;
}
return true;
}
PacketBuffer *WoBle::TxPacket()
{
return mTxBuf;
}
bool WoBle::ClearTxPacket()
{
if (mTxState == kState_Complete)
{
mTxState = kState_Idle;
mTxBuf = NULL;
// do not reset mTxNextSeqNum
return true;
}
return false;
}
void WoBle::LogState() const
{
WeaveLogError(Ble, "mAppState: %p", mAppState);
WeaveLogError(Ble, "mRxFragmentSize: %d", mRxFragmentSize);
WeaveLogError(Ble, "mRxState: %d", mRxState);
WeaveLogError(Ble, "mRxBuf: %p", mRxBuf);
WeaveLogError(Ble, "mRxNextSeqNum: %d", mRxNextSeqNum);
WeaveLogError(Ble, "mRxNewestUnackedSeqNum: %d", mRxNewestUnackedSeqNum);
WeaveLogError(Ble, "mRxOldestUnackedSeqNum: %d", mRxOldestUnackedSeqNum);
WeaveLogError(Ble, "mRxCharCount: %d", mRxCharCount);
WeaveLogError(Ble, "mRxPacketCount: %d", mRxPacketCount);
WeaveLogError(Ble, "mTxFragmentSize: %d", mTxFragmentSize);
WeaveLogError(Ble, "mTxState: %d", mTxState);
WeaveLogError(Ble, "mTxBuf: %p", mTxBuf);
WeaveLogError(Ble, "mTxNextSeqNum: %d", mTxNextSeqNum);
WeaveLogError(Ble, "mTxNewestUnackedSeqNum: %d", mTxNewestUnackedSeqNum);
WeaveLogError(Ble, "mTxOldestUnackedSeqNum: %d", mTxOldestUnackedSeqNum);
WeaveLogError(Ble, "mTxCharCount: %d", mTxCharCount);
WeaveLogError(Ble, "mTxPacketCount: %d", mTxPacketCount);
}
void WoBle::LogStateDebug() const
{
#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
LogState();
#endif
}
//
// Unit Testing
//
bool WoBle::UnitTest1()
{
PacketBuffer *first_packet;
SequenceNumber_t rcvd_ack;
bool did_rcv_ack;
bool rc = true;
Init(NULL, false);
first_packet = PacketBuffer::New(5);
if (first_packet->AvailableDataLength() < 5)
{
PacketBuffer::Free(first_packet);
return false;
}
first_packet->SetDataLength(5, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL)
{
PacketBuffer::Free(first_packet);
return false;
}
data[0] = 0x05;
data[1] = 1;
data[2] = 1;
data[3] = 0;
data[4] = 0xff; // payload
if (!HandleCharacteristicReceived(first_packet, rcvd_ack, did_rcv_ack))
rc = false;
if (mRxState != kState_Complete)
rc = false;
Init(NULL, false);
PacketBuffer::Free(first_packet);
return rc;
}
bool WoBle::UnitTest2()
{
PacketBuffer *first_packet;
PacketBuffer *second_packet;
SequenceNumber_t rcvd_ack;
bool did_rcv_ack;
bool rc = true;
Init(NULL, false);
first_packet = PacketBuffer::New(10);
first_packet->SetDataLength(5, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL)
{
PacketBuffer::Free(first_packet);
return false;
}
data[0] = kHeaderFlag_StartMessage;
data[1] = 1;
data[2] = 2;
data[3] = 0;
data[4] = 0xfe; // payload
if (!HandleCharacteristicReceived(first_packet, rcvd_ack, did_rcv_ack))
{
PacketBuffer::Free(first_packet);
return false;
}
if (mRxState != kState_InProgress)
{
PacketBuffer::Free(first_packet);
rc = false;
}
second_packet = PacketBuffer::New(3);
second_packet->SetDataLength(3, NULL);
data = second_packet->Start();
if (data == NULL)
{
PacketBuffer::Free(first_packet);
PacketBuffer::Free(second_packet);
return false;
}
data[0] = kHeaderFlag_EndMessage;
data[1] = 2;
data[4] = 0xff; // payload
if (!HandleCharacteristicReceived(second_packet, rcvd_ack, did_rcv_ack))
{
rc = false;
}
if (mRxState != kState_Complete)
{
rc = false;
}
Init(NULL, false);
PacketBuffer::Free(first_packet);
return rc;
}
bool WoBle::UnitTest3()
{
PacketBuffer *first_packet;
PacketBuffer *second_packet;
PacketBuffer *last_packet;
SequenceNumber_t rcvd_ack;
bool did_rcv_ack;
bool rc = true;
Init(NULL, false);
first_packet = PacketBuffer::New(10);
first_packet->SetDataLength(5, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
return false;
}
data[0] = kHeaderFlag_StartMessage;
data[1] = 1;
data[2] = 3;
data[3] = 0;
data[4] = 0xfd; // payload
if (!HandleCharacteristicReceived(first_packet, rcvd_ack, did_rcv_ack)) {
PacketBuffer::Free(first_packet);
return false;
}
if (mRxState != kState_InProgress) {
PacketBuffer::Free(first_packet);
rc = false;
}
second_packet = PacketBuffer::New(3);
second_packet->SetDataLength(3, NULL);
data = second_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
PacketBuffer::Free(second_packet);
return false;
}
data[0] = kHeaderFlag_ContinueMessage;
data[1] = 2;
data[4] = 0xfe; // payload
if (!HandleCharacteristicReceived(second_packet, rcvd_ack, did_rcv_ack)) {
PacketBuffer::Free(first_packet);
PacketBuffer::Free(second_packet);
return false;
}
if (mRxState != kState_InProgress) {
PacketBuffer::Free(first_packet);
PacketBuffer::Free(second_packet);
return false;
}
last_packet = PacketBuffer::New(3);
last_packet->SetDataLength(3, NULL);
data = last_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
PacketBuffer::Free(last_packet);
return false;
}
data[0] = kHeaderFlag_EndMessage;
data[1] = 3;
data[4] = 0xff; // payload
if (!HandleCharacteristicReceived(last_packet, rcvd_ack, did_rcv_ack)) {
rc = false;
}
if (mRxState != kState_Complete) {
rc = false;
}
Init(NULL, false);
PacketBuffer::Free(first_packet);
return rc;
}
bool WoBle::UnitTest4()
{
PacketBuffer *first_packet;
bool rc = true;
Init(NULL, false);
first_packet = PacketBuffer::New(10);
first_packet->SetDataLength(1, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
return false;
}
data[0] = 0xff; // payload
if ((!HandleCharacteristicSend(first_packet, false)) ||
(first_packet->DataLength() != 5) ||
(mTxState != kState_Complete)) {
PacketBuffer::Free(first_packet);
return false;
}
Init(NULL, false);
PacketBuffer::Free(first_packet);
return rc;
}
bool WoBle::UnitTest5()
{
PacketBuffer *first_packet;
Init(NULL, false);
first_packet = PacketBuffer::New(140);
first_packet->SetDataLength(140, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
return false;
}
for (int i = 0; i < 140; i++) {
data[i] = i; // payload
}
if (!HandleCharacteristicSend(first_packet, false) ||
(first_packet->DataLength() != 128) ||
(mTxState != kState_InProgress)) {
PacketBuffer::Free(first_packet);
return false;
}
if ((!HandleCharacteristicSend(NULL, false)) ||
(mTxState != kState_Complete) ||
(first_packet->DataLength() != 18)) {
PacketBuffer::Free(first_packet);
return false;
}
Init(NULL, false);
PacketBuffer::Free(first_packet);
return true;
}
// Send 300-byte payload.
// First packet: 4 byte header + 124 byte payload
// Second packet: 2 byte header + 126 byte payload
// Third packet: 2 byte header + 50 byte payload
bool WoBle::UnitTest6()
{
PacketBuffer *first_packet;
bool rc = true;
Init(NULL, false);
first_packet = PacketBuffer::New(300);
first_packet->SetDataLength(300, NULL);
uint8_t *data = first_packet->Start();
if (data == NULL) {
PacketBuffer::Free(first_packet);
return false;
}
for (int i = 0; i < 300; i++) {
data[i] = i; // payload
}
if (!HandleCharacteristicSend(first_packet, false) ||
(first_packet->DataLength() != 128) ||
(mTxState != kState_InProgress)) {
PacketBuffer::Free(first_packet);
return false;
}
if (!HandleCharacteristicSend(NULL, false) ||
(mTxState != kState_InProgress) ||
(first_packet->DataLength() != 128)) {
PacketBuffer::Free(first_packet);
return false;
}
if (!HandleCharacteristicSend(NULL, false) ||
(mTxState != kState_Complete) ||
(first_packet->DataLength() != 52)) {
PacketBuffer::Free(first_packet);
return false;
}
Init(NULL, false);
PacketBuffer::Free(first_packet);
return rc;
}
bool WoBle::UnitTest()
{
bool rc;
rc = UnitTest1();
rc = rc && UnitTest2();
rc = rc && UnitTest3();
rc = rc && UnitTest4();
rc = rc && UnitTest5();
rc = rc && UnitTest6();
if (!rc) {
WeaveDie();
}
return rc;
}
} /* namespace Ble */
} /* namespace nl */
#endif /* CONFIG_NETWORK_LAYER_BLE */