[tcat] implement new tcat General commands (#10526)

New General TLV's implemented:
- Get network name
- Get device id
- Get ext pan ID
- get provisioning URL
This commit is contained in:
Przemysław Bida
2024-08-21 19:21:10 -07:00
committed by GitHub
parent afac808a81
commit 8a0ea2b692
14 changed files with 508 additions and 141 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (434)
#define OPENTHREAD_API_VERSION (435)
/**
* @addtogroup api-instance
+34 -21
View File
@@ -67,9 +67,10 @@ extern "C" {
#define OT_TCAT_MAX_SERVICE_NAME_LENGTH \
15 ///< Maximum string length of a UDP or TCP service name (does not include null char).
#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement.
#define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code.
#define OT_TCAT_MAX_VENDORID_SIZE 5 ///< TCAT max size of vendor ID including null as termination.
#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement.
#define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code.
#define OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE 5 ///< TCAT max size of any type of advertised Device ID.
#define OT_TCAT_MAX_DEVICEID_SIZE 64 ///< TCAT max size of device ID.
/**
* Represents TCAT status code.
@@ -116,10 +117,10 @@ typedef enum otTcatCommandClass
} otTcatCommandClass;
/**
* Represents Device ID type.
* Represents Advertised Device ID type. (used during TCAT advertisement)
*
*/
typedef enum otTcatDeviceIdType
typedef enum otTcatAdvertisedDeviceIdType
{
OT_TCAT_DEVICE_ID_EMPTY = 0, ///< Vendor device ID type not set
OT_TCAT_DEVICE_ID_OUI24 = 1, ///< Vendor device ID type IEEE OUI-24
@@ -127,14 +128,24 @@ typedef enum otTcatDeviceIdType
OT_TCAT_DEVICE_ID_DISCRIMINATOR = 3, ///< Vendor device ID type Device Discriminator
OT_TCAT_DEVICE_ID_IANAPEN = 4, ///< Vendor device ID type IANA PEN
OT_TCAT_DEVICE_ID_MAX = 5, ///< Vendor device ID type size
} otTcatDeviceIdType;
} otTcatAdvertisedDeviceIdType;
typedef struct otTcatDeviceId
typedef struct otTcatAdvertisedDeviceId
{
otTcatDeviceIdType mDeviceIdType;
uint16_t mDeviceIdLen;
uint8_t mDeviceId[OT_TCAT_MAX_VENDORID_SIZE];
} otTcatDeviceId;
otTcatAdvertisedDeviceIdType mDeviceIdType;
uint16_t mDeviceIdLen;
uint8_t mDeviceId[OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE];
} otTcatAdvertisedDeviceId;
/**
* Represents General Device ID type.
*
*/
typedef struct otTcatGeneralDeviceId
{
uint16_t mDeviceIdLen;
uint8_t mDeviceId[OT_TCAT_MAX_DEVICEID_SIZE];
} otTcatGeneralDeviceId;
/**
* This structure represents a TCAT vendor information.
@@ -144,16 +155,18 @@ typedef struct otTcatDeviceId
*/
typedef struct otTcatVendorInfo
{
const char *mProvisioningUrl; ///< Provisioning URL path string
const char *mVendorName; ///< Vendor name string
const char *mVendorModel; ///< Vendor model string
const char *mVendorSwVersion; ///< Vendor software version string
const char *mVendorData; ///< Vendor specific data string
const char *mPskdString; ///< Vendor managed pre-shared key for device
const char *mInstallCode; ///< Vendor managed install code string
const otTcatDeviceId *mDeviceIds; /** Vendor managed device ID array.
(if NULL: device ID is set to EUI-64 in binary format)
Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */
const char *mProvisioningUrl; ///< Provisioning URL path string
const char *mVendorName; ///< Vendor name string
const char *mVendorModel; ///< Vendor model string
const char *mVendorSwVersion; ///< Vendor software version string
const char *mVendorData; ///< Vendor specific data string
const char *mPskdString; ///< Vendor managed pre-shared key for device
const char *mInstallCode; ///< Vendor managed install code string
const otTcatAdvertisedDeviceId *mAdvertisedDeviceIds; /** Vendor managed advertised device ID array.
Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */
const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array.
(if NULL: device ID is set to EUI-64 in binary format)*/
} otTcatVendorInfo;
/**
+87
View File
@@ -2,16 +2,103 @@
## Command List
- advid [#advid]
- devid [#devid]
- help [#help]
- start [#start]
- stop [#stop]
### advid
Displays currently set TCAT advertised ids.
```bash
tcat advid
type oui24, value: f378aa
Done
```
### advid ianapen \<id\>
Sets TCAT advertised ianapen id.
```bash
tcat advid ianapen f378aabb
Done
```
### advid oui24 \<id\>
Sets TCAT advertised oui24 id.
```bash
tcat advid oui24 f378aa
Done
```
### advid oui36 \<id\>
Sets TCAT advertised oui36 id.
```bash
tcat advid oui36 f378aabbcc
Done
```
### advid discriminator \<id\>
Sets TCAT advertised discriminator id.
```bash
tcat advid discriminator f378aabbdd
Done
```
### advid clear
Clears TCAT advertised id.
```bash
tcat advid clear
Done
```
### devid
Displays currently set TCAT device id.
```bash
tcat devid
abcd
Done
```
### devid \<id\>
Sets TCAT device id.
```bash
tcat devid abcd
Done
```
### devid clear
Clears TCAT device id.
```bash
tcat devid clear
Done
```
### help
print help
```bash
tcat help
advid
devid
help
start
stop
+73 -20
View File
@@ -32,6 +32,7 @@
#include "cli/cli_tcat.hpp"
#include "common/code_utils.hpp"
#include "common/error.hpp"
#include <openthread/ble_secure.h>
@@ -88,11 +89,26 @@ namespace ot {
namespace Cli {
otTcatDeviceId sVendorDeviceIds[OT_TCAT_DEVICE_ID_MAX];
otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX];
otTcatGeneralDeviceId sGeneralDeviceId;
const char kPskdVendor[] = "JJJJJJ";
const char kUrl[] = "dummy_url";
static bool IsDeviceIdSet(void)
{
bool ret = false;
for (const otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
{
if (vendorDeviceId.mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY)
{
ExitNow(ret = true);
}
}
exit:
return ret;
}
static void HandleBleSecureReceive(otInstance *aInstance,
const otMessage *aMessage,
int32_t aOffset,
@@ -121,30 +137,26 @@ static void HandleBleSecureReceive(otInstance *aInstance,
IgnoreReturnValue(otBleSecureFlush(aInstance));
}
template <> otError Tcat::Process<Cmd("vendorid")>(Arg aArgs[])
template <> otError Tcat::Process<Cmd("advid")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otTcatDeviceId devId;
static const char *const kVendorIdTypes[] = {"empty", "oui24", "oui36", "discriminator", "ianapen"};
otTcatAdvertisedDeviceId devId;
static const char *const kVendorIdTypes[] = {"clear", "oui24", "oui36", "discriminator", "ianapen"};
mVendorInfo.mDeviceIds = sVendorDeviceIds;
mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds;
if (aArgs[0].IsEmpty())
{
if (mVendorInfo.mDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY)
if (mVendorInfo.mAdvertisedDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY)
{
OutputLine("Set vendorIds:");
for (size_t i = 0; mVendorInfo.mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
OutputLine("Set advertisedIds:");
for (size_t i = 0; mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
{
OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mDeviceIds[i].mDeviceIdType]);
OutputBytesLine(const_cast<uint8_t *>(mVendorInfo.mDeviceIds[i].mDeviceId),
mVendorInfo.mDeviceIds[i].mDeviceIdLen);
OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType]);
OutputBytesLine(const_cast<uint8_t *>(mVendorInfo.mAdvertisedDeviceIds[i].mDeviceId),
mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdLen);
}
}
else
{
OutputLine("%s", kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]);
}
ExitNow();
}
@@ -166,7 +178,7 @@ template <> otError Tcat::Process<Cmd("vendorid")>(Arg aArgs[])
}
else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY])
{
for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds)
for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
{
vendorDeviceId.mDeviceIdType = OT_TCAT_DEVICE_ID_EMPTY;
vendorDeviceId.mDeviceIdLen = 0;
@@ -178,11 +190,11 @@ template <> otError Tcat::Process<Cmd("vendorid")>(Arg aArgs[])
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_VENDORID_SIZE * 2 + 1))
if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE * 2 + 1))
{
devId.mDeviceIdLen = OT_TCAT_MAX_VENDORID_SIZE;
devId.mDeviceIdLen = OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE;
SuccessOrExit(error = aArgs[1].ParseAsHexString(devId.mDeviceIdLen, devId.mDeviceId));
for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds)
for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
{
if (vendorDeviceId.mDeviceIdType == devId.mDeviceIdType ||
vendorDeviceId.mDeviceIdType == OT_TCAT_DEVICE_ID_EMPTY)
@@ -200,6 +212,35 @@ exit:
return error;
}
template <> otError Tcat::Process<Cmd("devid")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgs[0].IsEmpty())
{
if (sGeneralDeviceId.mDeviceIdLen != 0)
{
OutputLine("TCAT DeviceId:");
OutputBytesLine(sGeneralDeviceId.mDeviceId, sGeneralDeviceId.mDeviceIdLen);
}
ExitNow();
}
if (aArgs[0] == "clear")
{
ClearAllBytes(sGeneralDeviceId);
}
else
{
VerifyOrExit(aArgs[0].GetLength() < (OT_TCAT_MAX_DEVICEID_SIZE * 2 + 1), error = OT_ERROR_INVALID_ARGS);
sGeneralDeviceId.mDeviceIdLen = OT_TCAT_MAX_DEVICEID_SIZE;
SuccessOrExit(error = aArgs[0].ParseAsHexString(sGeneralDeviceId.mDeviceIdLen, sGeneralDeviceId.mDeviceId));
}
exit:
return error;
}
template <> otError Tcat::Process<Cmd("start")>(Arg aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
@@ -210,6 +251,16 @@ template <> otError Tcat::Process<Cmd("start")>(Arg aArgs[])
mVendorInfo.mPskdString = kPskdVendor;
mVendorInfo.mProvisioningUrl = kUrl;
if (IsDeviceIdSet())
{
mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds;
}
if (sGeneralDeviceId.mDeviceIdLen != 0)
{
mVendorInfo.mGeneralDeviceId = &sGeneralDeviceId;
}
otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_X509_CERT),
sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_PRIV_KEY),
sizeof(OT_CLI_TCAT_PRIV_KEY));
@@ -244,10 +295,12 @@ otError Tcat::Process(Arg aArgs[])
aCommandString, &Tcat::Process<Cmd(aCommandString)> \
}
static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop"), CmdEntry("vendorid")};
static constexpr Command kCommands[] = {CmdEntry("advid"), CmdEntry("devid"), CmdEntry("start"), CmdEntry("stop")};
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
#undef CmdEntry
otError error = OT_ERROR_NONE;
const Command *command;
+16 -8
View File
@@ -269,16 +269,24 @@ template Error Tlv::AppendUintTlv<uint8_t>(Message &aMessage, uint8_t aType, uin
template Error Tlv::AppendUintTlv<uint16_t>(Message &aMessage, uint8_t aType, uint16_t aValue);
template Error Tlv::AppendUintTlv<uint32_t>(Message &aMessage, uint8_t aType, uint32_t aValue);
Error Tlv::AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength)
Error Tlv::AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint16_t aLength)
{
Error error = kErrorNone;
Tlv tlv;
Error error = kErrorNone;
ExtendedTlv extTlv;
Tlv tlv;
OT_ASSERT(aLength <= Tlv::kBaseTlvMaxLength);
tlv.SetType(aType);
tlv.SetLength(aLength);
SuccessOrExit(error = aMessage.Append(tlv));
if (aLength > kBaseTlvMaxLength)
{
extTlv.SetType(aType);
extTlv.SetLength(aLength);
SuccessOrExit(error = aMessage.Append(extTlv));
}
else
{
tlv.SetType(aType);
tlv.SetLength(static_cast<uint8_t>(aLength));
SuccessOrExit(error = aMessage.Append(tlv));
}
VerifyOrExit(aLength > 0);
error = aMessage.AppendBytes(aValue, aLength);
+4 -1
View File
@@ -574,6 +574,9 @@ public:
/**
* Appends a TLV with a given type and value to a message.
*
* If the TLV length is longer than maximum base TLV size defined by `kBaseTlvMaxLength` then
* appends extended TLV.
*
* On success this method grows the message by the size of the TLV.
*
* @param[in] aMessage The message to append to.
@@ -585,7 +588,7 @@ public:
* @retval kErrorNoBufs Insufficient available buffers to grow the message.
*
*/
static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength);
static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint16_t aLength);
/**
* Appends a TLV with a given type and value to a message.
+111 -19
View File
@@ -33,6 +33,7 @@
#include "tcat_agent.hpp"
#include <openthread/tcat.h>
#include "meshcop/network_name.hpp"
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
@@ -55,7 +56,11 @@ RegisterLogModule("TcatAgent");
bool TcatAgent::VendorInfo::IsValid(void) const
{
return mProvisioningUrl == nullptr || IsValidUtf8String(mProvisioningUrl) || mPskdString != nullptr;
return (mProvisioningUrl == nullptr ||
(IsValidUtf8String(mProvisioningUrl) &&
(static_cast<uint8_t>(StringLength(mProvisioningUrl, kProvisioningUrlMaxLength)) <
kProvisioningUrlMaxLength))) &&
mPskdString != nullptr;
}
TcatAgent::TcatAgent(Instance &aInstance)
@@ -414,17 +419,24 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
error = HandleDecomission();
break;
case kTlvPing:
error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length);
if (error == kErrorNone)
{
response = true;
}
error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response);
break;
case kTlvGetNetworkName:
error = HandleGetNetworkName(aOutgoingMessage, response);
break;
case kTlvGetDeviceId:
error = HandleGetDeviceId(aOutgoingMessage, response);
break;
case kTlvGetExtendedPanID:
error = HandleGetExtPanId(aOutgoingMessage, response);
break;
case kTlvGetProvisioningURL:
error = HandleGetProvisioningUrl(aOutgoingMessage, response);
break;
default:
error = kErrorInvalidCommand;
}
}
if (!response)
{
StatusCode statusCode;
@@ -514,7 +526,8 @@ Error TcatAgent::HandleDecomission(void)
Error TcatAgent::HandlePing(const Message &aIncomingMessage,
Message &aOutgoingMessage,
uint16_t aOffset,
uint16_t aLength)
uint16_t aLength,
bool &response)
{
Error error = kErrorNone;
ot::ExtendedTlv extTlv;
@@ -535,6 +548,85 @@ Error TcatAgent::HandlePing(const Message &aIncomingMessage,
}
SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength));
response = true;
exit:
return error;
}
Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &response)
{
Error error = kErrorNone;
MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
#if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState);
#endif
SuccessOrExit(
error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength()));
response = true;
exit:
return error;
}
Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &response)
{
const uint8_t *deviceId;
uint16_t length = 0;
Mac::ExtAddress eui64;
Error error = kErrorNone;
if (mVendorInfo->mGeneralDeviceId != nullptr)
{
length = mVendorInfo->mGeneralDeviceId->mDeviceIdLen;
deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId;
}
if (length == 0)
{
Get<Radio>().GetIeeeEui64(eui64);
length = sizeof(Mac::ExtAddress);
deviceId = eui64.m8;
}
SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length));
response = true;
exit:
return error;
}
Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &response)
{
Error error;
VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload,
&Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId(), sizeof(ExtendedPanId)));
response = true;
exit:
return error;
}
Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response)
{
Error error = kErrorNone;
uint16_t length;
VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState);
length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength);
VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState);
error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length);
response = true;
exit:
return error;
@@ -585,31 +677,31 @@ Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementDat
aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE;
aLen++;
if (mVendorInfo->mDeviceIds != nullptr)
if (mVendorInfo->mAdvertisedDeviceIds != nullptr)
{
for (uint8_t i = 0; mVendorInfo->mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
{
switch (MapEnum(mVendorInfo->mDeviceIds[i].mDeviceIdType))
switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType))
{
case kTcatDeviceIdOui24:
SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24,
mVendorInfo->mDeviceIds[i].mDeviceIdLen,
mVendorInfo->mDeviceIds[i].mDeviceId);
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
break;
case kTcatDeviceIdOui36:
SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36,
mVendorInfo->mDeviceIds[i].mDeviceIdLen,
mVendorInfo->mDeviceIds[i].mDeviceId);
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
break;
case kTcatDeviceIdDiscriminator:
SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator,
mVendorInfo->mDeviceIds[i].mDeviceIdLen,
mVendorInfo->mDeviceIds[i].mDeviceId);
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
break;
case kTcatDeviceIdIanaPen:
SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen,
mVendorInfo->mDeviceIds[i].mDeviceIdLen,
mVendorInfo->mDeviceIds[i].mDeviceId);
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
break;
default:
break;
+15 -4
View File
@@ -168,6 +168,7 @@ public:
kTlvPing = 10, ///< TCAT ping request TLV
kTlvGetDeviceId = 11, ///< TCAT device ID query TLV
kTlvGetExtendedPanID = 12, ///< TCAT extended PAN ID query TLV
kTlvGetProvisioningURL = 13, ///< TCAT provisioning URL query TLV
kTlvPresentPskdHash = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash
kTlvPresentPskcHash = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash
kTlvPresentInstallCodeHash = 18, ///< TCAT commissioner rights elevation request TLV using install code
@@ -353,7 +354,15 @@ private:
Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage);
Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
Error HandleDecomission(void);
Error HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, uint16_t aLength);
Error HandlePing(const Message &aIncomingMessage,
Message &aOutgoingMessage,
uint16_t aOffset,
uint16_t aLength,
bool &response);
Error HandleGetNetworkName(Message &aOutgoingMessage, bool &response);
Error HandleGetDeviceId(Message &aOutgoingMessage, bool &response);
Error HandleGetExtPanId(Message &aOutgoingMessage, bool &response);
Error HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response);
Error HandleStartThreadInterface(void);
bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
@@ -362,8 +371,10 @@ private:
bool CanProcessTlv(uint8_t aTlvType) const;
CommandClass GetCommandClass(uint8_t aTlvType) const;
static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT;
static constexpr uint16_t kPingPayloadMaxLength = 512;
static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT;
static constexpr uint16_t kPingPayloadMaxLength = 512;
static constexpr uint16_t kProvisioningUrlMaxLength = 64;
static constexpr uint16_t kTcatMaxDeviceIdSize = OT_TCAT_MAX_DEVICEID_SIZE;
JoinerPskd mJoinerPskd;
const VendorInfo *mVendorInfo;
@@ -389,7 +400,7 @@ private:
DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo);
DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol);
DefineMapEnum(otTcatDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType);
DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType);
// Command class TLVs
typedef UintTlvInfo<MeshCoP::TcatAgent::kTlvResponseWithStatus, uint8_t> ResponseWithStatusTlv;
+29 -13
View File
@@ -32,44 +32,60 @@ source "tests/scripts/expect/_common.exp"
spawn_node 1 "cli"
switch_node 1
send "tcat vendorid ianapen f378\n"
send "tcat advid ianapen f378\n"
expect_line "Done"
send "tcat vendorid\n"
send "tcat advid\n"
expect_line "type ianapen, value: f378"
expect_line "Done"
send "tcat vendorid ianapen f378aabb\n"
send "tcat advid ianapen f378aabb\n"
expect_line "Done"
send "tcat vendorid\n"
send "tcat advid\n"
expect_line "type ianapen, value: f378aabb"
expect_line "Done"
send "tcat vendorid oui24 f378aa\n"
send "tcat advid oui24 f378aa\n"
expect_line "Done"
send "tcat vendorid\n"
send "tcat advid\n"
expect_line "type oui24, value: f378aa"
expect_line "Done"
send "tcat vendorid oui36 f378aabbcc\n"
send "tcat advid oui36 f378aabbcc\n"
expect_line "Done"
send "tcat vendorid\n"
send "tcat advid\n"
expect_line "type oui36, value: f378aabbcc"
expect_line "Done"
send "tcat vendorid discriminator f378aabbdd\n"
send "tcat advid discriminator f378aabbdd\n"
expect_line "Done"
send "tcat vendorid\n"
send "tcat advid\n"
expect_line "type discriminator, value: f378aabbdd"
expect_line "Done"
send "tcat vendorid empty\n"
send "tcat advid clear\n"
expect_line "Done"
send "tcat vendorid\n"
expect_line "empty"
send "tcat advid\n"
expect_line "Done"
send "tcat devid\n"
expect_line "Done"
send "tcat devid aaaa\n"
expect_line "aaaa"
expect_line "Done"
send "tcat devid\n"
expect_line "aaaa"
expect_line "Done"
send "tcat devid clear\n"
expect_line "Done"
send "tcat devid\n"
expect_line "Done"
+25
View File
@@ -38,6 +38,11 @@ expect_line "Done"
spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth"
set py_client "$spawn_id"
expect_line "Done"
send "network_name\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x06"
send "commission\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x00"
@@ -58,6 +63,26 @@ send "ping 512\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t512"
send "network_name\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t15"
expect_line "\tVALUE:\t0x4f70656e5468726561642d63363465"
send "device_id\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t8"
expect_line "\tVALUE:\t0x18b4300000000001"
send "ext_panid\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t8"
expect_line "\tVALUE:\t0xef1398c2fd504b67"
send "provisioning_url\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t9"
expect_line "\tVALUE:\t0x64756d6d795f75726c"
send "exit\n"
expect eof
+101 -52
View File
@@ -26,6 +26,7 @@
POSSIBILITY OF SUCH DAMAGE.
"""
from abc import abstractmethod
from ble.ble_connection_constants import BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, \
BBTC_RX_CHAR_UUID
from ble.ble_stream import BleStream
@@ -54,55 +55,112 @@ class HelpCommand(Command):
return CommandResultNone()
class HelloCommand(Command):
class BleCommand(Command):
@abstractmethod
def get_log_string(self) -> str:
pass
@abstractmethod
def prepare_data(self, context):
pass
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print(self.get_log_string())
data = self.prepare_data(context)
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
class HelloCommand(BleCommand):
def get_log_string(self) -> str:
return 'Sending hello world...'
def get_help_string(self) -> str:
return 'Send round trip "Hello world!" message.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print('Sending hello world...')
data = TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
def prepare_data(self, context):
return TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes()
class CommissionCommand(Command):
class CommissionCommand(BleCommand):
def get_log_string(self) -> str:
return 'Commissioning...'
def get_help_string(self) -> str:
return 'Update the connected device with current dataset.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
def prepare_data(self, context):
dataset: ThreadDataset = context['dataset']
print('Commissioning...')
dataset_bytes = dataset.to_bytes()
data = TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
return TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes()
class DecommissionCommand(Command):
class DecommissionCommand(BleCommand):
def get_log_string(self) -> str:
return 'Disabling Thread and decommissioning device...'
def get_help_string(self) -> str:
return 'Stop Thread interface and decommission device from current network.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print('Disabling Thread and decommissioning device...')
data = (TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes())
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
def prepare_data(self, context):
return TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes()
class GetDeviceIdCommand(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving device id.'
def get_help_string(self) -> str:
return 'Get unique identifier for the TCAT device.'
def prepare_data(self, context):
return TLV(TcatTLVType.GET_DEVICE_ID.value, bytes()).to_bytes()
class GetExtPanIDCommand(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving extended PAN ID.'
def get_help_string(self) -> str:
return 'Get extended PAN ID that is commissioned in the active dataset.'
def prepare_data(self, context):
return TLV(TcatTLVType.GET_EXT_PAN_ID.value, bytes()).to_bytes()
class GetProvisioningUrlCommand(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving provisioning url.'
def get_help_string(self) -> str:
return 'Get a URL for an application suited to commission the TCAT device.'
def prepare_data(self, context):
return TLV(TcatTLVType.GET_PROVISIONING_URL.value, bytes()).to_bytes()
class GetNetworkNameCommand(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving network name.'
def get_help_string(self) -> str:
return 'Get the Thread network name that is commissioned in the active dataset.'
def prepare_data(self, context):
return TLV(TcatTLVType.GET_NETWORK_NAME.value, bytes()).to_bytes()
class PingCommand(Command):
@@ -136,37 +194,28 @@ class PingCommand(Command):
return CommandResultTLV(tlv_response)
class ThreadStartCommand(Command):
class ThreadStartCommand(BleCommand):
def get_log_string(self) -> str:
return 'Enabling Thread...'
def get_help_string(self) -> str:
return 'Enable thread interface.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print('Enabling Thread...')
data = TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
def prepare_data(self, context):
return TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes()
class ThreadStopCommand(Command):
class ThreadStopCommand(BleCommand):
def get_log_string(self) -> str:
return 'Disabling Thread...'
def get_help_string(self) -> str:
return 'Disable thread interface.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print('Disabling Thread...')
data = TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
return CommandResultTLV(tlv_response)
def prepare_data(self, context):
return TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes()
class ThreadStateCommand(Command):
+6 -1
View File
@@ -29,7 +29,8 @@
import readline
import shlex
from ble.ble_stream_secure import BleStreamSecure
from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, PingCommand,
from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, GetDeviceIdCommand,
GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand,
ThreadStateCommand, ScanCommand)
from cli.dataset_commands import (DatasetCommand)
from dataset.dataset import ThreadDataset
@@ -44,6 +45,10 @@ class CLI:
'hello': HelloCommand(),
'commission': CommissionCommand(),
'decommission': DecommissionCommand(),
'device_id': GetDeviceIdCommand(),
'ext_panid': GetExtPanIDCommand(),
'provisioning_url': GetProvisioningUrlCommand(),
'network_name': GetNetworkNameCommand(),
'ping': PingCommand(),
'dataset': DatasetCommand(),
'thread': ThreadStateCommand(),
+2 -1
View File
@@ -28,6 +28,7 @@
from tlv.tlv import TLV
from tlv.tcat_tlv import TcatTLVType
from ble.ble_stream_secure import BleStreamSecure
from abc import ABC, abstractmethod
@@ -57,7 +58,7 @@ class Command(ABC):
return await self._subcommands[args[0]].execute(args[1:], context)
@abstractmethod
async def execute_default(self, args, context) -> CommandResult:
async def execute_default(self, args, context):
pass
@abstractmethod
+4
View File
@@ -31,8 +31,12 @@ from enum import Enum
class TcatTLVType(Enum):
RESPONSE_W_STATUS = 0x01
RESPONSE_W_PAYLOAD = 0x02
GET_NETWORK_NAME = 0x08
DISCONNECT = 0x09
PING = 0x0A
GET_DEVICE_ID = 0x0B
GET_EXT_PAN_ID = 0x0C
GET_PROVISIONING_URL = 0x0D
ACTIVE_DATASET = 0x20
DECOMMISSION = 0x60
APPLICATION = 0x82