matter源码分析之初始化
1. 前言
Matter的代码由Apple,Google,Silicon Labs,Amazon等大厂贡献,且由C++编写,当然不能错过学习大厂编写代码的机会,而且实现细节也吸引着我,所以开始阅读源码。
Matter的环境搭建见之前的文章Matter初体验。
Matter源码庞大,编译后程序大小达到55M,为了更好的知道程序流转,使用VS Code的GDB可视化调试功能辅助阅读。在工程的lanuch.json
的configurations
中新增以下内容,并选择Chiptool debug
后按F5开始调试。
{
"name": "Chiptool debug",
"type": "cppdbg",
"request": "launch",
"program": "/home/matter/connectedhomeip/out/debug/standalone/chip-tool",
"args": ["onoff", "toggle", "1234", "1"],//传参
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
建议在阅读源码前,按需食用以下知识:
2. main入口
文件examples/chip-tool/main.cpp
int main(int argc, char * argv[])
{
/**注意与Command类区分*/
Commands commands;
/**对命令的预处理,注册命令*/
registerCommandsDiscover(commands);
registerCommandsPayload(commands);
registerCommandsPairing(commands);
registerCommandsReporting(commands);
registerCommandsTests(commands);
registerClusters(commands);/**注册cluster*/
return commands.Run(argc, argv);/**初始化,程序运行*/
}
2.1 注册cluster
zzz_generated/chip-tool/zap-generated/cluster/Commands.h:57893
如:registerClusterOnOff
,同时注册command
命令,这些命令都使用类进行了封装,其基类为Command
,都在Commands.h
文件中
void registerClusterOnOff(Commands & commands)
{
const char * clusterName = "OnOff";
commands_list clusterCommands = {
make_unique<OnOffOff>(), //
......
make_unique<OnOffToggle>(), //
......
make_unique<ReportOnOffClusterRevision>(), //
};
commands.Register(clusterName, clusterCommands);
}
make_unique
是一个模版,用于分配内存
template <typename T, typename... Args>
std::unique_ptr<Command> make_unique(Args &&... args)
{
return std::unique_ptr<Command>(new T(std::forward<Args>(args)...));
}
2.1.1 cluster命令示例
class OnOffToggle : public ModelCommand
{
public:
OnOffToggle() : ModelCommand("toggle") { ModelCommand::AddArguments(); }
CHIP_ERROR SendCommand(ChipDevice * device, uint8_t endpointId) override
{
ChipLogProgress(chipTool, "Sending cluster (0x00000006) command (0x00000002) on endpoint %" PRIu8, endpointId);
return chip::Controller::InvokeCommand(device, this, OnDefaultSuccess, OnDefaultFailure, endpointId, mRequest);
}
private:
chip::app::Clusters::OnOff::Commands::Toggle::Type mRequest;
};
2.2 文件和内存初始化部分调用流程
commands.Run(argc, argv)
-->int Commands::Run(int argc, char ** argv)
-->err = chip::Platform::MemoryInit();/**内存分配*/
-->err = mStorage.Init();/**配置文件的加载及解析chip_tool_config.ini*/
-->err = RunCommand(argc, argv);/**见下方分析*/
2.3 文件初始化
定义如下,默认参数为空,且程序没有传入,虽然可以传入,但是命名的只是后缀,如果想改存储目录,还是需要修改代码
CHIP_ERROR Init(const char * name = nullptr);
std::string GetFilename(const char * name)
{
if (name == nullptr)
{
return "/tmp/chip_tool_config.ini";
}
return "/tmp/chip_tool_config." + std::string(name) + ".ini";
}
所以在目录下有时会看到两个配置文件,只有其中一个是正在使用的。比如我的就是使用的chip_tool_config.alpha.ini
。定义在examples/chip-tool/commands/common/CHIPCommand.h
中,暂且就叫发行版本定义。
constexpr const char kIdentityAlpha[] = "alpha";
constexpr const char kIdentityBeta[] = "beta";
constexpr const char kIdentityGamma[] = "gamma";
2.4 Command部分解析
examples/chip-tool/commands/common/Commands.cpp:117
//执行 ./chip-tool onoff toggle 1234 1
CHIP_ERROR Commands::RunCommand(int argc, char ** argv)
{
std::map<std::string, CommandsVector>::iterator cluster;
Command * command = nullptr;
......
cluster = GetCluster(argv[1]);//根据cluster名字获取指针
......
if (!IsGlobalCommand(argv[2]))//read write report是global的
{
command = GetCommand(cluster->second, argv[2]);//取出
if (command == nullptr)
{
ChipLogError(chipTool, "Unknown command: %s", argv[2]);
ShowCluster(argv[0], argv[1], cluster->second);
return CHIP_ERROR_INVALID_ARGUMENT;
}
}
else{
......
}
return command->Run();
}
最重要的就是command->Run();
之后的流程。
2.5 总调用流程
/**CHIPCommand::Run()*/
command->Run()
-->mDefaultStorage.Init()/**配置文件读取*/
-->PersistentStorage::Init(const char * name)
-->mFabricStorage.Initialize(&mDefaultStorage)/**Fabric storage 读取*/
-->CHIP_ERROR Initialize(PersistentStorageDelegate * storage)
-->listenport初始化 /**见下方 IPV6部分端口初始化 节详解*/
/**初始化大类1 DeviceControllerFactory::GetInstance().Init(factoryInitParams)*/
-->CHIP_ERROR DeviceControllerFactory::Init(FactoryInitParams params)
-->CHIP_ERROR err = InitSystemState(params)/**见下方第3章详解*/
/**DeviceLayer::PlatformMgr().InitChipStack();*/
-->inline CHIP_ERROR PlatformManager::InitChipStack()
/**平台初始化,包括信号接管,GDBus IPC通信、Glib 事件线程、netlink初始化*/
-->CHIP_ERROR PlatformManagerImpl::_InitChipStack()
/**除下放功能,还有条件变量和互斥锁的初始化*/
-->CHIP_ERROR GenericPlatformManagerImpl_POSIX<ImplClass>::_InitChipStack()
/**LOG、CHIP system layer、Configuration Manager、CHIP Inet layer、
* BLE manager、Connectivity Manager object、NFC Manager。
* 因为需要文件和设备操作,所以需要sudo权限 */
-->CHIP_ERROR GenericPlatformManagerImpl<ImplClass>::_InitChipStack()
-->stateParams.transportMgr->Init(...)/**见下方3.2详解*/
-->stateParams.fabricTable->Init(mFabricStorage)/**见下方3.3详解*/
-->LoadFromStorage(fabric)
-->stateParams.sessionMgr->Init()/**GlobalEncryptedMessageCounter init*/
/**初始化大类2 InitializeCommissioner(GetIdentity(), CurrentCommissionerIndex())*/
-->CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId fabricId)/**见下方第4章详解*/
可以看出东西还是比较多的,下面开始详细分析。
3. System State Initialized的初始化
本部分主要是对系统资源、配置文件读取等的初始化
3.1 扩展阅读
配置文件,系统相关初始化比较简单,可以自行阅读。
3.2 stateParams.transportMgr->Init
这个类中的方法,如初始化网络等,内部比较复杂,尤其是UDP类的多层构造和获取,比较绕
{
stateParams.transportMgr = chip::Platform::New<DeviceTransportMgr>();/**new*/
ReturnErrorOnFailure(stateParams.transportMgr->Init(
Transport::UdpListenParameters(stateParams.inetLayer).SetAddressType(Inet::IPAddressType::kIPv6).SetListenPort(mListenPort)
#if INET_CONFIG_ENABLE_IPV4
,
Transport::UdpListenParameters(stateParams.inetLayer).SetAddressType(Inet::IPAddressType::kIPv4).SetListenPort(mListenPort)
#endif
#if CONFIG_NETWORK_LAYER_BLE
,
Transport::BleListenParameters(stateParams.bleLayer)
#endif
));
}
stateParams.transportMgr->Init
是new出来的DeviceTransportMgr
,我们来看看这是什么。
在命名空间chip
中,DeviceTransportMgr
使用using关键字修饰,作用类似typedef
,实际是TransportMgr
类的。
namespace chip {
......
using DeviceTransportMgr = TransportMgr<Transport::UDP /* IPv6 */
#if INET_CONFIG_ENABLE_IPV4
,
Transport::UDP /* IPv4 */
#endif
#if CONFIG_NETWORK_LAYER_BLE
,
Transport::BLE<kMaxDeviceTransportBlePendingPackets> /* BLE */
#endif
>;
......
}
我看到类TransportMgr
是可变参数模板,这样我们就能理解stateParams.transportMgr->Init
的传参了
template <typename... TransportTypes>/**前向声明*/
class TransportMgr : public TransportMgrBase
{
public:
template <typename... Args>//基本定义
CHIP_ERROR Init(Args &&... transportInitArgs)
{
ReturnErrorOnFailure(mTransport.Init(this, std::forward<Args>(transportInitArgs)...));
return TransportMgrBase::Init(&mTransport);
}
......
}
3.2.1 本部分调用流程
CHIP_ERROR Init(Args &&... transportInitArgs)
-->mTransport.Init(this, std::forward<Args>(transportInitArgs)...)
-->CHIP_ERROR Init(RawTransportDelegate * delegate, Args &&... args)
-->return InitImpl(delegate, std::forward<Args>(args)...);
-->CHIP_ERROR InitImpl(RawTransportDelegate * delegate, InitArg && arg, Rest &&... rest)
-->CHIP_ERROR UDP::Init(UdpListenParameters & params)/**见下方UDP初始化节*/
-->CHIP_ERROR TransportMgrBase::Init(Transport::Base * transport)
src/transport/raw/Tuple.h:219
递归函数方式展开参数包
/**
* Initialization method that forwards arguments for initialization to each of the underlying
* transports.
*
* Transports are assumed to have an Init call with EXACTLY one argument. This method MUST initialize
* all underlying transports.
*
* @param delegate the delegate to handle messages.
* @param args initialization arguments, forwarded as-is to the underlying transports.
*/
template <typename... Args, typename std::enable_if<(sizeof...(Args) == sizeof...(TransportTypes))>::type * = nullptr>
CHIP_ERROR Init(RawTransportDelegate * delegate, Args &&... args)
{
return InitImpl(delegate, std::forward<Args>(args)...);
}
/**
* Recursive init implementation iterating through transport members
*
* Given a set of arguments 'a1, a2, a3, ... aN' will call an Init method on the last N
* transports.
*
* Method is expected to be called initially with exactly sizeof(TransportTypes) to initialize
* all transports.
*
* @param arg the next initialize argument to pass to the transport Init method
* @param rest tail arguments to be passed to the rest of transport Init methods.
*/
template <typename InitArg, typename... Rest>
CHIP_ERROR InitImpl(RawTransportDelegate * delegate, InitArg && arg, Rest &&... rest)
{
auto transport = &std::get<sizeof...(TransportTypes) - sizeof...(Rest) - 1>(mTransports);/**怎么转换到UDP类的?*/
CHIP_ERROR err = transport->Init(std::forward<InitArg>(arg));
if (err != CHIP_NO_ERROR)
{
return err;
}
transport->SetDelegate(delegate);
return InitImpl(delegate, std::forward<Rest>(rest)...);/**递归函数方式展开参数包*/
}
3.2.2 蓝牙部分
显示声明构造函数
class BleListenParameters
{
public:
explicit BleListenParameters(Ble::BleLayer * layer) : mLayer(layer) {}
......
}
3.2.3 IPV6部分
端口的由来:
CHIP_ERROR CHIPCommand::Run()
{
......
factoryInitParams.listenPort = static_cast<uint16_t>(mDefaultStorage.GetListenPort() + CurrentCommissionerIndex());
......
}
其中mDefaultStorage.GetListenPort()
方法中计算端口号,默认为5541。接着调用SyncGetKeyValue
从字段ListenPort
中读取,不存在将会直接返回。
uint16_t PersistentStorage::GetListenPort()
{
CHIP_ERROR err = CHIP_NO_ERROR;
// By default chip-tool listens on CHIP_PORT + 1. This is done in order to avoid
// having 2 servers listening on CHIP_PORT when one runs an accessory server locally.
uint16_t chipListenPort = static_cast<uint16_t>(CHIP_PORT + 1);
char value[6];
uint16_t size = static_cast<uint16_t>(sizeof(value));
err = SyncGetKeyValue(kPortKey, value, size);
......
return chipListenPort;
}
接着计算偏移,是根据发行版本不同进行的偏移,可以方便测试。最终得到端口5542
uint16_t CHIPCommand::CurrentCommissionerIndex()
{
uint16_t index = 0;
std::string name = GetIdentity();
if (name.compare(kIdentityAlpha) == 0)/** alpha */
{
index = kIdentityAlphaFabricId;/** 1 */
}
else if (name.compare(kIdentityBeta) == 0)
{
index = kIdentityBetaFabricId;/** 2 */
}
else if (name.compare(kIdentityGamma) == 0)
{
index = kIdentityGammaFabricId;/** 3 */
}
VerifyOrDieWithMsg(index != 0, chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(),
kIdentityAlpha, kIdentityBeta, kIdentityGamma);
return index;
}
std::string CHIPCommand::GetIdentity()
{
std::string name = mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha;/** alpha */
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0)
{
ChipLogError(chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(), kIdentityAlpha,
kIdentityBeta, kIdentityGamma);
chipDie();
}
return name;
}
UDP初始化:
上面说到InitImpl
方法内transport->Init(std::forward<InitArg>(arg))
的实际调用是UDP::Init
,我们看看UDP初始化做了什么。
src/transport/raw/UDP.cpp:40
CHIP_ERROR UDP::Init(UdpListenParameters & params)
-->CHIP_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, InterfaceId intfId)
-->CHIP_ERROR UDPEndPointImplSockets::BindImpl(IPAddressType addressType, const IPAddress & addr, uint16_t port, InterfaceId interface)
-->CHIP_ERROR UDPEndPointImplSockets::BindImpl(IPAddressType addressType, const IPAddress & addr, uint16_t port, InterfaceId interface)
-->CHIP_ERROR IPv6Bind(int socket, const IPAddress & address, uint16_t port, InterfaceId interface)
-->CHIP_ERROR UDPEndPoint::Listen(OnMessageReceivedFunct onMessageReceived, OnReceiveErrorFunct onReceiveError, void * appState)
-->CHIP_ERROR UDPEndPointImplSockets::ListenImpl()/**里面的实现没有看懂*/
3.3 stateParams.fabricTable->Init
定义
CHIP_ERROR FabricTable::Init(FabricStorage * storage)
-->LoadFromStorage(fabric)/**加载信息*/
定义了最多能接受的Fabric数量
#define CHIP_CONFIG_MAX_DEVICE_ADMINS 16
CHIP_ERROR FabricInfo::LoadFromStorage(FabricStorage * storage)
{
char key[kKeySize];
/**结果 key=Fabric1 */
ReturnErrorOnFailure(GenerateKey(mFabric, key, sizeof(key)));/**kFabricTableKeyPrefix 固定前缀*/
......
/**原型SimpleFabricStorage::SyncLoad(FabricIndex fabricIndex, const char * key, void * buffer, uint16_t & size)
* 没有读取到会退出,要是读取到则会进行下面的数据处理
*/
SuccessOrExit(err = storage->SyncLoad(mFabric, key, info, infoSize));
......
SetRootCert(ByteSpan(info->mRootCert, rootCertLen))
......
}
CHIP_ERROR SimpleFabricStorage::SyncLoad(FabricIndex fabricIndex, const char * key, void * buffer, uint16_t & size)
{
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
char formattedKey[MAX_KEY_SIZE] = "";
/**最终结果:F01/Fabric1 */
ReturnErrorOnFailure(formatKey(fabricIndex, MutableCharSpan(formattedKey, MAX_KEY_SIZE), key));
/**去配置文件中找,我的配置文件中是没有的*/
return mStorage->SyncGetKeyValue(formattedKey, buffer, size);
};
4. Commissioner的初始化
InitializeCommissioner(GetIdentity(), CurrentCommissionerIndex())
//原型CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId fabricId)
入参就是"alpha"
和1
4.1 调用流程
InitializeCommissioner(GetIdentity(), CurrentCommissionerIndex())
-->ephemeralKey.Initialize()/**P256v1加密初始化*/
-->mCommissionerStorage.Init(key.c_str())/**配置文件初始化*/
/**读取配置文件中的pairkey,如果不存在,则创建,可以自定义key
* 加密由Openssl提供,是库的方式使用,计算出value,将value进行base64后存入
*/
-->mOpCredsIssuer.Initialize()
/**生成NOC、ICAC
* 从chip_tool_config.alpha.ini中读取
* ExampleOpCredsCAKey,ExampleOpCredsICAKey,ExampleCARootCert
*/
-->mOpCredsIssuer.GenerateNOCChainAfterValidation()
/**CHIP_ERROR DeviceControllerFactory::SetupCommissioner(SetupParams params, DeviceCommissioner & commissioner)*/
-->DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, *(commissioner.get()))
-->InitSystemState()/**已经初始化完成,内部赋值指针后退出*/
-->PopulateInitParams(commissionerParams, params);/**参数赋值*/
/**CHIP_ERROR DeviceCommissioner::Init(CommissionerInitParams params)*/
-->commissioner.Init(commissionerParams)
/**DeviceController::Init(ControllerInitParams params)*/
-->DeviceController::Init(params)
-->系统初始化验证
/**InitDataModelHandler(chip::Messaging::ExchangeManager * exchangeManager)*/
-->InitDataModelHandler(params.systemState->ExchangeMgr())
/**ZCL ZAP数据模型的初始化*/
-->emberAfEndpointConfigure()
-->emberAfInit(exchangeManager)
-->ProcessControllerNOCChain(params)
-->/**Convert standard X.509 certificate to CHIP certificate.*/
-->/**Storage of NOC and Root Cert in FabricInfo.*/
/**FabricInfo::SetFabricInfo(FabricInfo & newFabric)
* 证书链的构建和验证
*/
-->fabric->SetFabricInfo(newFabric)
-->VerifyCredentials(newFabric.mNOCCert, newFabric.mICACert, validContext, mOperationalId, mFabricId, pubkey)
-->certificates.LoadCert(mRootCert, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor))
-->Enter the certificate structure
-->reader.EnterContainer(containerType)
/** Decode and convert the To-Be-Signed (TBS) portion of the CHIP certificate into X.509 DER encoded form.*/
-->DecodeConvertTBSCert(reader, writer, cert)
/** Verify the cert has both the Subject Key Id and Authority Key Id extensions present. */
-->cert.mCertFlags.HasAll(CertFlags::kExtPresent_SubjectKeyId, CertFlags::kExtPresent_AuthKeyId)
/** Decode the certificate's signature */
-->DecodeECDSASignature(reader, cert)
/** Verify no more elements in the certificate. */
-->reader.VerifyEndOfContainer()
-->//检查是否证书已经存在,空间检查,new保存
/**检查加载的证书链 (noc -> icac -> mRootCert)*/
-->certificates.FindValidCert(nocSubjectDN, nocSubjectKeyId, context, &resultCert)
/**从 TLV 编码形式的操作证书中提取Node ID and Fabric ID*/
-->ExtractNodeIdFabricIdFromOpCert(certificates.GetLastCert()[0], &nodeId, &fabricId)
-->GetCompressedId(fabricId, nodeId, &nocPeerId)
/**获取root pubkey*/
-->P256PublicKey rootPubkey(GetRootPubkey());
/*GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id,MutableByteSpan & out_compressed_fabric_id)
* 从节点的根公钥和结构 ID 计算用于操作发现服务记录的压缩结构标识符。 成功后,out_compressed_fabric_id 的大小将恰好为 kCompressedFabricIdentifierSize。
* 还不太理解怎么算的
*/
-->GenerateCompressedFabricId(rootPubkey, fabricId, compressedFabricIdSpan)
-->设置compressedFabricId和nodeId
-->设置正式、FabricId、nodeId等
-->参数指针赋值
void InitDataModelHandler(chip::Messaging::ExchangeManager * exchangeManager)
中就是芯科的ZCL部分代码,C语言编写
证书的读取作为一个单独功能,使用TLV存储,经过转换存储在ChipCertificateData
中
-->certificates.LoadCert(mRootCert, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor))
/**Enter the certificate structure*/
-->reader.EnterContainer(containerType)
/** Decode and convert the To-Be-Signed (TBS) portion of the CHIP certificate into X.509 DER encoded form.*/
-->DecodeConvertTBSCert(reader, writer, cert)
/** Verify the cert has both the Subject Key Id and Authority Key Id extensions present. */
-->cert.mCertFlags.HasAll(CertFlags::kExtPresent_SubjectKeyId, CertFlags::kExtPresent_AuthKeyId)
/** Decode the certificate's signature */
-->DecodeECDSASignature(reader, cert)
/** Verify no more elements in the certificate. */
-->reader.VerifyEndOfContainer()
-->//检查是否证书已经存在,空间检查,new保存
/**证书存储数据结构*/
struct ChipCertificateData
{
ChipCertificateData();
~ChipCertificateData();
void Clear();
bool IsEqual(const ChipCertificateData & other) const;
ByteSpan mCertificate; /**< Original raw buffer data. */
ChipDN mSubjectDN; /**< Certificate Subject DN. */
ChipDN mIssuerDN; /**< Certificate Issuer DN. */
CertificateKeyId mSubjectKeyId; /**< Certificate Subject public key identifier. */
CertificateKeyId mAuthKeyId; /**< Certificate Authority public key identifier. */
uint32_t mNotBeforeTime; /**< Certificate validity: Not Before field. */
uint32_t mNotAfterTime; /**< Certificate validity: Not After field. */
P256PublicKeySpan mPublicKey; /**< Certificate public key. */
uint16_t mPubKeyCurveOID; /**< Public key Elliptic Curve CHIP OID. */
uint16_t mPubKeyAlgoOID; /**< Public key algorithm CHIP OID. */
uint16_t mSigAlgoOID; /**< Certificate signature algorithm CHIP OID. */
BitFlags<CertFlags> mCertFlags; /**< Certificate data flags. */
BitFlags<KeyUsageFlags> mKeyUsageFlags; /**< Certificate key usage extensions flags. */
BitFlags<KeyPurposeFlags> mKeyPurposeFlags; /**< Certificate extended key usage extensions flags. */
uint8_t mPathLenConstraint; /**< Basic constraint: path length. */
P256ECDSASignatureSpan mSignature; /**< Certificate signature. */
uint8_t mTBSHash[Crypto::kSHA256_Hash_Length]; /**< Certificate TBS hash. */
};
5. 总结
Matter的初始化流程还是比较复杂的,在阅读后仍然有一些不太懂的地方或者错误的地方,有知道的大神希望能够指点指点。
代码编写使用了很多C++11的特性,尤其广泛使用可变参数模板,代码规范度很高,是很好的学习材料。
下面将阅读配对入网过程。