diff --git a/src/Makefile b/src/Makefile index 70d49eaa..e075c0b9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,67 +9,58 @@ TARGET_SHARED_LIBNAMES = libezsp libezsp_MODULE_NAME = test_log -libezsp_SRCS = domain/zbmessage/zigbee-message.cpp \ - domain/zbmessage/zclheader.cpp \ - domain/zbmessage/zclframecontrol.cpp \ - domain/zbmessage/apsoption.cpp \ - domain/zbmessage/aps.cpp \ - domain/zbmessage/zdp-enum.cpp \ - domain/zbmessage/green-power-device.cpp \ - domain/zbmessage/green-power-frame.cpp \ - domain/zigbee-tools/green-power-sink.cpp \ - domain/zbmessage/green-power-sink-table-entry.cpp \ - domain/zigbee-tools/zigbee-messaging.cpp \ - domain/zigbee-tools/zigbee-networking.cpp \ - domain/zigbee-tools/green-power-sink-table.cpp \ - domain/ezsp-protocol/ezsp-enum.cpp \ - domain/ezsp-protocol/get-network-parameters-response.cpp \ - domain/ezsp-protocol/struct/ember-key-struct.cpp \ - domain/ezsp-protocol/struct/ember-network-parameters.cpp \ - domain/ezsp-protocol/struct/ember-child-data-struct.cpp \ - domain/ezsp-protocol/struct/ember-gp-address-struct.cpp \ - domain/ezsp-dongle.cpp \ - domain/ash.cpp \ - spi/GenericAsyncDataInputObservable.cpp \ - spi/raritan/RaritanUartDriver.cpp \ - spi/raritan/RaritanTimerFactory.cpp \ - spi/raritan/RaritanTimer.cpp \ - spi/raritan/RaritanEventLoop.cpp \ - spi/raritan/RaritanLogger.cpp \ +# SRC_PATH should point to the src/ folder containing source code for this library (can be overridden from environment) +SRC_PATH ?= . +SRC_DOMAIN_PATH ?= $(SRC_PATH)/domain +SRC_SPI_PATH ?= $(SRC_PATH)/spi + +include libezsp.mk.inc + +libezsp_SRCS = $(LIBEZSP_RARITAN_SRC) libezsp_LIBS = -lpp_base # Header files to install libezspincludedir = $(includedir)/include/local/ezsp -libezspinclude_HEADERS = domain/zbmessage/zdp-enum.h \ -domain/ezsp-protocol/ezsp-enum.h \ -domain/ezsp-protocol/struct/ember-key-struct.h \ -domain/ezsp-protocol/get-network-parameters-response.h \ -domain/byte-manip.h \ -domain/ash.h \ -domain/ezsp-dongle.h \ -domain/ezsp-dongle-observer.h \ +libezspinclude_HEADERS = \ domain/zigbee-tools/zigbee-networking.h \ -domain/zigbee-tools/zigbee-messaging.h \ -domain/zbmessage/green-power-frame.h \ domain/zigbee-tools/green-power-sink.h \ -domain/zigbee-tools/green-power-sink-table.h \ -domain/zbmessage/green-power-sink-table-entry.h \ +domain/zigbee-tools/zigbee-messaging.h \ domain/green-power-observer.h \ -domain/zbmessage/zigbee-message.h \ +domain/ezsp-dongle-observer.h \ +domain/ezsp-dongle.h \ +domain/ash.h \ +domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.h \ +domain/ezsp-protocol/struct/ember-key-struct.h \ +domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.h \ +domain/ezsp-protocol/struct/ember-gp-sink-table-entry-struct.h \ +domain/ezsp-protocol/struct/ember-child-data-struct.h \ +domain/ezsp-protocol/struct/ember-network-parameters.h \ +domain/ezsp-protocol/struct/ember-gp-address-struct.h \ +domain/ezsp-protocol/get-network-parameters-response.h \ +domain/ezsp-protocol/ezsp-enum.h \ +domain/zbmessage/green-power-device.h \ +domain/zbmessage/green-power-frame.h \ +domain/zbmessage/gp-pairing-command-option-struct.h \ domain/zbmessage/aps.h \ -domain/zbmessage/apsoption.h \ -domain/zbmessage/zclheader.h \ domain/zbmessage/zclframecontrol.h \ -domain/ezsp-protocol/struct/ember-network-parameters.h \ +domain/zbmessage/zclheader.h \ +domain/zbmessage/apsoption.h \ +domain/zbmessage/green-power-sink-table-entry.h \ +domain/zbmessage/gpd-commissioning-command-payload.h \ +domain/zbmessage/zdp-enum.h \ +domain/zbmessage/zigbee-message.h \ +spi/raritan/RaritanLogger.h \ spi/raritan/RaritanTimerFactory.h \ +spi/raritan/RaritanTimer.h \ spi/raritan/RaritanEventLoop.h \ spi/raritan/RaritanUartDriver.h \ +spi/ILogger.h \ spi/ITimerFactory.h \ -spi/ITimerFactory.h \ -spi/IUartDriver.h \ spi/ITimer.h \ +spi/IUartDriver.h \ +spi/GenericLogger.h \ spi/GenericAsyncDataInputObservable.h \ spi/IAsyncDataInputObserver.h \ diff --git a/src/domain/custom-aes.cpp b/src/domain/custom-aes.cpp new file mode 100644 index 00000000..d2132343 --- /dev/null +++ b/src/domain/custom-aes.cpp @@ -0,0 +1,298 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 2013, Igor Saric. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + */ + +#include "custom-aes.h" + +#include + +CAes::CAes() : + context() +{ +} + + +// algorithm +void CAes::xor_block( void *d, const void *s ) +{ + (static_cast(d))[ 0] ^= (static_cast(s))[ 0]; + (static_cast(d))[ 1] ^= (static_cast(s))[ 1]; + (static_cast(d))[ 2] ^= (static_cast(s))[ 2]; + (static_cast(d))[ 3] ^= (static_cast(s))[ 3]; +} + +void CAes::copy_and_key( void *d, const void *s, const void *k ) +{ + (static_cast(d))[ 0] = (static_cast(s))[ 0] ^ (static_cast(k))[ 0]; + (static_cast(d))[ 1] = (static_cast(s))[ 1] ^ (static_cast(k))[ 1]; + (static_cast(d))[ 2] = (static_cast(s))[ 2] ^ (static_cast(k))[ 2]; + (static_cast(d))[ 3] = (static_cast(s))[ 3] ^ (static_cast(k))[ 3]; +} + +void CAes::add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +void CAes::shift_sub_rows( uint8_t st[N_BLOCK] ) +{ + uint8_t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +void CAes::inv_shift_sub_rows( uint8_t st[N_BLOCK] ) +{ + uint8_t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +void CAes::mix_sub_columns( uint8_t dt[N_BLOCK] ) +{ + uint8_t st[N_BLOCK]; + block_copy(st, dt); + + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); + } + +void CAes::inv_mix_sub_columns( uint8_t dt[N_BLOCK] ) +{ + uint8_t st[N_BLOCK]; + block_copy(st, dt); + + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); + } + +// Set the cipher key for the pre-keyed version +void CAes::aes_set_key( const uint8_t key[AES_KEY_SIZE] ) +{ + uint8_t cc, rc, hi; + aes_context *ctx = &context; + // 128 only + uint8_t keylen = AES_KEY_SIZE; + + block_copy_nn(ctx->ksch, key, keylen); + hi = static_cast(static_cast(keylen + 28) << 2); + ctx->rnd = static_cast((hi >> 4) - 1); + for( cc = keylen, rc = 1; cc < hi; cc = static_cast(cc + 4) ) + { uint8_t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if( cc % keylen == 0 ) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = static_cast(f2(rc)); + } + else if( keylen > 24 && cc % keylen == 16 ) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = static_cast(cc - keylen); + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } +} + +// Encrypt a single block of 16 bytes +aes_result CAes::aes_encrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK] ) +{ + if( context.rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, context.ksch ); + + for( r = 1 ; r < context.rnd ; ++r ) + { + mix_sub_columns( s1 ); + add_round_key( s1, context.ksch + r * N_BLOCK); + } + shift_sub_rows( s1 ); + copy_and_key( out, s1, context.ksch + r * N_BLOCK ); + } + else + return false; + return true; +} + +// CBC encrypt a number of blocks (input and return an IV) +/* +aes_result CAes::aes_cbc_encrypt(const unsigned char *in, unsigned char *out, unsigned long size, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + if (size % 16 != 0) + return EXIT_FAILURE; + + unsigned long n_block = size / 16; + + while(n_block--) + { + xor_block(iv, in); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + memcpy(out, iv, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} +*/ +// Decrypt a single block of 16 bytes +/* +aes_result CAes::aes_decrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for( r = ctx->rnd ; --r ; ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } + copy_and_key( out, s1, ctx->ksch ); + } + else + return -1; + return 0; +} +*/ + +// CBC decrypt a number of blocks (input and return an IV) +/* +aes_result CAes::aes_cbc_decrypt( const unsigned char *in, unsigned char *out, unsigned long size, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + if (size % 16 != 0) + return EXIT_FAILURE; + + unsigned long n_block = size / 16; + + while (n_block--) + { + uint8_t tmp[N_BLOCK]; + + memcpy(tmp, in, N_BLOCK); + if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + memcpy(iv, tmp, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} +*/ diff --git a/src/domain/custom-aes.h b/src/domain/custom-aes.h new file mode 100644 index 00000000..9cf1d85d --- /dev/null +++ b/src/domain/custom-aes.h @@ -0,0 +1,248 @@ +/** + * @brief Utility functions for aes 256 + */ +/* + --------------------------------------------------------------------------- + Copyright (c) 2013, Igor Saric. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include + +#define AES_KEY_SIZE 16 + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#define s_box(x) sbox[(x)] +#define is_box(x) isbox[(x)] +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] + +#define block_copy_nn(d, s, l) memcpy(d, s, l) +#define block_copy(d, s) memcpy(d, s, N_BLOCK) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + + +typedef bool aes_result; + +static const uint8_t sbox[256] = sb_data(f1); +static const uint8_t isbox[256] = isb_data(f1); + +static const uint8_t gfm2_sbox[256] = sb_data(f2); +static const uint8_t gfm3_sbox[256] = sb_data(f3); + +static const uint8_t gfmul_9[256] = mm_data(f9); +static const uint8_t gfmul_b[256] = mm_data(fb); +static const uint8_t gfmul_d[256] = mm_data(fd); +static const uint8_t gfmul_e[256] = mm_data(fe); + +typedef struct +{ + uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint8_t rnd; +}aes_context; + +class CAes +{ + public: + + CAes(); + + void aes_set_key( const uint8_t key[AES_KEY_SIZE] ); + aes_result aes_encrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK] ); + + // encryption functions + // \todo rewrite with class context + /* + aes_result aes_decrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ); + aes_result aes_cbc_encrypt(const unsigned char *in, unsigned char *out, unsigned long size, unsigned char iv[N_BLOCK], const aes_context ctx[1] ); + aes_result aes_cbc_decrypt(const unsigned char *in, unsigned char *out, unsigned long size, unsigned char iv[N_BLOCK], const aes_context ctx[1] ); + */ + + // helper functions + void xor_block( void *d, const void *s ); + + + private: + // context for this instance + aes_context context; + + // helper functions + void copy_and_key( void *d, const void *s, const void *k ); + void add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] ); + void shift_sub_rows( uint8_t st[N_BLOCK] ); + void inv_shift_sub_rows( uint8_t st[N_BLOCK] ); + void mix_sub_columns( uint8_t dt[N_BLOCK] ); + void inv_mix_sub_columns( uint8_t dt[N_BLOCK] ); +}; diff --git a/src/domain/ezsp-protocol/ezsp-enum.cpp b/src/domain/ezsp-protocol/ezsp-enum.cpp index 7aac4cd8..7b227254 100644 --- a/src/domain/ezsp-protocol/ezsp-enum.cpp +++ b/src/domain/ezsp-protocol/ezsp-enum.cpp @@ -43,7 +43,7 @@ std::string CEzspEnum::EEmberStatusToString( EEmberStatus in ) { EMBER_NO_NETWORK_KEY_RECEIVED, "EMBER_NO_NETWORK_KEY_RECEIVED" } }; auto it = MyEnumStrings.find(in); - return it == MyEnumStrings.end() ? "OUT_OF_RANGE" : it->second; + return it == MyEnumStrings.end() ? "OUT_OF_RANGE : " + std::to_string(in) : it->second; } std::string CEzspEnum::EmberJoinMethodToString( EmberJoinMethod in ) @@ -61,46 +61,225 @@ std::string CEzspEnum::EmberJoinMethodToString( EmberJoinMethod in ) std::string CEzspEnum::EEzspCmdToString( EEzspCmd in ) { const std::map MyEnumStrings { + /* Configuration Frames */ { EZSP_VERSION, "EZSP_VERSION" }, { EZSP_GET_CONFIGURATION_VALUE, "EZSP_GET_CONFIGURATION_VALUE" }, { EZSP_SET_CONFIGURATION_VALUE, "EZSP_SET_CONFIGURATION_VALUE" }, { EZSP_ADD_ENDPOINT, "EZSP_ADD_ENDPOINT" }, { EZSP_SET_POLICY, "EZSP_SET_POLICY" }, { EZSP_GET_POLICY, "EZSP_GET_POLICY" }, + { EZSP_SEND_PAN_ID_UPDATE, "EZSP_SEND_PAN_ID_UPDATE" }, { EZSP_GET_VALUE, "EZSP_GET_VALUE" }, + { EZSP_GET_EXTENDED_VALUE, "EZSP_GET_EXTENDED_VALUE" }, { EZSP_SET_VALUE, "EZSP_SET_VALUE" }, + { EZSP_SET_GPIO_CURRENT_CONFIGURATION, "EZSP_SET_GPIO_CURRENT_CONFIGURATION" }, + { EZSP_SET_GPIO_POWER_UP_DOWN_CONFIGURATION, "EZSP_SET_GPIO_POWER_UP_DOWN_CONFIGURATION" }, + { EZSP_SET_GPIO_RADIO_POWER_MASK, "EZSP_SET_GPIO_RADIO_POWER_MASK" }, + { EZSP_SET_CTUNE, "EZSP_SET_CTUNE" }, + { EZSP_GET_CTUNE, "EZSP_GET_CTUNE" }, + /* Utilities Frames */ { EZSP_NOP, "EZSP_NOP" }, + { EZSP_ECHO, "EZSP_ECHO" }, + { EZSP_INVALID_COMMAND, "EZSP_INVALID_COMMAND" }, + { EZSP_CALLBACK, "EZSP_CALLBACK" }, + { EZSP_NO_CALLBACKS, "EZSP_NO_CALLBACKS" }, + { EZSP_SET_TOKEN, "EZSP_SET_TOKEN" }, + { EZSP_GET_TOKEN, "EZSP_GET_TOKEN" }, + { EZSP_GET_MFG_TOKEN, "EZSP_GET_MFG_TOKEN" }, + { EZSP_SET_MFG_TOKEN, "EZSP_SET_MFG_TOKEN" }, + { EZSP_STACK_TOKEN_CHANGED_HANDLER, "EZSP_STACK_TOKEN_CHANGED_HANDLER" }, + { EZSP_GET_RANDOM_NUMBER, "EZSP_GET_RANDOM_NUMBER" }, + { EZSP_SET_TIMER, "EZSP_SET_TIMER" }, + { EZSP_GET_TIMER, "EZSP_GET_TIMER" }, { EZSP_TIMER_HANDLER, "EZSP_TIMER_HANDLER" }, - /*{ EZSP_DEBUG_HANDLER, "EZSP_DEBUG_HANDLER" },*/ + { EZSP_DEBUG_WRITE, "EZSP_DEBUG_WRITE" }, + { EZSP_READ_AND_CLEAR_COUNTERS, "EZSP_READ_AND_CLEAR_COUNTERS" }, + { EZSP_READ_COUNTERS, "EZSP_READ_COUNTERS" }, + { EZSP_COUNTER_ROLLOVER_HANDLER, "EZSP_COUNTER_ROLLOVER_HANDLER" }, + { EZSP_DELAY_TEST, "EZSP_DELAY_TEST" }, + { EZSP_GET_LIBRARY_STATUS, "EZSP_GET_LIBRARY_STATUS" }, + { EZSP_GET_XNCP_INFO, "EZSP_GET_XNCP_INFO" }, + { EZSP_CUSTOM_FRAME, "EZSP_CUSTOM_FRAME" }, + { EZSP_CUSTOM_FRAME_HANDLER, "EZSP_CUSTOM_FRAME_HANDLER" }, + { EZSP_GET_EUI64, "EZSP_GET_EUI64" }, + { EZSP_GET_NODE_ID, "EZSP_GET_NODE_ID" }, + { EZSP_GET_PHY_INTERFACE_COUNT, "EZSP_GET_PHY_INTERFACE_COUNT" }, + { EZSP_GET_TRUE_RANDOM_ENTROPY_SOURCE, "EZSP_GET_TRUE_RANDOM_ENTROPY_SOURCE" }, + /* Networking Frames */ + { EZSP_SET_MANUFACTURER_CODE, "EZSP_SET_MANUFACTURER_CODE" }, + { EZSP_SET_POWER_DESCRIPTOR, "EZSP_SET_POWER_DESCRIPTOR" }, + { EZSP_NETWORK_INIT, "EZSP_NETWORK_INIT" }, + { EZSP_NETWORK_STATE, "EZSP_NETWORK_STATE" }, { EZSP_STACK_STATUS_HANDLER, "EZSP_STACK_STATUS_HANDLER" }, + { EZSP_START_SCAN, "EZSP_START_SCAN" }, { EZSP_ENERGY_SCAN_RESULT_HANDLER, "EZSP_ENERGY_SCAN_RESULT_HANDLER" }, { EZSP_NETWORK_FOUND_HANDLER, "EZSP_NETWORK_FOUND_HANDLER" }, { EZSP_SCAN_COMPLETE_HANDLER, "EZSP_SCAN_COMPLETE_HANDLER" }, + { EZSP_UNUSED_PAN_ID_FOUND_HANDLER, "EZSP_UNUSED_PAN_ID_FOUND_HANDLER" }, + { EZSP_FIND_UNUSED_PAN_ID, "EZSP_FIND_UNUSED_PAN_ID" }, + { EZSP_STOP_SCAN, "EZSP_STOP_SCAN" }, + { EZSP_FORM_NETWORK, "EZSP_FORM_NETWORK" }, + { EZSP_JOIN_NETWORK, "EZSP_JOIN_NETWORK" }, + { EZSP_LEAVE_NETWORK, "EZSP_LEAVE_NETWORK" }, + { EZSP_FIND_AND_REJOIN_NETWORK, "EZSP_FIND_AND_REJOIN_NETWORK" }, + { EZSP_PERMIT_JOINING, "EZSP_PERMIT_JOINING" }, { EZSP_CHILD_JOIN_HANDLER, "EZSP_CHILD_JOIN_HANDLER" }, + { EZSP_ENERGY_SCAN_REQUEST, "EZSP_ENERGY_SCAN_REQUEST" }, + { EZSP_GET_NETWORK_PARAMETERS, "EZSP_GET_NETWORK_PARAMETERS" }, + { EZSP_GET_RADIO_PARAMETERS, "EZSP_GET_RADIO_PARAMETERS" }, + { EZSP_GET_PARENT_CHILD_PARAMETERS, "EZSP_GET_PARENT_CHILD_PARAMETERS" }, + { EZSP_GET_CHILD_DATA, "EZSP_GET_CHILD_DATA" }, + { EZSP_GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE, "EZSP_GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE" }, + { EZSP_GET_SOURCE_ROUTE_TABLE_FILLED_SIZE, "EZSP_GET_SOURCE_ROUTE_TABLE_FILLED_SIZE" }, + { EZSP_GET_SOURCE_ROUTE_TABLE_ENTRY, "EZSP_GET_SOURCE_ROUTE_TABLE_ENTRY" }, + { EZSP_GET_NEIGHBOR, "EZSP_GET_NEIGHBOR" }, + { EZSP_SET_ROUTING_SHORTCUT_THRESHOLD, "EZSP_SET_ROUTING_SHORTCUT_THRESHOLD" }, + { EZSP_GET_ROUTING_SHORTCUT_THRESHOLD, "EZSP_GET_ROUTING_SHORTCUT_THRESHOLD" }, + { EZSP_NEIGHBOR_COUNT, "EZSP_NEIGHBOR_COUNT" }, + { EZSP_GET_ROUTE_TABLE_ENTRY, "EZSP_GET_ROUTE_TABLE_ENTRY" }, + { EZSP_SET_RADIO_POWER, "EZSP_SET_RADIO_POWER" }, + { EZSP_SET_RADIO_CHANNEL, "EZSP_SET_RADIO_CHANNEL" }, + { EZSP_SET_CONCENTRATOR, "EZSP_SET_CONCENTRATOR" }, + { EZSP_SET_BROKEN_ROUTE_ERROR_CODE, "EZSP_SET_BROKEN_ROUTE_ERROR_CODE" }, + { EZSP_MULTI_PHY_START, "EZSP_MULTI_PHY_START" }, + { EZSP_MULTI_PHY_STOP, "EZSP_MULTI_PHY_STOP" }, + { EZSP_MULTI_PHY_SET_RADIO_POWER, "EZSP_MULTI_PHY_SET_RADIO_POWER" }, + { EZSP_SEND_LINK_POWER_DELTA_REQUEST, "EZSP_SEND_LINK_POWER_DELTA_REQUEST" }, + { EZSP_MULTI_PHY_SET_RADIO_CHANNEL, "EZSP_MULTI_PHY_SET_RADIO_CHANNEL" }, + { EZSP_GET_DUTY_CYCLE_STATE, "EZSP_GET_DUTY_CYCLE_STATE" }, + { EZSP_SET_DUTY_CYCLE_LIMITS_IN_STACK, "EZSP_SET_DUTY_CYCLE_LIMITS_IN_STACK" }, + { EZSP_GET_DUTY_CYCLE_LIMITS, "EZSP_GET_DUTY_CYCLE_LIMITS" }, + { EZSP_GET_CURRENT_DUTY_CYCLE, "EZSP_GET_CURRENT_DUTY_CYCLE" }, + { EZSP_DUTY_CYCLE_HANDLER, "EZSP_DUTY_CYCLE_HANDLER" }, + /* Binding Frames */ + { EZSP_CLEAR_BINDING_TABLE, "EZSP_CLEAR_BINDING_TABLE" }, + { EZSP_SET_BINDING, "EZSP_SET_BINDING" }, + { EZSP_GET_BINDING, "EZSP_GET_BINDING" }, + { EZSP_DELETE_BINDING, "EZSP_DELETE_BINDING" }, + { EZSP_BINDING_IS_ACTIVE, "EZSP_BINDING_IS_ACTIVE" }, + { EZSP_GET_BINDING_REMOTE_NODE_ID, "EZSP_GET_BINDING_REMOTE_NODE_ID" }, + { EZSP_SET_BINDING_REMOTE_NODE_ID, "EZSP_SET_BINDING_REMOTE_NODE_ID" }, { EZSP_REMOTE_SET_BINDING_HANDLER, "EZSP_REMOTE_SET_BINDING_HANDLER" }, { EZSP_REMOTE_DELETE_BINDING_HANDLER, "EZSP_REMOTE_DELETE_BINDING_HANDLER" }, + /* Messaging Frames */ + { EZSP_MAXIMUM_PAYLOAD_LENGTH, "EZSP_MAXIMUM_PAYLOAD_LENGTH" }, + { EZSP_SEND_UNICAST, "EZSP_SEND_UNICAST" }, + { EZSP_SEND_BROADCAST, "EZSP_SEND_BROADCAST" }, + { EZSP_PROXY_BROADCAST, "EZSP_PROXY_BROADCAST" }, + { EZSP_SEND_MULTICAST, "EZSP_SEND_MULTICAST" }, + { EZSP_SEND_MULTICAST_WITH_ALIAS, "EZSP_SEND_MULTICAST_WITH_ALIAS" }, + { EZSP_SEND_REPLY, "EZSP_SEND_REPLY" }, { EZSP_MESSAGE_SENT_HANDLER, "EZSP_MESSAGE_SENT_HANDLER" }, + { EZSP_SEND_MANY_TO_ONE_ROUTE_REQUEST, "EZSP_SEND_MANY_TO_ONE_ROUTE_REQUEST" }, + { EZSP_POLL_FOR_DATA, "EZSP_POLL_FOR_DATA" }, { EZSP_POLL_COMPLETE_HANDLER, "EZSP_POLL_COMPLETE_HANDLER" }, { EZSP_POLL_HANDLER, "EZSP_POLL_HANDLER" }, { EZSP_INCOMING_SENDER_EUI64_HANDLER, "EZSP_INCOMING_SENDER_EUI64_HANDLER" }, { EZSP_INCOMING_MESSAGE_HANDLER, "EZSP_INCOMING_MESSAGE_HANDLER" }, { EZSP_INCOMING_ROUTE_RECORD_HANDLER, "EZSP_INCOMING_ROUTE_RECORD_HANDLER" }, + { EZSP_CHANGE_SOURCE_ROUTE_HANDLER, "EZSP_CHANGE_SOURCE_ROUTE_HANDLER" }, + { EZSP_SET_SOURCE_ROUTE, "EZSP_SET_SOURCE_ROUTE" }, { EZSP_INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER, "EZSP_INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER" }, { EZSP_INCOMING_ROUTE_ERROR_HANDLER, "EZSP_INCOMING_ROUTE_ERROR_HANDLER" }, + { EZSP_ADDRESS_TABLE_ENTRY_IS_ACTIVE, "EZSP_ADDRESS_TABLE_ENTRY_IS_ACTIVE" }, + { EZSP_SET_ADDRESS_TABLE_REMOTE_EUI64, "EZSP_SET_ADDRESS_TABLE_REMOTE_EUI64" }, + { EZSP_SET_ADDRESS_TABLE_REMOTE_NODE_ID, "EZSP_SET_ADDRESS_TABLE_REMOTE_NODE_ID" }, + { EZSP_GET_ADDRESS_TABLE_REMOTE_EUI64, "EZSP_GET_ADDRESS_TABLE_REMOTE_EUI64" }, + { EZSP_GET_ADDRESS_TABLE_REMOTE_NODE_ID, "EZSP_GET_ADDRESS_TABLE_REMOTE_NODE_ID" }, + { EZSP_SET_EXTENDED_TIMEOUT, "EZSP_SET_EXTENDED_TIMEOUT" }, + { EZSP_GET_EXTENDED_TIMEOUT, "EZSP_GET_EXTENDED_TIMEOUT" }, + { EZSP_REPLACE_ADDRESS_TABLE_ENTRY, "EZSP_REPLACE_ADDRESS_TABLE_ENTRY" }, + { EZSP_LOOKUP_NODE_ID_BY_EUI64, "EZSP_LOOKUP_NODE_ID_BY_EUI64" }, + { EZSP_LOOKUP_EUI64_BY_NODE_ID, "EZSP_LOOKUP_EUI64_BY_NODE_ID" }, + { EZSP_GET_MULTICAST_TABLE_ENTRY, "EZSP_GET_MULTICAST_TABLE_ENTRY" }, + { EZSP_SET_MULTICAST_TABLE_ENTRY, "EZSP_SET_MULTICAST_TABLE_ENTRY" }, { EZSP_ID_CONFLICT_HANDLER, "EZSP_ID_CONFLICT_HANDLER" }, + { EZSP_WRITE_NODE_DATA, "EZSP_WRITE_NODE_DATA" }, + { EZSP_SEND_RAW_MESSAGE, "EZSP_SEND_RAW_MESSAGE" }, { EZSP_MAC_PASSTHROUGH_MESSAGE_HANDLER, "EZSP_MAC_PASSTHROUGH_MESSAGE_HANDLER" }, { EZSP_MAC_FILTER_MATCH_MESSAGE_HANDLER, "EZSP_MAC_FILTER_MATCH_MESSAGE_HANDLER" }, { EZSP_RAW_TRANSMIT_COMPLETE_HANDLER, "EZSP_RAW_TRANSMIT_COMPLETE_HANDLER" }, + { EZSP_SET_MAC_POLL_CCA_WAIT_TIME, "EZSP_SET_MAC_POLL_CCA_WAIT_TIME" }, + { EZSP_SET_BEACON_CLASSIFICATION_PARAMS, "EZSP_SET_BEACON_CLASSIFICATION_PARAMS" }, + { EZSP_GET_BEACON_CLASSIFICATION_PARAMS, "EZSP_GET_BEACON_CLASSIFICATION_PARAMS" }, + /* Security Frames */ + { EZSP_SET_INITIAL_SECURITY_STATE, "EZSP_SET_INITIAL_SECURITY_STATE" }, + { EZSP_GET_CURRENT_SECURITY_STATE, "EZSP_GET_CURRENT_SECURITY_STATE" }, + { EZSP_GET_KEY, "EZSP_GET_KEY" }, { EZSP_SWITCH_NETWORK_KEY_HANDLER, "EZSP_SWITCH_NETWORK_KEY_HANDLER" }, + { EZSP_GET_KEY_TABLE_ENTRY, "EZSP_GET_KEY_TABLE_ENTRY" }, + { EZSP_SET_KEY_TABLE_ENTRY, "EZSP_SET_KEY_TABLE_ENTRY" }, + { EZSP_FIND_KEY_TABLE_ENTRY, "EZSP_FIND_KEY_TABLE_ENTRY" }, + { EZSP_ADD_OR_UPDATE_KEY_TABLE_ENTRY, "EZSP_ADD_OR_UPDATE_KEY_TABLE_ENTRY" }, + { EZSP_SEND_TRUST_CENTER_LINK_KEY, "EZSP_SEND_TRUST_CENTER_LINK_KEY" }, + { EZSP_ERASE_KEY_TABLE_ENTRY, "EZSP_ERASE_KEY_TABLE_ENTRY" }, + { EZSP_CLEAR_KEY_TABLE, "EZSP_CLEAR_KEY_TABLE" }, + { EZSP_REQUEST_LINK_KEY, "EZSP_REQUEST_LINK_KEY" }, + { EZSP_UPDATE_TC_LINK_KEY, "EZSP_UPDATE_TC_LINK_KEY" }, { EZSP_ZIGBEE_KEY_ESTABLISHMENT_HANDLER, "EZSP_ZIGBEE_KEY_ESTABLISHMENT_HANDLER" }, + { EZSP_ADD_TRANSIENT_LINK_KEY, "EZSP_ADD_TRANSIENT_LINK_KEY" }, + { EZSP_CLEAR_TRANSIENT_LINK_KEYS, "EZSP_CLEAR_TRANSIENT_LINK_KEYS" }, + { EZSP_GET_TRANSIENT_LINK_KEY, "EZSP_GET_TRANSIENT_LINK_KEY" }, + { EZSP_GET_TRANSIENT_KEY_TABLE_ENTRY, "EZSP_GET_TRANSIENT_KEY_TABLE_ENTRY" }, + /* Trust Center Frames */ { EZSP_TRUST_CENTER_JOIN_HANDLER, "EZSP_TRUST_CENTER_JOIN_HANDLER" }, + { EZSP_BROADCAST_NEXT_NETWORK_KEY, "EZSP_BROADCAST_NEXT_NETWORK_KEY" }, + { EZSP_BROADCAST_NETWORK_KEY_SWITCH, "EZSP_BROADCAST_NETWORK_KEY_SWITCH" }, + { EZSP_BECOME_TRUST_CENTER, "EZSP_BECOME_TRUST_CENTER" }, + { EZSP_AES_MMO_HASH, "EZSP_AES_MMO_HASH" }, + { EZSP_REMOVE_DEVICE, "EZSP_REMOVE_DEVICE" }, + { EZSP_UNICAST_NWK_KEY_UPDATE, "EZSP_UNICAST_NWK_KEY_UPDATE" }, + /* Certificate Based Key Exchange (CBKE) */ + { EZSP_GENERATE_CBKE_KEYS, "EZSP_GENERATE_CBKE_KEYS" }, { EZSP_GENERATE_CBKE_KEYS_HANDLER, "EZSP_GENERATE_CBKE_KEYS_HANDLER" }, + { EZSP_CALCULATE_SMACS, "EZSP_CALCULATE_SMACS" }, { EZSP_CALCULATE_SMACS_HANDLER, "EZSP_CALCULATE_SMACS_HANDLER" }, + { EZSP_GENERATE_CBKE_KEYS283K1, "EZSP_GENERATE_CBKE_KEYS283K1" }, + { EZSP_GENERATE_CBKE_KEYS_HANDLER283K1, "EZSP_GENERATE_CBKE_KEYS_HANDLER283K1" }, + { EZSP_CALCULATE_SMACS283K1, "EZSP_CALCULATE_SMACS283K1" }, + { EZSP_CALCULATE_SMACS_HANDLER283K1, "EZSP_CALCULATE_SMACS_HANDLER283K1" }, + { EZSP_CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY, "EZSP_CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY" }, + { EZSP_CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1, "EZSP_CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1" }, + { EZSP_GET_CERTIFICATE, "EZSP_GET_CERTIFICATE" }, + { EZSP_GET_CERTIFICATE283K1, "EZSP_GET_CERTIFICATE283K1" }, + { EZSP_DSA_SIGN, "EZSP_DSA_SIGN" }, { EZSP_DSA_SIGN_HANDLER, "EZSP_DSA_SIGN_HANDLER" }, + { EZSP_DSA_VERIFY, "EZSP_DSA_VERIFY" }, { EZSP_DSA_VERIFY_HANDLER, "EZSP_DSA_VERIFY_HANDLER" }, + { EZSP_DSA_VERIFY283K1, "EZSP_DSA_VERIFY283K1" }, + { EZSP_SET_PREINSTALLED_CBKE_DATA, "EZSP_SET_PREINSTALLED_CBKE_DATA" }, + { EZSP_SAVE_PREINSTALLED_CBKE_DATA283K1, "EZSP_SAVE_PREINSTALLED_CBKE_DATA283K1" }, + /* Mfglib */ { EZSP_MFGLIB_RX_HANDLER, "EZSP_MFGLIB_RX_HANDLER" }, + /* --- */ + /* Bootloader */ + { EZSP_LAUNCH_STANDALONE_BOOTLOADER, "EZSP_LAUNCH_STANDALONE_BOOTLOADER" }, + { EZSP_SEND_BOOTLOAD_MESSAGE, "EZSP_SEND_BOOTLOAD_MESSAGE" }, + { EZSP_GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY, "EZSP_GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY" }, { EZSP_INCOMING_BOOTLOAD_MESSAGE_HANDLER, "EZSP_INCOMING_BOOTLOAD_MESSAGE_HANDLER" }, { EZSP_BOOTLOAD_TRANSMIT_COMPLETE_HANDLER, "EZSP_BOOTLOAD_TRANSMIT_COMPLETE_HANDLER" }, + { EZSP_AES_ENCRYPT, "EZSP_AES_ENCRYPT" }, + { EZSP_OVERRIDE_CURRENT_CHANNEL, "EZSP_OVERRIDE_CURRENT_CHANNEL" }, + /* ZLL */ + /* --- */ + /* WWAH */ + /* --- */ + /* Green Power */ + { EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING, "EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING" }, + { EZSP_D_GP_SEND, "EZSP_D_GP_SEND" }, + { EZSP_D_GP_SENT_HANDLER, "EZSP_D_GP_SENT_HANDLER" }, + { EZSP_GPEP_INCOMING_MESSAGE_HANDLER, "EZSP_GPEP_INCOMING_MESSAGE_HANDLER" }, + { EZSP_GP_PROXY_TABLE_GET_ENTRY, "EZSP_GP_PROXY_TABLE_GET_ENTRY" }, + { EZSP_GP_PROXY_TABLE_LOOKUP, "EZSP_GP_PROXY_TABLE_LOOKUP" }, + { EZSP_GP_SINK_TABLE_GET_ENTRY, "EZSP_GP_SINK_TABLE_GET_ENTRY" }, + { EZSP_GP_SINK_TABLE_LOOKUP, "EZSP_GP_SINK_TABLE_LOOKUP" }, + { EZSP_GP_SINK_TABLE_SET_ENTRY, "EZSP_GP_SINK_TABLE_SET_ENTRY" }, + { EZSP_GP_SINK_TABLE_REMOVE_ENTRY, "EZSP_GP_SINK_TABLE_REMOVE_ENTRY" }, + { EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY, "EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY" }, + { EZSP_GP_SINK_TABLE_CLEAR_ALL, "EZSP_GP_SINK_TABLE_CLEAR_ALL" }, + { EZSP_GP_SINK_TABLE_INIT, "EZSP_GP_SINK_TABLE_INIT" }, + /* Secure EZSP */ + /* --- */ }; auto it = MyEnumStrings.find(in); return it == MyEnumStrings.end() ? "OUT_OF_RANGE" : it->second; diff --git a/src/domain/ezsp-protocol/ezsp-enum.h b/src/domain/ezsp-protocol/ezsp-enum.h index 8b2477e6..dbe5fbb0 100644 --- a/src/domain/ezsp-protocol/ezsp-enum.h +++ b/src/domain/ezsp-protocol/ezsp-enum.h @@ -1045,6 +1045,29 @@ class CEzspEnum{ static std::string EmberIncomingMessageTypeToString( EmberIncomingMessageType in ); }; +// The security level of the GPD. +typedef uint8_t EmberGpSecurityLevel; + +// The type of security key to use for the GPD. +typedef uint8_t EmberGpKeyType; + +// The proxy table entry status +typedef uint8_t EmberGpProxyTableEntryStatus; + +// The security frame counter +typedef uint32_t EmberGpSecurityFrameCounter; + +// The sink table entry status +typedef uint8_t EmberGpSinkTableEntryStatus; + +#define GP_SINK_LIST_ENTRIES 2 // hardcoded to 2 which is the spec minimum (cf. A.3.4.2.2.6 Sink group list parameter from doc-14-0563-16-batt-green-power-spec_ProxyBasic.pdf) +#define EMBER_GP_SINK_LIST_ENTRY_SIZE 11 // why ? first byte to 0xFF is for not used ! +typedef std::vector EmberGpSinkListEntry; + +// assume this value is never reach for a frame counter +#define INVALID_FRAME_COUNTER 0xFFFFFFFF + + #ifdef USE_RARITAN #include #endif // USE_RARITAN diff --git a/src/domain/ezsp-protocol/struct/ember-gp-address-struct.cpp b/src/domain/ezsp-protocol/struct/ember-gp-address-struct.cpp index 0eb00792..b0588db8 100644 --- a/src/domain/ezsp-protocol/struct/ember-gp-address-struct.cpp +++ b/src/domain/ezsp-protocol/struct/ember-gp-address-struct.cpp @@ -5,42 +5,96 @@ */ #include #include -#include "../../byte-manip.h" #include "ember-gp-address-struct.h" +CEmberGpAddressStruct::CEmberGpAddressStruct(): + gpdIeeeAddress(), + applicationId(), + endpoint() +{ +} + +CEmberGpAddressStruct::CEmberGpAddressStruct(const CEmberGpAddressStruct& other): + gpdIeeeAddress(other.getGpdIeeeAddress()), + applicationId(other.getApplicationId()), + endpoint(other.getEndpoint()) +{ +} + CEmberGpAddressStruct::CEmberGpAddressStruct(const std::vector& raw_message): + gpdIeeeAddress(raw_message.begin()+1,raw_message.begin()+1+EMBER_EUI64_BYTE_SIZE), + applicationId(raw_message.at(0)), + endpoint(raw_message.at(EMBER_EUI64_BYTE_SIZE+1)) +{ +} + +/** + * This method is a friend of CEmberGpAddressStruct class + * swap() is needed within operator=() to implement to copy and swap paradigm +**/ +void swap(CEmberGpAddressStruct& first, CEmberGpAddressStruct& second) /* nothrow */ +{ + using std::swap; // Enable ADL + + swap(first.gpdIeeeAddress, second.gpdIeeeAddress); + swap(first.applicationId, second.applicationId); + swap(first.endpoint, second.endpoint); + /* Once we have swapped the members of the two instances... the two instances have actually been swapped */ +} + +CEmberGpAddressStruct& CEmberGpAddressStruct::operator=(CEmberGpAddressStruct other) +{ + swap(*this, other); + return *this; +} + + +CEmberGpAddressStruct::CEmberGpAddressStruct(const uint32_t i_srcId): gpdIeeeAddress(), /* FIXME */ - sourceId(0), applicationId(0), endpoint(0) { + // update Ieee with twice SourceId + gpdIeeeAddress.push_back(static_cast(i_srcId&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>8)&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>16)&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>24)&0xFF)); + gpdIeeeAddress.push_back(static_cast(i_srcId&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>8)&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>16)&0xFF)); + gpdIeeeAddress.push_back(static_cast((i_srcId>>24)&0xFF)); +} + +std::vector CEmberGpAddressStruct::getRaw() const +{ + std::vector lo_raw; + + // application Id + lo_raw.push_back(applicationId); + + // Ieee | sourceId for(uint8_t loop=0; loop& raw_message); + /** + * @brief Construct from sourceId + * + * @param i_srcId SourceId to construct from + */ + CEmberGpAddressStruct(const uint32_t i_srcId); + + /** + * @brief swap function to allow implementing of copy-and-swap idiom on members of type CEmberGpAddressStruct + * + * This function will swap all attributes of \p first and \p second + * See http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom + * + * @param first The first object + * @param second The second object + */ + friend void (::swap)(CEmberGpAddressStruct& first, CEmberGpAddressStruct& second); + /** * @brief Assignment operator + * @param other The object to assign to the lhs * - * Copy construction is forbidden on this class + * @return The object that has been assigned the value of @p other */ - CEmberGpAddressStruct& operator=(const CEmberGpAddressStruct& other) = delete; + CEmberGpAddressStruct& operator=(CEmberGpAddressStruct other); /** * @brief The GPD's EUI64. @@ -37,9 +62,9 @@ class CEmberGpAddressStruct EmberEUI64 getGpdIeeeAddress() const { return gpdIeeeAddress; } /** - * @brief The GPD's source ID. + * @brief The GPD's source ID. not mention in ezsp specification but ieee and sourceId is an union */ - uint32_t getSourceId() const { return sourceId; } + uint32_t getSourceId() const { return quad_u8_to_u32(gpdIeeeAddress.at(3), gpdIeeeAddress.at(2), gpdIeeeAddress.at(1), gpdIeeeAddress.at(0)); } /** * @brief The GPD Application ID. @@ -51,6 +76,13 @@ class CEmberGpAddressStruct */ uint8_t getEndpoint() const { return endpoint; } + /** + * @brief return a raw buffer + * + * @return raw buffer + */ + std::vector getRaw() const; + /** * @brief Dump this instance as a string * @@ -69,10 +101,8 @@ class CEmberGpAddressStruct friend std::ostream& operator<< (std::ostream& out, const CEmberGpAddressStruct& data); private: - - EmberEUI64 gpdIeeeAddress; // The GPD's EUI64. - uint32_t sourceId; // The GPD's source ID. - uint8_t applicationId; // The GPD Application ID. - uint8_t endpoint; // The GPD endpoint. + EmberEUI64 gpdIeeeAddress; /*!< The GPD's EUI64 */ + uint8_t applicationId; /*!< The GPD Application ID */ + uint8_t endpoint; /*!< The GPD endpoint */ }; diff --git a/src/domain/ezsp-protocol/struct/ember-gp-sink-table-entry-struct.cpp b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-entry-struct.cpp new file mode 100644 index 00000000..3823bf71 --- /dev/null +++ b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-entry-struct.cpp @@ -0,0 +1,176 @@ +/** + * @file ember-gp-sink-table-entry-struct.cpp + * + * @brief Represents one sink table entry from Ember + */ + +#include +#include + +#include "ember-gp-sink-table-entry-struct.h" + +#include "../../byte-manip.h" + +CEmberGpSinkTableEntryStruct::CEmberGpSinkTableEntryStruct(): + status(0xFF), + options(), + gpd(), + device_id(), + sink_list(), + assigned_alias(), + groupcast_radius(), + security_options(), + gpdSecurity_frame_counter(), + gpd_key() +{ + sink_list[0].push_back(0xFF); + sink_list[1].push_back(0xFF); +} + +CEmberGpSinkTableEntryStruct::CEmberGpSinkTableEntryStruct(const std::vector& raw_message): + status(raw_message.at(0)), + options(dble_u8_to_u16(raw_message.at(1),raw_message.at(2))), + gpd(std::vector(raw_message.begin()+3,raw_message.end())), + device_id(raw_message.at(13)), + sink_list(), + assigned_alias(dble_u8_to_u16(raw_message.at(36),raw_message.at(37))), + groupcast_radius(raw_message.at(38)), + security_options(raw_message.at(39)), + gpdSecurity_frame_counter(static_cast(quad_u8_to_u32(raw_message.at(40),raw_message.at(41),raw_message.at(42),raw_message.at(43)))), + gpd_key(raw_message.begin()+44,raw_message.begin()+60) +{ + sink_list[0].push_back(0xFF); + sink_list[1].push_back(0xFF); +} + +CEmberGpSinkTableEntryStruct::CEmberGpSinkTableEntryStruct(EmberGpSinkTableEntryStatus i_status, CEmberGpSinkTableOption i_options, + CEmberGpAddressStruct i_gpd_address, uint8_t i_device_id, uint16_t i_alias, + uint8_t i_security_option, EmberGpSecurityFrameCounter i_frm_counter, EmberKeyData i_gpd_key): + status(i_status), + options(i_options), + gpd(i_gpd_address), + device_id(i_device_id), + sink_list(), + assigned_alias(i_alias), + groupcast_radius(0), + security_options(i_security_option), + gpdSecurity_frame_counter(i_frm_counter), + gpd_key(i_gpd_key) +{ + sink_list[0].push_back(0xFF); + sink_list[1].push_back(0xFF); +} + +CEmberGpSinkTableEntryStruct::CEmberGpSinkTableEntryStruct(const CEmberGpSinkTableEntryStruct& other): + status(other.status), + options(other.options), + gpd(other.gpd), + device_id(other.device_id), + sink_list(other.sink_list), + assigned_alias(other.assigned_alias), + groupcast_radius(other.groupcast_radius), + security_options(other.security_options), + gpdSecurity_frame_counter(other.gpdSecurity_frame_counter), + gpd_key(other.gpd_key) +{ + +} + + +/** + * This method is a friend of CEmberGpSinkTableEntryStruct class + * swap() is needed within operator=() to implement to copy and swap paradigm +**/ +void swap(CEmberGpSinkTableEntryStruct& first, CEmberGpSinkTableEntryStruct& second) /* nothrow */ +{ + using std::swap; // Enable ADL + + swap(first.status, second.status); + swap(first.options, second.options); + swap(first.gpd, second.gpd); + swap(first.device_id, second.device_id); + swap(first.sink_list, second.sink_list); + swap(first.assigned_alias, second.assigned_alias); + swap(first.groupcast_radius, second.groupcast_radius); + swap(first.security_options, second.security_options); + swap(first.gpdSecurity_frame_counter, second.gpdSecurity_frame_counter); + swap(first.gpd_key, second.gpd_key); + /* Once we have swapped the members of the two instances... the two instances have actually been swapped */ +} + +CEmberGpSinkTableEntryStruct& CEmberGpSinkTableEntryStruct::operator=(CEmberGpSinkTableEntryStruct other) +{ + swap(*this, other); + return *this; +} + +std::vector CEmberGpSinkTableEntryStruct::getRaw() const +{ + std::vector l_struct; + + // Internal status of the sink table entry. + l_struct.push_back(status); // 0x01 : active, 0xff : disable + // The tunneling options (this contains both options and extendedOptions from the spec). WARNING 16 bits !!! + l_struct.push_back(u16_get_lo_u8(options.get())); + l_struct.push_back(u16_get_hi_u8(options.get())); + // The addressing info of the GPD. + std::vector l_addr = gpd.getRaw(); + l_struct.insert(l_struct.end(), l_addr.begin(), l_addr.end()); + // The device id for the GPD. + l_struct.push_back(device_id); + // The list of sinks (hardcoded to 2 which is the spec minimum). + for( int loop=0; loop(gpdSecurity_frame_counter&0xFF)); + l_struct.push_back(static_cast((gpdSecurity_frame_counter>>8)&0xFF)); + l_struct.push_back(static_cast((gpdSecurity_frame_counter>>16)&0xFF)); + l_struct.push_back(static_cast((gpdSecurity_frame_counter>>24)&0xFF)); + + // The key to use for GPD. + l_struct.insert(l_struct.end(), gpd_key.begin(), gpd_key.end()); + + return l_struct; +} + + +std::string CEmberGpSinkTableEntryStruct::String() const +{ + std::stringstream buf; + + buf << "CEmberGpSinkTableEntryStruct : { "; + buf << "[status : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(status) << "]"; + buf << "[options : "<< options << "]"; + buf << "[gpd : "<< gpd << "]"; + buf << "[device_id : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(device_id) << "]"; + buf << "[sink_list[0] :"; + for(uint8_t loop=0; loop +#endif // USE_RARITAN + +class CEmberGpSinkTableEntryStruct +{ + public: + /** + * @brief Default constructor + */ + CEmberGpSinkTableEntryStruct(); + + /** + * @brief Construction from a buffer + * + * @param raw_message The buffer to construct from + */ + CEmberGpSinkTableEntryStruct(const std::vector& raw_message); + + /** + * @brief constructor with specific value, others are set to default value. + * option : 0x02A8 (cf. A3.3.2.2.1 Options parameter of the Sink Table from doc doc-14-0563-16-batt-green-power-spec_ProxyBasic.pdf) + * - bits 0..2 : Application Id (0b000 : use source Id) + * - bits 3..4 : Communication mode (0b01 : groupcast forwarding of the GP Notification command to DGroupID) + * - bit 5 : Sequence number capabilities (0b1 : use incremental sequence number) + * - bit 6 : RxOnCapability (0b0 : not capable) + * - bit 7 : FixedLocation (0b1 : not a mobile device) + * - bit 8 : AssignedAlias (0b0 : the derived alias is used) + * - bit 9 : Security use (0b1 : indicates that security-related parameters of the Sink Table entry are present) + * - bit 10..15 : Reserved + * sink_list : by reverse set first byte of each to 0xFF to disable usage. + * groupcast_radius : The default value of 0x00 indicates undefined + * + * @param i_status Internal status of the sink table entry. 0x01 active, 0xff : disable + * @param i_option see upper + * @param i_gpd_address The addressing info of the GPD. + * @param i_device_id The device id for the GPD. + * @param i_alias The assigned alias for the GPD. + * @param i_security_option The security options field. currently 0x12 : SecurtityLevel (0b10), SecurityKeyType(0b100) + * @param i_frm_counter the last observed valid frame counter value + * @param i_gpd_key The key to use for GPD. + */ + CEmberGpSinkTableEntryStruct(EmberGpSinkTableEntryStatus i_status, CEmberGpSinkTableOption i_options, + CEmberGpAddressStruct i_gpd_address, uint8_t i_device_id, uint16_t i_alias, + uint8_t i_security_option, EmberGpSecurityFrameCounter i_frm_counter, EmberKeyData i_gpd_key); + + /** + * @brief Copy constructor + * + * @param other The object instance to construct from + */ + CEmberGpSinkTableEntryStruct(const CEmberGpSinkTableEntryStruct& other); + + /** + * @brief swap function to allow implementing of copy-and-swap idiom on members of type CEmberGpSinkTableEntryStruct + * + * This function will swap all attributes of \p first and \p second + * See http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom + * + * @param first The first object + * @param second The second object + */ + friend void (::swap)(CEmberGpSinkTableEntryStruct& first, CEmberGpSinkTableEntryStruct& second); + + /** + * @brief Assignment operator + * @param other The object to assign to the lhs + * + * @return The object that has been assigned the value of @p other + */ + CEmberGpSinkTableEntryStruct& operator=(CEmberGpSinkTableEntryStruct other); + + /** + * @brief return structure as a raw + * + * @return raw of structure + */ + std::vector getRaw() const; + + /** + * @brief getters + */ + CEmberGpSinkTableOption getOption() const { return options; } + CEmberGpAddressStruct getGpdAddr() const { return gpd; } + EmberNodeId getAssignedAlias() const { return assigned_alias; } + EmberGpSecurityFrameCounter getSecurityFrameCounter() const { return gpdSecurity_frame_counter; } + EmberKeyData getGpdKey() const { return gpd_key; } + uint8_t getGroupcastRadius() const { return groupcast_radius; } + uint8_t getSecurityLevel() const { return security_options&0x03; } + uint8_t getSecurityKeyType() const { return (security_options>>2)&0x07; } + bool isActive() const { return status==0x01; } + + /** + * @brief status setter + */ + void setEntryActive(bool i_active) { this->status=(i_active?0x01:0xFF); } + + /** + * @brief options setter + */ + void setOptions(CEmberGpSinkTableOption i_options){ options=i_options; } + + /** + * @brief gpd setter + */ + void setGpdAddress(CEmberGpAddressStruct i_gpd_address){ gpd=i_gpd_address; } + + /** + * @brief device_id setter + */ + void setDeviceId(uint8_t i_device_id){ device_id=i_device_id; } + + /** + * @brief assigned_alias setter + */ + void setAlias(uint16_t i_alias){ assigned_alias=i_alias; } + + /** + * @brief security_options setter + */ + void setSecurityOption(uint8_t i_security_option){ security_options=i_security_option; } + + /** + * @brief gpdSecurity_frame_counter setter + */ + void setFrameCounter(EmberGpSecurityFrameCounter i_frm_counter){ gpdSecurity_frame_counter=i_frm_counter; } + + /** + * @brief gpd_key setter + */ + void setKey(EmberKeyData i_gpd_key){ gpd_key=i_gpd_key; } + + /** + * @brief Dump this instance as a string + * + * @return The resulting string + */ + std::string String() const; + + /** + * @brief Serialize to an iostream + * + * @param out The original output stream + * @param data The object to serialize + * + * @return The new output stream with serialized data appended + */ + friend std::ostream& operator<< (std::ostream& out, const CEmberGpSinkTableEntryStruct& data); + + private: + EmberGpSinkTableEntryStatus status; /*!< Internal status of the sink table entry */ + CEmberGpSinkTableOption options; /*!< The tunneling options (this contains both options and extendedOptions from the spec). WRONG Specification only 16 bits like option without extended... */ + CEmberGpAddressStruct gpd; /*!< The addressing info of the GPD */ + uint8_t device_id; /*!< The device id for the GPD */ + EmberGpSinkListEntry sink_list[GP_SINK_LIST_ENTRIES]; /*!< The list of sinks (hardcoded to 2 which is the spec minimum) */ + EmberNodeId assigned_alias; /*!< The assigned alias for the GPD */ + uint8_t groupcast_radius; /*!< The groupcast radius */ + uint8_t security_options; /*!< The security options field */ + EmberGpSecurityFrameCounter gpdSecurity_frame_counter; /*!< The security frame counter of the GPD */ + EmberKeyData gpd_key; /*!< The key to use for GPD */ +}; + +#ifdef USE_RARITAN +#include +#endif // USE_RARITAN \ No newline at end of file diff --git a/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.cpp b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.cpp new file mode 100644 index 00000000..eda8dd12 --- /dev/null +++ b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.cpp @@ -0,0 +1,149 @@ +/** + * @file ember-gp-sink-table-options-field.cpp + * + * @brief Represents Ember's sink table entry options bit field + * + * option : 0x02A8 (cf. A3.3.2.2.1 Options parameter of the Sink Table from doc doc-14-0563-16-batt-green-power-spec_ProxyBasic.pdf) + * - bits 0..2 : Application Id (0b000 : use source Id) + * - bits 3..4 : Communication mode (0b01 : groupcast forwarding of the GP Notification command to DGroupID) + * - bit 5 : Sequence number capabilities (0b1 : use incremental sequence number) + * - bit 6 : RxOnCapability (0b0 : not capable) + * - bit 7 : FixedLocation (0b1 : not a mobile device) + * - bit 8 : AssignedAlias (0b0 : the derived alias is used) + * - bit 9 : Security use (0b1 : indicates that security-related parameters of the Sink Table entry are present) + * - bit 10..15 : Reserved + */ + +#include +#include + +#include "ember-gp-sink-table-options-field.h" + +CEmberGpSinkTableOption::CEmberGpSinkTableOption() : + application_id(), + communication_mode(), + sequence_number_capabilities(), + rx_on_capability(), + fixed_location(), + assigned_alias(), + security_use() +{ + +} + + +CEmberGpSinkTableOption::CEmberGpSinkTableOption(const CEmberGpSinkTableOption& other) : + application_id(other.application_id), + communication_mode(other.communication_mode), + sequence_number_capabilities(other.sequence_number_capabilities), + rx_on_capability(other.rx_on_capability), + fixed_location(other.fixed_location), + assigned_alias(other.assigned_alias), + security_use(other.security_use) +{ + +} + + +/** + * raw constructor + * - bits 0..2 : Application Id (0b000 : use source Id) + * - bits 3..4 : Communication mode (0b01 : groupcast forwarding of the GP Notification command to DGroupID) + * - bit 5 : Sequence number capabilities (0b1 : use incremental sequence number) + * - bit 6 : RxOnCapability (0b0 : not capable) + * - bit 7 : FixedLocation (0b1 : not a mobile device) + * - bit 8 : AssignedAlias (0b0 : the derived alias is used) + * - bit 9 : Security use (0b1 : indicates that security-related parameters of the Sink Table entry are present) + * - bit 10..15 : Reserved + */ +CEmberGpSinkTableOption::CEmberGpSinkTableOption(const uint16_t i_options) : + application_id(static_cast(i_options&0x7)), + communication_mode(static_cast((i_options>>3)&0x3)), + sequence_number_capabilities((i_options&0x20)!=0), + rx_on_capability((i_options&0x40)!=0), + fixed_location((i_options&0x80)!=0), + assigned_alias((i_options&0x100)!=0), + security_use((i_options&0x200)!=0) +{ +} + +/** + * @brief constructor from commissioning payload option and more + * + * @param i_application_id : application id meeans way to address gpd : by sourceid or ieee + * @param i_gpdf_commissioning_payload : permit to know capability of gpd + */ +CEmberGpSinkTableOption::CEmberGpSinkTableOption(const uint8_t i_application_id, CGpdCommissioningPayload i_gpdf_commissioning_payload) : + application_id(i_application_id), + communication_mode(1), + sequence_number_capabilities(i_gpdf_commissioning_payload.isMACsequenceNumberCapability()), + rx_on_capability(i_gpdf_commissioning_payload.isRxOnCapability()), + fixed_location(i_gpdf_commissioning_payload.isFixedLocation()), + assigned_alias(0), + security_use(i_gpdf_commissioning_payload.isExtendedOptionsFieldPresent()) +{ + +} + +/** + * This method is a friend of CEmberGpSinkTableOption class + * swap() is needed within operator=() to implement to copy and swap paradigm +**/ +void swap(CEmberGpSinkTableOption& first, CEmberGpSinkTableOption& second) /* nothrow */ +{ + using std::swap; // Enable ADL + + swap(first.application_id, second.application_id); + swap(first.communication_mode, second.communication_mode); + swap(first.sequence_number_capabilities, second.sequence_number_capabilities); + swap(first.rx_on_capability, second.rx_on_capability); + swap(first.fixed_location, second.fixed_location); + swap(first.assigned_alias, second.assigned_alias); + swap(first.security_use, second.security_use); + /* Once we have swapped the members of the two instances... the two instances have actually been swapped */ +} + +CEmberGpSinkTableOption& CEmberGpSinkTableOption::operator=( CEmberGpSinkTableOption other) +{ + swap(*this, other); + return *this; +} + +/** + * raw getter + */ +uint16_t CEmberGpSinkTableOption::get() const +{ + uint16_t o_options; + o_options = static_cast(application_id<<0); + o_options |= static_cast(communication_mode<<3); + o_options |= static_cast(sequence_number_capabilities<<5); + o_options |= static_cast(rx_on_capability<<6); + o_options |= static_cast(fixed_location<<7); + o_options |= static_cast(assigned_alias<<8); + o_options |= static_cast(security_use<<9); + + return o_options; +} + +std::string CEmberGpSinkTableOption::String() const +{ + std::stringstream buf; + + buf << "CEmberGpSinkTableOption : { "; + buf << "[application_id : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(application_id) << "]"; + buf << "[communication_mode : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(communication_mode) << "]"; + buf << "[sequence_number_capabilities : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(sequence_number_capabilities) << "]"; + buf << "[rx_on_capability : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(rx_on_capability) << "]"; + buf << "[fixed_location : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(fixed_location) << "]"; + buf << "[assigned_alias : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(assigned_alias) << "]"; + buf << "[security_use : "<< std::hex << std::setw(2) << std::setfill('0') << unsigned(security_use) << "]"; + buf << " }"; + + return buf.str(); +} + +std::ostream& operator<< (std::ostream& out, const CEmberGpSinkTableOption& data){ + out << data.String(); + return out; +} diff --git a/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.h b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.h new file mode 100644 index 00000000..fb9e7cc9 --- /dev/null +++ b/src/domain/ezsp-protocol/struct/ember-gp-sink-table-options-field.h @@ -0,0 +1,159 @@ +/** + * @file ember-gp-sink-table-options-field.h + * + * @brief Represents Ember's sink table entry options bit field + * + * option : 0x02A8 (cf. A3.3.2.2.1 Options parameter of the Sink Table from doc doc-14-0563-16-batt-green-power-spec_ProxyBasic.pdf) + * - bits 0..2 : Application Id (0b000 : use source Id) + * - bits 3..4 : Communication mode (0b01 : groupcast forwarding of the GP Notification command to DGroupID) + * - bit 5 : Sequence number capabilities (0b1 : use incremental sequence number) + * - bit 6 : RxOnCapability (0b0 : not capable) + * - bit 7 : FixedLocation (0b1 : not a mobile device) + * - bit 8 : AssignedAlias (0b0 : the derived alias is used) + * - bit 9 : Security use (0b1 : indicates that security-related parameters of the Sink Table entry are present) + * - bit 10..15 : Reserved + */ + +#pragma once + +#include +#include "../ezsp-enum.h" +#include "../../zbmessage/gpd-commissioning-command-payload.h" // BAD DEPENDANCY NEED TO BE INPROVE + +#ifdef USE_RARITAN +/**** Start of the official API; no includes below this point! ***************/ +#include +#endif // USE_RARITAN + +class CEmberGpSinkTableOption +{ + public: + /** + * @brief Default constructor + */ + CEmberGpSinkTableOption(); + + /** + * @brief Copy constructor + * + * @param other The object to copy from + */ + CEmberGpSinkTableOption(const CEmberGpSinkTableOption& other); + + /** + * @brief Assignment operator + * + * @param other The object to assign from + */ + CEmberGpSinkTableOption& operator=( CEmberGpSinkTableOption other); + + /** + * @brief swap function to allow implementing of copy-and-swap idiom on members of type CEmberGpSinkTableOption + * + * This function will swap all attributes of \p first and \p second + * See http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom + * + * @param first The first object + * @param second The second object + */ + friend void (::swap)(CEmberGpSinkTableOption& first, CEmberGpSinkTableOption& second); + + /** + * @brief raw constructor + */ + CEmberGpSinkTableOption(const uint16_t i_options); + + /** + * @brief constructor from commissioning payload option and more + * + * @param i_application_id : application id meeans way to address gpd : by sourceid or ieee + * @param i_gpdf_commissioning_option : permit to know capability of gpd + */ + CEmberGpSinkTableOption(const uint8_t i_application_id, CGpdCommissioningPayload i_gpdf_commissioning_payload); + + /** + * @brief Raw getter + * + * @return This option field object represented as a 16-bit word + */ + uint16_t get() const; + + /** + * @brief Getter for the enclosed application ID + * + * @return The enclosed application ID + */ + uint8_t getApplicationId() const { return application_id; } + + /** + * @brief Getter for the enclosed communication mode + * + * @return The enclosed communication mode + */ + uint8_t getCommunicationMode() const { return communication_mode; } + + /** + * @brief Getter for the enclosed sequence number capabilities + * + * @return The enclosed sequence number capabilities + */ + bool isSequenceNumberCapabilities() const { return sequence_number_capabilities; } + + /** + * @brief Getter for the enclosed RX On capability + * + * @return true if this object contains an RX On capability + */ + bool isRxOnCapability() const { return rx_on_capability; } + + /** + * @brief Getter for the enclosed fixed location + * + * @return true if this object contains a fixed location capability + */ + bool isFixedLocation() const { return fixed_location; } + + /** + * @brief Getter for the enclosed assigned alias + * + * @return true if this object contains an assigned alias + */ + bool isAssignedAlias() const { return assigned_alias; } + + /** + * @brief Getter for the enclosed security use + * + * @return true if this object uses security + */ + bool isSecurityUse() const { return security_use; } + + /** + * @brief Dump this instance as a string + * + * @return The resulting string + */ + std::string String() const; + + /** + * @brief Serialize to an iostream + * + * @param out The original output stream + * @param data The object to serialize + * + * @return The new output stream with serialized data appended + */ + friend std::ostream& operator<< (std::ostream& out, const CEmberGpSinkTableOption& data); + + private: + uint8_t application_id; /*!< The application ID contained in this sink table entry options bit field */ + uint8_t communication_mode; /*!< The communication mode contained in this sink table entry options bit field */ + bool sequence_number_capabilities; /*!< The sequence number capabilities contained in this sink table entry options bit field */ + bool rx_on_capability; /*!< The RX On capavility toggle contained in this sink table entry options bit field */ + bool fixed_location; /*!< The fixed location toggle contained in this sink table entry options bit field */ + bool assigned_alias; /*!< The assigned alias toggle contained in this sink table entry options bit field */ + bool security_use; /*!< The security use toggle contained in this sink table entry options bit field */ +}; + +#ifdef USE_RARITAN +#include +#endif // USE_RARITAN diff --git a/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.cpp b/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.cpp new file mode 100644 index 00000000..e611435d --- /dev/null +++ b/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.cpp @@ -0,0 +1,67 @@ +/** + * @file ember-process-gp-pairing-parameter.cpp + * + * @brief Parameters for gpProxyTableProcessGpPairing (0xC9) command. + * + * Reference: docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ + +#include "ember-process-gp-pairing-parameter.h" + + + +CProcessGpPairingParam::CProcessGpPairingParam(CEmberGpSinkTableEntryStruct i_sink_table_entry, bool i_add_sink, bool i_remove_gpd, + uint16_t i_sinkNetworkAddress, EmberEUI64 i_sinkIeeeAddress ) : + options(i_sink_table_entry.getOption(), i_add_sink, i_remove_gpd, i_sink_table_entry.getSecurityLevel(), i_sink_table_entry.getSecurityKeyType(), true, true, true), + addr(i_sink_table_entry.getGpdAddr()), + commMode(i_sink_table_entry.getOption().getCommunicationMode()), + sinkNetworkAddress(i_sinkNetworkAddress), + sinkGroupId(static_cast(i_sink_table_entry.getGpdAddr().getSourceId()&0xFFFF)), + assignedAlias(i_sink_table_entry.getAssignedAlias()), + sinkIeeeAddress(i_sinkIeeeAddress), + gpdKey(i_sink_table_entry.getGpdKey()), + gpdSecurityFrameCounter(i_sink_table_entry.getSecurityFrameCounter()), + forwardingRadius(i_sink_table_entry.getGroupcastRadius()) +{ + +} + + +std::vector CProcessGpPairingParam::get() const +{ + std::vector lo_out; + + // The options field of the GP Pairing command + uint32_t l_option = options.get(); + lo_out.push_back(static_cast(l_option&0xFF)); + lo_out.push_back(static_cast((l_option>>8)&0xFF)); + lo_out.push_back(static_cast((l_option>>16)&0xFF)); + lo_out.push_back(static_cast((l_option>>24)&0xFF)); + // The addressing info of the target GPD. + std::vector l_gpd_addr = addr.getRaw(); + lo_out.insert(lo_out.end(),l_gpd_addr.begin(),l_gpd_addr.end()); + // The communication mode of the GP Sink. + lo_out.push_back(commMode); + // The network address of the GP Sink. + lo_out.push_back(static_cast(sinkNetworkAddress&0xFF)); + lo_out.push_back(static_cast((sinkNetworkAddress>>8)&0xFF)); + // The group ID of the GP Sink. + lo_out.push_back(static_cast(sinkGroupId&0xFF)); + lo_out.push_back(static_cast((sinkGroupId>>8)&0xFF)); + // The alias assigned to the GPD. + lo_out.push_back(static_cast(assignedAlias&0xFF)); + lo_out.push_back(static_cast((assignedAlias>>8)&0xFF)); + // The IEEE address of the GP Sink. + lo_out.insert(lo_out.end(),sinkIeeeAddress.begin(),sinkIeeeAddress.end()); + // The key to use for GPD. + lo_out.insert(lo_out.end(),gpdKey.begin(),gpdKey.end()); + // The security frame counter of the GPD. + lo_out.push_back(static_cast(gpdSecurityFrameCounter&0xFF)); + lo_out.push_back(static_cast((gpdSecurityFrameCounter>>8)&0xFF)); + lo_out.push_back(static_cast((gpdSecurityFrameCounter>>16)&0xFF)); + lo_out.push_back(static_cast((gpdSecurityFrameCounter>>24)&0xFF)); + // The forwarding radius. + lo_out.push_back(forwardingRadius); + + return lo_out; +} diff --git a/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.h b/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.h new file mode 100644 index 00000000..aa0054b6 --- /dev/null +++ b/src/domain/ezsp-protocol/struct/ember-process-gp-pairing-parameter.h @@ -0,0 +1,75 @@ +/** + * @file ember-process-gp-pairing-parameter.h + * + * @brief Parameters for gpProxyTableProcessGpPairing (0xC9) command. + * + * Reference: docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ +#pragma once + +#include "../../byte-manip.h" +#include "../ezsp-enum.h" +#include "ember-gp-address-struct.h" +#include "ember-gp-sink-table-entry-struct.h" +#include "../../zbmessage/gp-pairing-command-option-struct.h" + + +#ifdef USE_RARITAN +/**** Start of the official API; no includes below this point! ***************/ +#include +#endif // USE_RARITAN + +class CProcessGpPairingParam +{ + public: + /** + * @brief Default constructor + * + * Construction without arguments is not allowed + */ + CProcessGpPairingParam() = delete; + + /** + * @brief Copy constructor + * + * Copy construction is forbidden on this class + */ + CProcessGpPairingParam(const CProcessGpPairingParam& other) = delete; + + /** + * @brief constructor using sink table entry and extra parameter + * + * @param i_sink_table_entry : sink table entry corresponding to gpd + * @param i_add_sink : true to add, false to remove + * @param i_remove_gpd : true to remove + * @param i_sinkNetworkAddress : short network address of sink + * @param i_sinkIeeeAddress : long network address of sink + */ + CProcessGpPairingParam(CEmberGpSinkTableEntryStruct i_sink_table_entry, bool i_add_sink, bool i_remove_gpd, + uint16_t i_sinkNetworkAddress, EmberEUI64 i_sinkIeeeAddress ); + + + /** + * @brief Raw getter + * + * @return This object represented as a raw byte-buffer + */ + std::vector get() const; + + + private: + CGpPairingCommandOption options; /*!< The options field of the GP Pairing command (see A.3.3.5.2 GP Pairing command for more details) */ + CEmberGpAddressStruct addr; /*!< The target GPD */ + uint8_t commMode; /*!< The communication mode of the GP Sink */ + uint16_t sinkNetworkAddress; /*!< The network address of the GP Sink */ + uint16_t sinkGroupId; /*!< The group ID of the GP Sink */ + uint16_t assignedAlias; /*!< The alias assigned to the GPD */ + EmberEUI64 sinkIeeeAddress; /*!< The IEEE address of the GP Sink */ + EmberKeyData gpdKey; /*!< The key to use for the target GPD */ + uint32_t gpdSecurityFrameCounter; /*!< The gpd security frame counter */ + uint8_t forwardingRadius; /*!< The forwarding radius */ +}; + +#ifdef USE_RARITAN +#include +#endif // USE_RARITAN diff --git a/src/domain/zbmessage/gp-pairing-command-option-struct.cpp b/src/domain/zbmessage/gp-pairing-command-option-struct.cpp new file mode 100644 index 00000000..90bd8ff4 --- /dev/null +++ b/src/domain/zbmessage/gp-pairing-command-option-struct.cpp @@ -0,0 +1,46 @@ +/** + * @file gp-pairing-command-option-struct.cpp + * + * @brief option field of gp pairing command according to A.3.3.5.2 GP Pairing command from docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ + +#include "gp-pairing-command-option-struct.h" + +CGpPairingCommandOption::CGpPairingCommandOption(CEmberGpSinkTableOption i_sink_table_option, bool i_add_sink, bool i_remove_gpd, + uint8_t i_security_level, uint8_t i_security_key_type, bool i_frm_counter_present, + bool i_key_present, bool i_radius_present ): + application_id(i_sink_table_option.getApplicationId()), + add_sink(i_add_sink), + remove_gpd(i_remove_gpd), + communication_mode(i_sink_table_option.getCommunicationMode()), + gpd_fixed(i_sink_table_option.isFixedLocation()), + gpd_mac_seq_number_capability(i_sink_table_option.isSequenceNumberCapabilities()), + security_level(i_security_level), + security_key_type(i_security_key_type), + gpd_security_frame_counter_present(i_frm_counter_present), + gpd_security_key_present(i_key_present), + assigned_alias_present(i_sink_table_option.isAssignedAlias()), + forwarding_radius_present(i_radius_present) +{ + +} + +uint32_t CGpPairingCommandOption::get() const +{ + uint32_t lo_option; + + lo_option = static_cast(application_id<<0); + lo_option |= static_cast(add_sink<<3); + lo_option |= static_cast(remove_gpd<<4); + lo_option |= static_cast(communication_mode<<5); + lo_option |= static_cast(gpd_fixed<<7); + lo_option |= static_cast(gpd_mac_seq_number_capability<<8); + lo_option |= static_cast(security_level<<9); + lo_option |= static_cast(security_key_type<<11); + lo_option |= static_cast(gpd_security_frame_counter_present<<14); + lo_option |= static_cast(gpd_security_key_present<<15); + lo_option |= static_cast(assigned_alias_present<<16); + lo_option |= static_cast(forwarding_radius_present<<17); + + return lo_option; +} diff --git a/src/domain/zbmessage/gp-pairing-command-option-struct.h b/src/domain/zbmessage/gp-pairing-command-option-struct.h new file mode 100644 index 00000000..79abcb1b --- /dev/null +++ b/src/domain/zbmessage/gp-pairing-command-option-struct.h @@ -0,0 +1,135 @@ +/** + * @file gp-pairing-command-option-struct.h + * + * @brief option field of gp pairing command according to A.3.3.5.2 GP Pairing command from docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ +#pragma once + +#include +#include + +#include "../ezsp-protocol/struct/ember-gp-sink-table-options-field.h" + +#ifdef USE_RARITAN +/**** Start of the official API; no includes below this point! ***************/ +#include +#endif // USE_RARITAN + +class CGpPairingCommandOption +{ + public: + /** + * @brief Default constructor + * + * Construction without arguments is not allowed + */ + CGpPairingCommandOption() = delete; + + /** + * @brief Copy constructor + * + * Copy construction is forbidden on this class + */ + CGpPairingCommandOption(const CGpPairingCommandOption& other) = delete; + + /** + * @brief Constructor based on sink table entry option field + * + * @param i_sink_table_option + * @param i_add_sink : true to add, false to remove + * @param i_remove_gpd : true to remove + * @param i_security_level + * @param i_security_key_type + * @param i_frm_counter_present + * @param i_key_present + * @param i_radius_present + */ + CGpPairingCommandOption(CEmberGpSinkTableOption i_sink_table_option, bool i_add_sink, bool i_remove_gpd, + uint8_t i_security_level, uint8_t i_security_key_type, bool i_frm_counter_present, + bool i_key_present, bool i_radius_present ); + + /** + * @brief Option getter + * + * @return This GP pairing command option represented as a 32-bit word + */ + uint32_t get() const; + + + private: + /** + * Bit 0..2: The ApplicationID sub-field contains the information about the application used by the GPD. + * ApplicationID = 0b000 indicates the GPD ID field has the length of 4B and contains the GPD SrcID; the Endpoint field is absent. + * ApplicationID = 0b010 indicates the GPD ID field has the length of 8B and contains the GPD IEEE address; the Endpoint field is present. + * All values of ApplicationID other than 0b000 and 0b010 are reserved in the current version of the Green Power cluster specification. + */ + uint8_t application_id; /*!< The ApplicationID value (contains the information about the application used by the GPD) */ + /** + * Bit 3: The AddSink sub-field of the Options field indicates, whether the GP sink wishes to add or remove a + * pairing for the GPD identified by the GPD ID. If set to 0b1 the pairing is being added. If set to 0b0 the + * pairing is being removed; then, the following fields are not present: DeviceID, GPD security Frame + * Counter, GPD key, AssignedAlias, and ForwardingRadius. + */ + bool add_sink; /*!< The AddSink value of the Options field. Indicates, whether the GP sink wishes to add or remove a pairing for the GPD identified by the GPD ID */ + /** + * Bit 4: The RemoveGPD sub-field of the Options field, if set to 0b1, indicates that the GPD identified by the + * GPD ID is being removed from the network. Then, none of the optional fields is present. + */ + bool remove_gpd; /*!< The RemoveGPD sub-field of the Options field, true indicates that the GPD identified by the */ + /** + * Bit 5..6: The Communication mode sub-field defines the communication mode requested by the sink, and can take values as defined in Table 27. + */ + uint8_t communication_mode; /*!< The Communication mode sub-field defines the communication mode requested by the sink */ + /** + * Bit 7: The GPDfixed sub-field and GPD MAC sequence number capabilities sub-field is copied from the corresponding + * FixedLocation and Sequence number capabilities sub-fields of the Options parameter of the Sink Table for this GPD. + */ + bool gpd_fixed; /*!< The GPDfixed sub-field value */ + /** + * Bit 8: see above + */ + bool gpd_mac_seq_number_capability; /*!< The GPD MAC sequence number capabilities sub-field */ + /** + * Bit 9..10: The SecurityLevel and SecurityKeyType SHALL carry the values of the corresponding parameters in Sink Table entry for this GPD. + */ + uint8_t security_level; /*!< The SecurityLevel for this GDP command */ + /** + * Bit 11..13: The sub-fields GPDsecurityFrameCounterPresent and GPDsecurityKeyPresent, if set to 0b1, indicate the presence of the fields + * GPDsecurityFrameCounter and GPDsecurityKey, respectively, which then carry the corresponding values from the Sink Table for this GPD. + * When the sub-fields GPDsecurityFrameCounterPresent and GPDsecurityKeyPresent are set to 0b0, + * the fields GPDsecurityFrame-Counter and GPDsecurityKey, respectively, are not present. + */ + uint8_t security_key_type; /*!< The SecurityKeyType for this GDP command */ + /** + * Bit 14: The GPDsecurityFrameCounter field SHALL be present whenever the AddSink sub-field of the Options field is set to 0b1; + * independent of the security level. If the SecurityLevel sub-field is set to 0b10-0b11 or if the SecurityLevel is 0b00 and + * the GPD MAC sequence number capabilities sub-field is set to 0b1, the GPDsecurityFrameCounter field carries the current value of + * the GPD security frame counter field from the Sink Table entry corresponding to the GPD ID. If the SecurityLevel is 0b00 and + * the GPD MAC sequence number capabilities sub-field is set to 0b0, the GPDsecurityFrameCounter SHALL be set to 0b00000000. + */ + bool gpd_security_frame_counter_present; /*!< Represents whether the GPD security frame counter is present in this GDP command */ + /** + * Bit 15: see above + */ + bool gpd_security_key_present; /*!< Represents whether the GPD security key is present in this GDP command */ + /** + * Bit 16: The AssignedAlias present sub-field, if set to 0b1, indicates that the AssignedAlias field is present and + * carries the Alias value to be used for this GPD instead of the derived alias. + */ + bool assigned_alias_present; /*!< Represents whether the AssignedAlias field is present and carries the Alias value to be used for this GPD instead of the derived alias */ + /** + * Bit 17: The Forwarding Radius present sub-field, if set to 0b1, indicates that the Forwarding Radius field is + * present and carries the Forwarding Radius value to be used as value of the radius in the groupcast forwarding + * of the GPDF packet. If the Forwarding Radius field is not present, and a new Proxy Table entry is to be created, + * the default value of 0x00 SHALL be used. The value 0x00 indicates unspecified, + * i.e. twice the value of the nwkMaxDepth attribute of the NIB, as specified by [1]. + */ + bool forwarding_radius_present; /*!< Represents whether the Forwarding Radius sub-field is present in this GDP command */ + /** + * Bit 18..23: Reserved + */ +}; + +#ifdef USE_RARITAN +#include +#endif // USE_RARITAN diff --git a/src/domain/zbmessage/gpd-commissioning-command-payload.cpp b/src/domain/zbmessage/gpd-commissioning-command-payload.cpp new file mode 100644 index 00000000..2aa432c2 --- /dev/null +++ b/src/domain/zbmessage/gpd-commissioning-command-payload.cpp @@ -0,0 +1,161 @@ +/** + * @file gpd-commissioning-command-payload.cpp + * + * @brief Decoding payload of gpd commissioning command according to A.4.2.1.1 GPD Commissioning command from docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ + +#include +#include +#include + +#include "../byte-manip.h" +#include "../custom-aes.h" +#include "gpd-commissioning-command-payload.h" + +CGpdCommissioningPayload::CGpdCommissioningPayload(const std::vector& raw_message, uint32_t i_src_id): + device_id(raw_message.at(0)), + options(raw_message.at(1)), + extended_options(0), + key(), + key_mic(), + out_frame_counter(), + app_information(0), + manufacturer_id(), + model_id(), + gpd_command_list(), + gpd_cluster_list() +{ + // only device_id and option are mandatory, other field depend of option value + unsigned int l_idx = 2; + + // extended option + if( options & (1<(l_idx),raw_message.begin()+static_cast(l_idx)+EMBER_KEY_DATA_BYTE_SIZE); + l_idx += EMBER_KEY_DATA_BYTE_SIZE; + // gpd key MIC and encryption + if( extended_options & (1<(&i_src_id), 4); + memcpy(&nonce[5], static_cast(&i_src_id), 4); + memcpy(&nonce[9], static_cast(&i_src_id), 4); + nonce[13] = 0x05; + nonce[14]=0x00; + nonce[15]=0x01; + + // decrypt + aes.aes_set_key(TC_LK); + aes.xor_block(out_key, nonce); + aes.aes_encrypt(out_key, out_key); + aes.xor_block(out_key, in_key); + + // fill key with uncrypt value + key.clear(); + for(int loop=0; loop<16; loop++){ key.push_back(out_key[loop]);} + + // verify MIC + // \todo + } + } + + // gpd outgoing counter + if( extended_options & (1<(l_idx),raw_message.end()); + } +} + +std::string CGpdCommissioningPayload::String() const +{ + std::stringstream buf; + + buf << "CGpdCommissioningPayload : { "; + buf << "[device_id : "<< std::hex << std::setw(2) << std::setfill('0') << static_cast(device_id) << "]"; + buf << "[options : "<< std::hex << std::setw(2) << std::setfill('0') << static_cast(options) << "]"; + buf << "[extended_options : "<< std::hex << std::setw(2) << std::setfill('0') << static_cast(extended_options) << "]"; + buf << "[key : "; + for(uint8_t loop=0; loop(key[loop]); } + buf << "]"; + buf << "[key_mic : "<< std::hex << std::setw(8) << std::setfill('0') << static_cast(key_mic) << "]"; + buf << "[out_frame_counter : "<< std::hex << std::setw(8) << std::setfill('0') << static_cast(out_frame_counter) << "]"; + buf << "[app_information : "<< std::hex << std::setw(2) << std::setfill('0') << static_cast(app_information) << "]"; + buf << "[manufacturer_id : "<< std::hex << std::setw(4) << std::setfill('0') << static_cast(manufacturer_id) << "]"; + buf << "[model_id : "<< std::hex << std::setw(4) << std::setfill('0') << static_cast(model_id) << "]"; + buf << "[gpd_command_list :"; + for(uint8_t loop=0; loop(gpd_command_list[loop]); } + buf << "]"; + buf << "[gpd_cluster_list :"; + for(uint8_t loop=0; loop(gpd_cluster_list[loop]); } + buf << "]"; + buf << " }"; + + return buf.str(); +} + +std::ostream& operator<< (std::ostream& out, const CGpdCommissioningPayload& data){ + out << data.String(); + return out; +} diff --git a/src/domain/zbmessage/gpd-commissioning-command-payload.h b/src/domain/zbmessage/gpd-commissioning-command-payload.h new file mode 100644 index 00000000..c2d8c201 --- /dev/null +++ b/src/domain/zbmessage/gpd-commissioning-command-payload.h @@ -0,0 +1,169 @@ +/** + * @file gpd-commissioning-command-payload.h + * + * @brief Decoding payload of gpd commissioning command according to A.4.2.1.1 GPD Commissioning command from docs-14-0563-16-batt-green-power-spec_ProxyBasic.pdf + */ +#pragma once + +#include +#include + +#include "../ezsp-protocol/ezsp-enum.h" + +// option bitfield +#define COM_OPTION_MAC_SEQ_CAPABILITY_BIT 0 +#define COM_OPTION_RX_ON_CAPABILITY_BIT 1 +#define COM_OPTION_APPLICATION_INFORMATION_BIT 2 +#define COM_OPTION_PAN_ID_REQUEST_BIT 4 +#define COM_OPTION_GP_SECURITY_KEY_REQUEST_BIT 5 +#define COM_OPTION_FIXED_LOCATION_BIT 6 +#define COM_OPTION_EXTENDED_OPTION_FIELD_BIT 7 + +// extend option bitfield +#define COM_EXT_OPTION_SECURITY_LVL_CAPABILITY_BIT 0 +#define COM_EXT_OPTION_KEY_TYPE_BIT 2 +#define COM_EXT_OPTION_GPD_KEY_PRESENT_BIT 5 +#define COM_EXT_OPTION_GPD_KEY_ENCRYPTION_BIT 6 +#define COM_EXT_OPTION_GPD_OUT_COUNTER_PRESENT_BIT 7 + +// application information bitfield +#define COM_APP_INFO_MANUFACTURER_ID_PRESENT_BIT 0 +#define COM_APP_INFO_MODEL_ID_PRESENT_BIT 1 +#define COM_APP_INFO_GPD_COMMANDS_PRESENT_BIT 2 +#define COM_APP_INFO_CLUSTER_LIST_PRESENT_BIT 3 + +class CGpdCommissioningPayload +{ + public: + + /** + * @brief Default constructor + * + * Construction without arguments is not allowed + */ + CGpdCommissioningPayload() = delete; + + /** + * @brief Assignment operator + * + * Assignment is forbidden on this class + */ + CGpdCommissioningPayload& operator=(const CGpdCommissioningPayload& other) = delete; + + /** + * @brief Construction from an incoming ezsp raw message + * + * @param raw_message The buffer to construct from + * @param i_src_id source id of gpd frame, used to decrypt key + */ + CGpdCommissioningPayload(const std::vector& raw_message, uint32_t i_src_id); + + /** + * @brief Getter for the enclosed encryption/authentication key + * + * @return The enclosed key + */ + EmberKeyData getKey() const { return key; } + + /** + * @brief Getter for the enclosed device ID + * + * @return The enclosed deviceId + */ + uint8_t getDeviceId() const { return device_id; } + + // bits field: + // b0 : MACsequenceNumberCapability (0b1:incremental MAC sequence number, 0b0:random MAC sequence number) + // b1 : RxOnCapability (0b1:GPD has receiving capabilities in operational mode.) + // b2 : Application information present + // b3 : reserved + // b4 : PANId request + // b5 : GP Security Key request + // b6 : FixedLocation + // b7 : Extended Options Field + /** + * @brief options getters bit field + */ + bool isMACsequenceNumberCapability() const { return (options & (1< gpd_command_list; /*!< The GPD command list contained in this GPD commissioning command */ + std::vector gpd_cluster_list; /*!< The GPD cluster list contained in this GPD commissioning command */ +}; \ No newline at end of file diff --git a/src/domain/zbmessage/green-power-device.cpp b/src/domain/zbmessage/green-power-device.cpp index 53bb099c..d382ce7f 100644 --- a/src/domain/zbmessage/green-power-device.cpp +++ b/src/domain/zbmessage/green-power-device.cpp @@ -8,10 +8,33 @@ CGpDevice::CGpDevice(uint32_t i_source_id, const EmberKeyData& i_key) : source_id(i_source_id), - key(i_key) + key(i_key), + option(0x02A8), + security_option(0x12) { } +/** + * This method is a friend of CEmberGpSinkTableOption class + * swap() is needed within operator=() to implement to copy and swap paradigm +**/ +void swap(CGpDevice& first, CGpDevice& second) /* nothrow */ +{ + using std::swap; // Enable ADL + + swap(first.source_id, second.source_id); + swap(first.key, second.key); + swap(first.option, second.option); + swap(first.security_option, second.security_option); + /* Once we have swapped the members of the two instances... the two instances have actually been swapped */ +} + +CGpDevice& CGpDevice::operator=( CGpDevice other) +{ + swap(*this, other); + return *this; +} + uint32_t CGpDevice::getSourceId() const { return this->source_id; diff --git a/src/domain/zbmessage/green-power-device.h b/src/domain/zbmessage/green-power-device.h index 692520d7..48441e45 100644 --- a/src/domain/zbmessage/green-power-device.h +++ b/src/domain/zbmessage/green-power-device.h @@ -8,6 +8,7 @@ #include #include "../ezsp-protocol/ezsp-enum.h" +#include "../ezsp-protocol/struct/ember-gp-sink-table-options-field.h" /** * @brief Class to encapsulate data representing a green power device @@ -34,9 +35,19 @@ class CGpDevice /** * @brief Assignment operator * - * Copy construction is forbidden on this class */ - CGpDevice& operator=(const CGpDevice& other) = delete; + CGpDevice& operator=(const CGpDevice other); + + /** + * @brief swap function to allow implementing of copy-and-swap idiom on members of type CEmberGpSinkTableOption + * + * This function will swap all attributes of \p first and \p second + * See http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom + * + * @param first The first object + * @param second The second object + */ + friend void (::swap)(CGpDevice& first, CGpDevice& second); /** * @brief Retrieve the source id for this device @@ -52,7 +63,23 @@ class CGpDevice */ EmberKeyData getKey() const; + /** + * @brief Retrieve the sink option table for this device + * + * @return The option + */ + CEmberGpSinkTableOption getSinkOption() const { return option; } + + /** + * @brief Retrieve the sink security option table for this device + * + * @return The security option + */ + uint8_t getSinkSecurityOption() const { return security_option; } + private: uint32_t source_id; /*!< The source ID for this device */ EmberKeyData key; /*!< The key for this device */ + CEmberGpSinkTableOption option; /*!< Sink table option for this device */ + uint8_t security_option; /*!< Sink table security option for this device */ }; diff --git a/src/domain/zbmessage/green-power-frame.cpp b/src/domain/zbmessage/green-power-frame.cpp index b2710319..3de49f75 100644 --- a/src/domain/zbmessage/green-power-frame.cpp +++ b/src/domain/zbmessage/green-power-frame.cpp @@ -12,6 +12,21 @@ #include "../ezsp-protocol/struct/ember-gp-address-struct.h" +CGpFrame::CGpFrame(): + link_value(0), + sequence_number(0), + source_id(0), + security(GPD_NO_SECURITY), + key_type(GPD_KEY_TYPE_NO_KEY), + auto_commissioning(false), + rx_after_tx(false), + security_frame_counter(0), + command_id(0xFF), + mic(0), + proxy_table_entry(0xFF), + payload() +{ +} CGpFrame::CGpFrame(const std::vector& raw_message): link_value(0), diff --git a/src/domain/zbmessage/green-power-frame.h b/src/domain/zbmessage/green-power-frame.h index c8e8bcae..e246582a 100644 --- a/src/domain/zbmessage/green-power-frame.h +++ b/src/domain/zbmessage/green-power-frame.h @@ -33,14 +33,14 @@ class CGpFrame * * Construction without arguments is not allowed */ - CGpFrame() = delete; + CGpFrame(); /** * @brief Assignment operator * * Copy construction is forbidden on this class */ - CGpFrame& operator=(const CGpFrame& other) = delete; + //CGpFrame& operator=(const CGpFrame& other) = delete; /** * @brief Construction from an incoming ezsp raw message @@ -67,19 +67,18 @@ class CGpFrame friend std::ostream& operator<< (std::ostream& out, const CGpFrame& data); // getter - uint8_t getLinkValue(){return link_value;} - uint8_t getSequenceNumber(){return sequence_number;} - uint32_t getSourceId(){return source_id;} - EGpSecurityLevel getSecurity(){return security;} - EGpSecurityKeyType getKeyType(){return key_type;} - bool isAutoCommissioning(){return auto_commissioning;} - bool isRxAfterTx(){return rx_after_tx;} - uint32_t getSecurityFrameCounter(){return security_frame_counter;} - uint8_t getCommandId(){return command_id;} - uint32_t getMic(){return mic;} - uint8_t getProxyTableEntry(){return proxy_table_entry;} - std::vector getPayload(){return payload;} - + uint8_t getLinkValue() const {return link_value;} + uint8_t getSequenceNumber() const {return sequence_number;} + uint32_t getSourceId() const {return source_id;} + EGpSecurityLevel getSecurity() const {return security;} + EGpSecurityKeyType getKeyType() const {return key_type;} + bool isAutoCommissioning() const {return auto_commissioning;} + bool isRxAfterTx() const {return rx_after_tx;} + uint32_t getSecurityFrameCounter() const {return security_frame_counter;} + uint8_t getCommandId() const {return command_id;} + uint32_t getMic() const {return mic;} + uint8_t getProxyTableEntry() const {return proxy_table_entry;} + std::vector getPayload() const {return payload;} private: uint8_t link_value; diff --git a/src/domain/zbmessage/zclheader.h b/src/domain/zbmessage/zclheader.h index d8c5e05e..179e1eb3 100644 --- a/src/domain/zbmessage/zclheader.h +++ b/src/domain/zbmessage/zclheader.h @@ -16,8 +16,14 @@ #include #endif // USE_RARITAN +// manufacturer code +#define PUBLIC_CODE 0xFFFF #define LG_MAN_CODE 0x1021 +// profile +#define GP_PROFILE_ID 0xA1E0 + + class CZCLHeader; /* Forward declaration */ void swap(CZCLHeader& first, CZCLHeader& second); /* Declaration before qualifying ::swap() as friend for class CZCLHeader */ diff --git a/src/domain/zbmessage/zigbee-message.cpp b/src/domain/zbmessage/zigbee-message.cpp index 806b3294..9846c1cb 100644 --- a/src/domain/zbmessage/zigbee-message.cpp +++ b/src/domain/zbmessage/zigbee-message.cpp @@ -26,18 +26,6 @@ CZigBeeMsg::~CZigBeeMsg() { } -/** - * @brief SetSpecific : build a basic cluster specific message - * @param i_profile_id : identifier of ZigBee profile to use - * @param i_manufacturer_code : set to 0xFFFF if public cluster - * @param i_endpoint : destination endpoint - * @param i_cluster_id : concerned cluster - * @param i_cmd_id : command - * @param i_direction : model side - * @param i_payload : payload of command - * @param i_src_ieee : address ieee to use as source of message - * @param i_grp_id : multicast group address to use (0 is assume as unicast/broadcast) - */ void CZigBeeMsg::SetSpecific( const uint16_t i_profile_id, const uint16_t i_manufacturer_code, const uint8_t i_endpoint, const uint16_t i_cluster_id, const uint8_t i_cmd_id, const EZCLFrameCtrlDirection i_direction, const std::vector& i_payload, const uint64_t i_src_ieee, const uint8_t i_transaction_number, const uint16_t i_grp_id ) @@ -51,17 +39,6 @@ void CZigBeeMsg::SetSpecific( const uint16_t i_profile_id, const uint16_t i_manu payload = i_payload; } -/** - * @brief SetGeneral : build a basic cluster general message - * @param i_msp_nwk : indicate if it's an msp or public message - * @param i_endpoint : destination endpoint - * @param i_cluster_id : concerned cluster - * @param i_cmd_id : command - * @param i_direction : model side - * @param i_payload : payload of command - * @param i_src_ieee : address ieee to use as source of message - * @param i_grp_id : multicast group address to use (0 is assume as unicast/broadcast) - */ void CZigBeeMsg::SetGeneral(const uint16_t i_profile_id, const uint16_t i_manufacturer_code, const uint8_t i_endpoint, const uint16_t i_cluster_id, const uint8_t i_cmd_id, const EZCLFrameCtrlDirection i_direction, const std::vector& i_payload , const uint64_t i_src_ieee, const uint8_t i_transaction_number, const uint16_t i_grp_id) @@ -75,12 +52,6 @@ void CZigBeeMsg::SetGeneral(const uint16_t i_profile_id, const uint16_t i_manufa payload = i_payload; } -/** - * @brief CZigBeeMsg::SetZdo : fill a ZDO message - * @param i_cmd_id : ZDO command - * @param i_payload : payload for command - * @param i_transaction_number : transaction sequence number - */ void CZigBeeMsg::SetZdo(const uint16_t i_cmd_id, const std::vector& i_payload, const uint8_t i_transaction_number) { aps.SetDefaultAPS( 0x0000, i_cmd_id, 0x00 ); @@ -92,12 +63,6 @@ void CZigBeeMsg::SetZdo(const uint16_t i_cmd_id, const std::vector& i_p payload.insert( payload.begin(), i_transaction_number ); } - -/** - * @brief Set : parse an incomming raw EZSP message - * @param i_aps : aps data - * @param i_msg : message data included header (ZCL and/or MSP) - */ void CZigBeeMsg::Set(const std::vector& i_aps, const std::vector& i_msg ) { uint8_t l_idx = 0; @@ -124,10 +89,6 @@ void CZigBeeMsg::Set(const std::vector& i_aps, const std::vector CZigBeeMsg::Get( void ) const { std::vector lo_msg; @@ -163,5 +124,3 @@ CZigBeeMsg& CZigBeeMsg::operator=(CZigBeeMsg other) swap(*this, other); return *this; } - - diff --git a/src/domain/zbmessage/zigbee-message.h b/src/domain/zbmessage/zigbee-message.h index c8432f0f..a3df0661 100644 --- a/src/domain/zbmessage/zigbee-message.h +++ b/src/domain/zbmessage/zigbee-message.h @@ -119,7 +119,7 @@ class CZigBeeMsg // high level /** - * @brief SetSpecific : build a basic cluster specific message + * @brief Build a basic cluster specific message * @param i_msp_nwk : indicate if it's an msp or public message * @param i_endpoint : destination endpoint * @param i_cluster_id : concerned cluster @@ -134,7 +134,7 @@ class CZigBeeMsg const uint8_t i_transaction_number = 0, const uint16_t i_grp_id = 0); /** - * @brief SetGeneral : build a basic cluster general message + * @brief Build a basic cluster general message * @param i_msp_nwk : indicate if it's an msp or public message * @param i_endpoint : destination endpoint * @param i_cluster_id : concerned cluster @@ -149,25 +149,36 @@ class CZigBeeMsg const uint8_t i_transaction_number = 0, const uint16_t i_grp_id = 0 ); /** - * @brief CZigBeeMsg::SetZdo : fill a ZDO message + * @brief Fill a ZDO message * @param i_cmd_id : ZDO command * @param i_payload : payload for command * @param i_transaction_number : transaction sequence number */ void SetZdo(const uint16_t i_cmd_id, const std::vector& i_payload, const uint8_t i_transaction_number = 0); - // aps - CAPSFrame GetAps(void) const { return aps; } + /** + * @brief Getter for the enclosed APS frame + * + * @return The enclosed APS frame + */ + CAPSFrame GetAps() const { return aps; } - // ZCL Header - CZCLHeader GetZCLHeader(void) const { return zcl_header; } + /** + * @brief Getter for the enclosed ZCL header + * + * @return The enclosed ZCL header + */ + CZCLHeader GetZCLHeader() const { return zcl_header; } - // payload - std::vector GetPayload(void) const { return payload; } + /** + * @brief Getter for the enclosed payload + * + * @return The enclosed payload + */ + std::vector GetPayload() const { return payload; } - // concatenate /** - * @brief Set : parse an incomming raw EZSP message + * @brief Parse an incomming raw EZSP message * @param i_aps : aps data * @param i_msg : message data included header (ZCL and/or MSP) */ @@ -175,22 +186,16 @@ class CZigBeeMsg /** * @brief Get : format zigbee message frame with header - * @return return zigbee message with header + * @return Zigbee message with header */ - std::vector Get( void ) const; + std::vector Get() const; + /* FIXME: make the atribute below private as create getter/setter methods */ + CAPSFrame aps; /*!< Enclosed APS frame */ private: - /** APS */ - CAPSFrame aps; - - /** ZCL Header */ - CZCLHeader zcl_header; - - /** Do we have a valid content in attribute zcl_header? */ - bool use_zcl_header; - - /** Payload */ - std::vector payload; + CZCLHeader zcl_header; /*!< Enclosed ZCL header */ + bool use_zcl_header; /*!< Do we have a valid content in attribute zcl_header? */ + std::vector payload; /*!< Enclosed payload */ }; #ifdef USE_RARITAN diff --git a/src/domain/zigbee-tools/green-power-sink-table.cpp b/src/domain/zigbee-tools/green-power-sink-table.cpp deleted file mode 100644 index 4c1b3eab..00000000 --- a/src/domain/zigbee-tools/green-power-sink-table.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file green-power-sink-table.cpp - * - * @brief A green power sink table - */ - -#include -#include - -#include "green-power-sink-table.h" - -#include "../../spi/GenericLogger.h" -#include "../../spi/ILogger.h" - -CGpSinkTable::CGpSinkTable() : - gpds() -{ - -} - -uint8_t CGpSinkTable::addEntry( CGpSinkTableEntry i_entry ) -{ - uint8_t lo_index = GP_SINK_INVALID_ENTRY; - -/* - std::stringstream buf; - buf << "[source_id : "<< std::hex << std::setw(8) << std::setfill('0') << unsigned(i_entry.getSourceId()) << "]"; - clogI << "CGpSinkTable::addEntry : " << buf.str() << std::endl; -*/ - // loop on table to search if gpd is already register - lo_index = getEntryIndexForSourceId( i_entry.getSourceId() ); - if( GP_SINK_INVALID_ENTRY == lo_index ) - { - // limit number of entry ! - if( gpds.size() < GP_SINK_INVALID_ENTRY ) - { - if (gpds.size()>255) - { - clogW << "gpd table size overflow\n"; - } - else - { - lo_index = static_cast(gpds.size()); - gpds.push_back(i_entry); - } - } - } - -// clogI << "CGpSinkTable::addEntry at index : " << unsigned(lo_index) << std::endl; - - return lo_index; -} - -uint8_t CGpSinkTable::getEntryIndexForSourceId(uint32_t i_source_id) -{ - uint8_t lo_index = GP_SINK_INVALID_ENTRY; - -/* - std::stringstream buf; - buf << "[source_id : "<< std::hex << std::setw(8) << std::setfill('0') << unsigned(i_source_id) << "]"; - clogI << "CGpSinkTable::getEntryIndexForSourceId : " << buf.str() << std::endl; -*/ - - for( unsigned int loop=0; loop255) - { - clogW << "gpd table index overflow\n"; - } - else - { - lo_index = static_cast(loop); - break; - } - } - } - -// clogI << "CGpSinkTable::getEntryIndexForSourceId at index : " << unsigned(lo_index) << std::endl; - - return lo_index; -} diff --git a/src/domain/zigbee-tools/green-power-sink-table.h b/src/domain/zigbee-tools/green-power-sink-table.h deleted file mode 100644 index c9cb8adb..00000000 --- a/src/domain/zigbee-tools/green-power-sink-table.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file green-power-sink-table.h - * - * @brief A green power sink table - */ -#pragma once - -#include -#include -#include "../zbmessage/green-power-sink-table-entry.h" - -#ifdef USE_RARITAN -/**** Start of the official API; no includes below this point! ***************/ -#include -#endif // USE_RARITAN - - -#define GP_SINK_INVALID_ENTRY 0xFF - -class CGpSinkTable -{ -public: - CGpSinkTable(const CGpSinkTable&) = delete; /* No copy construction allowed */ - - CGpSinkTable(); - - /** - * @brief add a green power sink table entry - * - * @return index of entry in sink table, or GP_SINK_INVALID_ENTRY if table is full - */ - uint8_t addEntry( CGpSinkTableEntry i_entry ); - - /** - * @brief obtain entry index according to a gpd source id - * - * @param i_source_id source id of gpd - * - * @return index of sink table entry, GP_SINK_INVALID_ENTRY if not found - */ - uint8_t getEntryIndexForSourceId(uint32_t i_source_id); - -private: - std::vector gpds; - -}; - -#ifdef USE_RARITAN -#include -#endif // USE_RARITAN \ No newline at end of file diff --git a/src/domain/zigbee-tools/green-power-sink.cpp b/src/domain/zigbee-tools/green-power-sink.cpp index 87520426..2c365c06 100644 --- a/src/domain/zigbee-tools/green-power-sink.cpp +++ b/src/domain/zigbee-tools/green-power-sink.cpp @@ -1,5 +1,7 @@ /** * @file green-power-sink.cpp + * + * @brief Access to green power capabilities */ #include @@ -11,24 +13,112 @@ #include "green-power-sink.h" #include "../ezsp-protocol/struct/ember-gp-address-struct.h" +#include "../byte-manip.h" + +#include "../../domain/zbmessage/zigbee-message.h" +#include "../../domain/zbmessage/gpd-commissioning-command-payload.h" + + #include "../../spi/GenericLogger.h" #include "../../spi/ILogger.h" +// some defines to help understanding +#define GP_ENDPOINT 242 + +// cluster +#define GP_CLUSTER_ID 0x0021 +// receive client command +#define GP_PROXY_COMMISIONING_MODE_CLIENT_CMD_ID 0x02 + +// GPF Command +#define GPF_SCENE_0_CMD 0x10 +#define GPF_SCENE_1_CMD 0x11 +#define GPF_SCENE_2_CMD 0x12 +#define GPF_SCENE_3_CMD 0x13 +#define GPF_SCENE_4_CMD 0x14 +#define GPF_SCENE_5_CMD 0x15 +#define GPF_SCENE_6_CMD 0x16 +#define GPF_SCENE_7_CMD 0x17 + +#define GPF_STORE_SCENE_0_CMD 0x18 +#define GPF_STORE_SCENE_1_CMD 0x19 +#define GPF_STORE_SCENE_2_CMD 0x1A +#define GPF_STORE_SCENE_3_CMD 0x1B +#define GPF_STORE_SCENE_4_CMD 0x1C +#define GPF_STORE_SCENE_5_CMD 0x1D +#define GPF_STORE_SCENE_6_CMD 0x1E +#define GPF_STORE_SCENE_7_CMD 0x1F +#define GPF_OFF_CMD 0x20 +#define GPF_ON_CMD 0x21 +#define GPF_TOGGLE_CMD 0x22 -CGpSink::CGpSink( CEzspDongle &i_dongle ) : +#define GPF_UP_W_ON_OFF_CMD 0x34 +#define GPF_STOP_CMD 0x35 +#define GPF_DOWN_W_ON_OFF_CMD 0x36 + +#define GPF_COMMISSIONING_CMD 0xE0 +#define GPF_DECOMMISSIONING_CMD 0xE1 + + + +CGpSink::CGpSink( CEzspDongle &i_dongle, CZigbeeMessaging &i_zb_messaging ) : dongle(i_dongle), - sink_table(), + zb_messaging(i_zb_messaging), + sink_state(SINK_NOT_INIT), + gpf_comm_frame(), + sink_table_index(0xFF), + gpds_to_register(), + sink_table_entry(), observers() { dongle.registerObserver(this); } -uint8_t CGpSink::registerGpd( uint32_t i_source_id ) +void CGpSink::init() +{ + // initialize green power sink + clogD << "Call EZSP_GP_SINK_TABLE_INIT" << std::endl; + dongle.sendCommand(EZSP_GP_SINK_TABLE_INIT); + + // set state + setSinkState(SINK_READY); +} + +void CGpSink::gpClearAllTables() +{ + // sink table + dongle.sendCommand(EZSP_GP_SINK_TABLE_CLEAR_ALL); +} + +void CGpSink::openCommissioningSession() { - CGpSinkTableEntry l_entry = CGpSinkTableEntry(i_source_id); + // set local proxy in commissioning mode + sendLocalGPProxyCommissioningMode(0x05); - return sink_table.addEntry(l_entry); + // set state + setSinkState(SINK_COM_OPEN); +} + +void CGpSink::closeCommissioningSession() +{ + // set local proxy in commissioning mode + sendLocalGPProxyCommissioningMode(0x00); + + // set state + setSinkState(SINK_READY); +} + +void CGpSink::registerGpds( const std::vector &gpd ) +{ + // save offline information + gpds_to_register = gpd; + + // request sink table entry + gpSinkTableFindOrAllocateEntry( gpds_to_register.back().getSourceId() ); + + // set state + setSinkState(SINK_COM_OFFLINE_IN_PROGRESS); } void CGpSink::handleDongleState( EDongleState i_state ) @@ -39,6 +129,11 @@ void CGpSink::handleEzspRxMessage( EEzspCmd i_cmd, std::vector i_msg_re { switch( i_cmd ) { + case EZSP_GP_SINK_TABLE_INIT: + { + clogI << "EZSP_GP_SINK_TABLE_INIT RSP" << std::endl; + } + break; case EZSP_GPEP_INCOMING_MESSAGE_HANDLER: { EEmberStatus l_status = static_cast(i_msg_receive.at(0)); @@ -46,25 +141,12 @@ void CGpSink::handleEzspRxMessage( EEzspCmd i_cmd, std::vector i_msg_re // build gpf frame from ezsp rx message CGpFrame gpf = CGpFrame(i_msg_receive); - - // Start DEBUG clogD << "EZSP_GPEP_INCOMING_MESSAGE_HANDLER status : " << CEzspEnum::EEmberStatusToString(l_status) << ", link : " << unsigned(i_msg_receive.at(1)) << ", sequence number : " << unsigned(i_msg_receive.at(2)) << - ", gp address : " << gpf <(i_msg_receive[i]) << " "; - } - clogI << "raw : " << bufDump.str() << std::endl; -*/ - // Stop DEBUG - /** * trame gpf: * - no cryptée : on essaye de la validé en donner la TC link key (zig...009), dans le cas ou il s'agit d'une trame de commissioning @@ -76,22 +158,200 @@ void CGpSink::handleEzspRxMessage( EEzspCmd i_cmd, std::vector i_msg_re if( GPD_NO_SECURITY == gpf.getSecurity() ) { - // to test notify - notifyObserversOfRxGpFrame( gpf ); + // if we are in Commissioning and this is a commissioning frame : use it ! + if( (SINK_COM_OPEN == sink_state) && (GPF_COMMISSIONING_CMD == gpf.getCommandId()) ) + { + // find entry in sink table + gpSinkTableFindOrAllocateEntry(gpf.getSourceId()); + + // save incomming message + gpf_comm_frame = gpf; + + // set new state + setSinkState(SINK_COM_IN_PROGRESS); + } } else { - // look up if product is register - uint8_t l_sink_entry_idx = sink_table.getEntryIndexForSourceId( gpf.getSourceId() ); - if( GP_SINK_INVALID_ENTRY != l_sink_entry_idx ) + // if success notify + if( EEmberStatus::EMBER_SUCCESS == l_status ) { - // to test notify notifyObserversOfRxGpFrame( gpf ); } } } break; + case EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY: + { + if( SINK_COM_IN_PROGRESS == sink_state ) + { + // save allocate index + sink_table_index = i_msg_receive.at(0); + + // debug + clogD << "EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY response index : " << std::hex << std::setw(2) << std::setfill('0') << sink_table_index << std::endl; + + // retrieve entry at selected index + if( 0xFF != sink_table_index ) + { + gpSinkGetEntry( sink_table_index ); + } + else + { + // no place to done pairing : FAILED + clogD << "INVALID SINK TABLE ENTRY, PAIRING FAILED !!" << std::endl; + setSinkState(SINK_READY); + } + } + else if( SINK_COM_OFFLINE_IN_PROGRESS == sink_state ) + { + // save allocate index + sink_table_index = i_msg_receive.at(0); + + // debug + clogD << "EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY response index : " << std::hex << std::setw(2) << std::setfill('0') << sink_table_index << std::endl; + + // retrieve entry at selected index + if( 0xFF != sink_table_index ) + { + gpSinkGetEntry( sink_table_index ); + } + else + { + // no place to done pairing : FAILED + clogD << "INVALID SINK TABLE ENTRY, PAIRING FAILED !!" << std::endl; + setSinkState(SINK_READY); + } + } + } + break; + + case EZSP_GP_SINK_TABLE_GET_ENTRY: + { + if( SINK_COM_IN_PROGRESS == sink_state ) + { + EEmberStatus l_status = static_cast(i_msg_receive.at(0)); + CEmberGpSinkTableEntryStruct l_entry({i_msg_receive.begin()+1,i_msg_receive.end()}); + + // debug + clogD << "EZSP_GP_SINK_TABLE_GET_ENTRY Response status :" << CEzspEnum::EEmberStatusToString(l_status) << ", table entry : " << l_entry << std::endl; + + // decode payload + CGpdCommissioningPayload l_payload(gpf_comm_frame.getPayload(),gpf_comm_frame.getSourceId()); + + // debug + clogD << "GPD Commissioning payload : " << l_payload << std::endl; + + // update sink table entry + CEmberGpAddressStruct l_gpd_addr(gpf_comm_frame.getSourceId()); + CEmberGpSinkTableOption l_options(l_gpd_addr.getApplicationId(),l_payload); + + l_entry.setEntryActive(true); + l_entry.setOptions(l_options); + l_entry.setGpdAddress(l_gpd_addr); + l_entry.setDeviceId(l_payload.getDeviceId()); + l_entry.setAlias(static_cast(gpf_comm_frame.getSourceId()&0xFFFF)); + l_entry.setSecurityOption(l_payload.getExtendedOption()&0x1F); + l_entry.setFrameCounter(l_payload.getOutFrameCounter()); + l_entry.setKey(l_payload.getKey()); + + // debug + clogD << "Update table entry : " << l_entry << std::endl; + + // call + gpSinkSetEntry(sink_table_index,l_entry); + + // save + sink_table_entry = l_entry; + } + else if( SINK_COM_OFFLINE_IN_PROGRESS == sink_state ) + { + EEmberStatus l_status = static_cast(i_msg_receive.at(0)); + CEmberGpSinkTableEntryStruct l_entry({i_msg_receive.begin()+1,i_msg_receive.end()}); + + // debug + clogD << "EZSP_GP_SINK_TABLE_GET_ENTRY Response status :" << CEzspEnum::EEmberStatusToString(l_status) << ", table entry : " << l_entry << std::endl; + + // update sink table entry + CEmberGpAddressStruct l_gp_addr(gpds_to_register.back().getSourceId()); + + l_entry.setEntryActive(true); + l_entry.setOptions(gpds_to_register.back().getSinkOption()); + l_entry.setGpdAddress(l_gp_addr); + l_entry.setAlias(static_cast(l_gp_addr.getSourceId()&0xFFFF)); + l_entry.setSecurityOption(gpds_to_register.back().getSinkSecurityOption()); + l_entry.setFrameCounter(0); + l_entry.setKey(gpds_to_register.back().getKey()); + + // debug + clogD << "Update table entry : " << l_entry << std::endl; + + // call + gpSinkSetEntry(sink_table_index,l_entry); + + // save + sink_table_entry = l_entry; + } + } + break; + + case EZSP_GP_SINK_TABLE_SET_ENTRY: + { + if( (SINK_COM_IN_PROGRESS == sink_state) || (SINK_COM_OFFLINE_IN_PROGRESS == sink_state) ) + { + EEmberStatus l_status = static_cast(i_msg_receive.at(0)); + + // debug + clogD << "EZSP_GP_SINK_TABLE_SET_ENTRY Response status :" << CEzspEnum::EEmberStatusToString(l_status) << std::endl; + + if( EMBER_SUCCESS != l_status ) + { + // error + clogD << "ERROR, Stop commissioning process !!" << std::endl; + setSinkState(SINK_READY); + } + else + { + // do proxy pairing + // \todo replace short and long sink network address by right value, currently we use group mode not so important + CProcessGpPairingParam l_param( sink_table_entry, true, false, 0, {0,0,0,0,0,0,0,0} ); + // call + gpProxyTableProcessGpPairing(l_param); + } + } + } + break; + + case EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING: + { + if( SINK_COM_IN_PROGRESS == sink_state ) + { + clogI << "CGpSink::ezspHandler EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING gpPairingAdded : " << std::hex << std::setw(2) << std::setfill('0') << static_cast(i_msg_receive[0]) << std::endl; + + // close commissioning session + closeCommissioningSession(); + } + else if( SINK_COM_OFFLINE_IN_PROGRESS == sink_state ) + { + clogI << "CGpSink::ezspHandler EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING gpPairingAdded : " << std::hex << std::setw(2) << std::setfill('0') << static_cast(i_msg_receive[0]) << std::endl; + + gpds_to_register.pop_back(); + + if( gpds_to_register.size() ) + { + // request sink table entry + gpSinkTableFindOrAllocateEntry( gpds_to_register.back().getSourceId() ); + } + else + { + // set state + setSinkState(SINK_READY); + } + } + } + break; + default: { /* DEBUG VIEW @@ -122,3 +382,229 @@ void CGpSink::notifyObserversOfRxGpFrame( CGpFrame i_gpf ) { observer->handleRxGpFrame( i_gpf ); } } + +void CGpSink::sendLocalGPProxyCommissioningMode(uint8_t i_option) +{ + // forge GP Proxy Commissioning Mode command + // assume we are coordinator of network and our nodeId is 0 + + CZigBeeMsg l_gp_comm_msg; + std::vector l_gp_comm_payload; + + // options: + // bit0 (Action) : 0b1 / request to enter commissioning mode + // bit1-3 (exit mode) : 0b010 / On first Pairing success + // bit4 (channel present) : 0b0 / shall always be set to 0 according current spec. + // bit5 (unicast communication) : 0b0 / send GP Commissioning Notification commands in broadcast + // bit6-7 (reserved) + l_gp_comm_payload.push_back(i_option); // 0x05 => open + + // comm windows 2 bytes + // present only if exit mode flag On commissioning Window expiration (bit0) is set + + // channel 1 byte + // never present with current specification + + + // create message sending from ep242 to ep242 using green power profile + l_gp_comm_msg.SetSpecific(GP_PROFILE_ID, PUBLIC_CODE, GP_ENDPOINT, + GP_CLUSTER_ID, GP_PROXY_COMMISIONING_MODE_CLIENT_CMD_ID, + E_DIR_SERVER_TO_CLIENT, l_gp_comm_payload, 0, 0, 0); + + // WARNING use ep 242 as sources + l_gp_comm_msg.aps.src_ep = GP_ENDPOINT; + + // + clogI << "SEND UNICAST : OPEN/CLOSE GP COMMISSIONING option : " << std::hex << std::setw(2) << std::setfill('0') << i_option << std::endl; + zb_messaging.SendUnicast(0,l_gp_comm_msg); +} + +/* +void CGpSink::gpBrCommissioningNotification( uint32_t i_gpd_src_id, uint8_t i_seq_number ) +{ + std::vector l_proxy_br_payload; + + // The source from which to send the broadcast + l_proxy_br_payload.push_back(static_cast(i_gpd_src_id&0xFF)); + l_proxy_br_payload.push_back(static_cast((i_gpd_src_id>>8)&0xFF)); + + // The destination to which to send the broadcast. This must be one of the three ZigBee broadcast addresses. + l_proxy_br_payload.push_back(0xFD); + l_proxy_br_payload.push_back(0xFF); + + // The network sequence number for the broadcast + l_proxy_br_payload.push_back(i_seq_number); + + // The APS frame for the message. + CAPSFrame l_aps; + l_aps.SetDefaultAPS(GP_PROFILE_ID,GP_CLUSTER_ID,GP_ENDPOINT); + l_aps.src_ep = GP_ENDPOINT; + std::vector l_ember_aps = l_aps.GetEmberAPS(); + l_proxy_br_payload.insert(l_proxy_br_payload.end(), l_ember_aps.begin(), l_ember_aps.end()); + + // The message will be delivered to all nodes within radius hops of the sender. A radius of zero is converted to EMBER_MAX_HOPS. + l_proxy_br_payload.push_back(0x00); + + // A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. + l_proxy_br_payload.push_back(0x00); + + // The length of the messageContents parameter in bytes. + l_proxy_br_payload.push_back(49); + + // The broadcast message. + l_proxy_br_payload.push_back(0x11); + l_proxy_br_payload.push_back(0x06); + l_proxy_br_payload.push_back(0x04); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x08); + l_proxy_br_payload.push_back(0x50); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x51); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x24); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0xe0); + l_proxy_br_payload.push_back(0x1f); + l_proxy_br_payload.push_back(0x02); + l_proxy_br_payload.push_back(0xc5); + l_proxy_br_payload.push_back(0xf2); + l_proxy_br_payload.push_back(0xa8); + l_proxy_br_payload.push_back(0xac); + l_proxy_br_payload.push_back(0x43); + l_proxy_br_payload.push_back(0x76); + l_proxy_br_payload.push_back(0x30); + l_proxy_br_payload.push_back(0x80); + l_proxy_br_payload.push_back(0x89); + l_proxy_br_payload.push_back(0x5f); + l_proxy_br_payload.push_back(0x3c); + l_proxy_br_payload.push_back(0xd5); + l_proxy_br_payload.push_back(0xdc); + l_proxy_br_payload.push_back(0x9a); + l_proxy_br_payload.push_back(0xd8); + l_proxy_br_payload.push_back(0x87); + l_proxy_br_payload.push_back(0x1c); + l_proxy_br_payload.push_back(0x0d); + l_proxy_br_payload.push_back(0x15); + l_proxy_br_payload.push_back(0xde); + l_proxy_br_payload.push_back(0x17); + l_proxy_br_payload.push_back(0x2b); + l_proxy_br_payload.push_back(0x24); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x04); + l_proxy_br_payload.push_back(0x02); + l_proxy_br_payload.push_back(0x20); + l_proxy_br_payload.push_back(0x21); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0x00); + l_proxy_br_payload.push_back(0xdc); + + clogI << "EZSP_PROXY_BROADCAST\n"; + dongle.sendCommand(EZSP_PROXY_BROADCAST,l_proxy_br_payload); +} +*/ +/* +void CAppDemo::ezspGetExtendedValue( uint8_t i_value_id, uint32_t i_characteristic ) +{ + std::vector l_payload; + + // Identifies which extended value ID to read. + l_payload.push_back(i_value_id); + + // Identifies which characteristics of the extended value ID to read. These are specific to the value being read. + l_payload.push_back((uint8_t)(i_characteristic&0xFF)); + l_payload.push_back((uint8_t)((i_characteristic>>8)&0xFF)); + l_payload.push_back((uint8_t)((i_characteristic>>16)&0xFF)); + l_payload.push_back((uint8_t)((i_characteristic>>24)&0xFF)); + + clogI << "EZSP_GET_EXTENDED_VALUE\n"; + dongle.sendCommand(EZSP_GET_EXTENDED_VALUE,l_payload); +} +*/ + +/* +void CAppDemo::gpSinkTableLookup( uint32_t i_src_id ) +{ + std::vector l_payload; + + // EmberGpAddress addr The address to search for. + l_payload.push_back(0x00); + l_payload.push_back((uint8_t)(i_src_id&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>8)&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>16)&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>24)&0xFF)); + l_payload.push_back((uint8_t)(i_src_id&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>8)&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>16)&0xFF)); + l_payload.push_back((uint8_t)((i_src_id>>24)&0xFF)); + l_payload.push_back(0x00); + + clogI << "EZSP_GP_SINK_TABLE_LOOKUP\n"; + dongle.sendCommand(EZSP_GP_SINK_TABLE_LOOKUP,l_payload); +} +*/ + +void CGpSink::gpSinkTableFindOrAllocateEntry( uint32_t i_src_id ) +{ + // An EmberGpAddress struct containing a copy of the gpd address to be found. + CEmberGpAddressStruct l_gp_address(i_src_id); + + clogI << "EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY\n"; + dongle.sendCommand(EZSP_GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY,l_gp_address.getRaw()); +} + +void CGpSink::gpSinkGetEntry( uint8_t i_index ) +{ + std::vector l_payload; + + // The index of the requested sink table entry. + l_payload.push_back(i_index); + + clogI << "EZSP_GP_SINK_TABLE_GET_ENTRY\n"; + dongle.sendCommand(EZSP_GP_SINK_TABLE_GET_ENTRY,l_payload); +} + + +void CGpSink::gpSinkSetEntry( uint8_t i_index, CEmberGpSinkTableEntryStruct& i_entry ) +{ + std::vector l_payload; + + // The index of the requested sink table entry. + l_payload.push_back(i_index); + + // struct + std::vector i_struct = i_entry.getRaw(); + l_payload.insert(l_payload.end(), i_struct.begin(), i_struct.end()); + + clogI << "EZSP_GP_SINK_TABLE_SET_ENTRY\n"; + dongle.sendCommand(EZSP_GP_SINK_TABLE_SET_ENTRY,l_payload); +} + + +void CGpSink::gpProxyTableProcessGpPairing( CProcessGpPairingParam& i_param ) +{ + clogI << "EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING\n"; + dongle.sendCommand(EZSP_GP_PROXY_TABLE_PROCESS_GP_PAIRING,i_param.get()); +} + + +void CGpSink::setSinkState( ESinkState i_state ) +{ + sink_state = i_state; + + const std::map MyEnumStrings { + { SINK_NOT_INIT, "SINK_NOT_INIT" }, + { SINK_READY, "SINK_READY" }, + { SINK_ERROR, "SINK_ERROR" }, + { SINK_COM_OPEN, "SINK_COM_OPEN" }, + { SINK_COM_IN_PROGRESS, "SINK_COM_IN_PROGRESS" }, + { SINK_COM_OFFLINE_IN_PROGRESS, "SINK_COM_OFFLINE_IN_PROGRESS" }, + }; + + auto it = MyEnumStrings.find(sink_state); /* FIXME: we issue a warning, but the variable app_state is now out of bounds */ + std::string error_str = it == MyEnumStrings.end() ? "OUT_OF_RANGE" : it->second; + clogI << "SINK State change : " << error_str << std::endl; +} diff --git a/src/domain/zigbee-tools/green-power-sink.h b/src/domain/zigbee-tools/green-power-sink.h index a1a1f10b..7e7b8105 100644 --- a/src/domain/zigbee-tools/green-power-sink.h +++ b/src/domain/zigbee-tools/green-power-sink.h @@ -1,24 +1,38 @@ /** * @file green-power-sink.h - * @brief access to green power capabilities + * + * @brief Access to green power capabilities */ + #pragma once #include "../zbmessage/green-power-frame.h" +#include "../zbmessage/green-power-device.h" #include "../green-power-observer.h" #include "../ezsp-dongle.h" -#include "green-power-sink-table.h" +#include "zigbee-messaging.h" +#include "../ezsp-protocol/struct/ember-gp-sink-table-entry-struct.h" +#include "../ezsp-protocol/struct/ember-process-gp-pairing-parameter.h" #ifdef USE_RARITAN /**** Start of the official API; no includes below this point! ***************/ #include #endif // USE_RARITAN +typedef enum +{ + SINK_NOT_INIT, // starting state + SINK_READY, // default state if no action in progress + SINK_ERROR, // something wrong + SINK_COM_OPEN, // GP Proxy Commissioning mode open + SINK_COM_IN_PROGRESS, // GP sink receive gpf comm frame + SINK_COM_OFFLINE_IN_PROGRESS, // Doing offline commissioning for GPD list +}ESinkState; class CGpSink : public CEzspDongleObserver { public: - CGpSink( CEzspDongle &i_dongle ); + CGpSink( CEzspDongle &i_dongle, CZigbeeMessaging &i_zb_messaging ); CGpSink() = delete; /* Construction without arguments is not allowed */ CGpSink(const CGpSink&) = delete; /* No copy construction allowed */ @@ -26,11 +40,31 @@ class CGpSink : public CEzspDongleObserver CGpSink& operator=(CGpSink) = delete; /* No assignment allowed */ /** - * @brief add a green power sink table entry + * @brief Initialize sink, shall be done after a network init. + */ + void init(); + + /** + * Clear all GP tables + */ + void gpClearAllTables(); + + /** + * @brief Open a commissioning session for limited time, close as soon as a binding is done. + */ + void openCommissioningSession(); + + /** + * @brief Force to close commissioning session + */ + void closeCommissioningSession(); + + /** + * @brief Add a green power device to this sink * - * @return index of entry in sink table, or GP_SINK_INVALID_ENTRY if table is full + * @param gpd list of gpds to add */ - uint8_t registerGpd( uint32_t i_source_id ); + void registerGpds( const std::vector &gpd ); /** * Observer @@ -47,13 +81,61 @@ class CGpSink : public CEzspDongleObserver private: CEzspDongle &dongle; - CGpSinkTable sink_table; + CZigbeeMessaging &zb_messaging; + ESinkState sink_state; + // parameters to save for pairing + CGpFrame gpf_comm_frame; + uint8_t sink_table_index; + std::vector gpds_to_register; + CEmberGpSinkTableEntryStruct sink_table_entry; + + std::set observers; /*!< List of observers of this class */ /** - * Notify Observer of this class + * @brief Notify observers of this class + * + * @param i_gpf The received GP frame */ - std::set observers; void notifyObserversOfRxGpFrame( CGpFrame i_gpf ); + + /** + * @brief Private utility function to manage error state + */ + void setSinkState( ESinkState i_state ); + + /** + * @brief send zigbee unicast message GP Proxy Commissioning Mode. + * done from sink to local dongle. + * @warning All parameters are hardcoded for testing + */ + void sendLocalGPProxyCommissioningMode(uint8_t i_option); + + /** + * @brief Retrieves the sink table entry stored at the specified index + * + * @param i_index The index to lookup + */ + void gpSinkGetEntry( uint8_t i_index ); + + /** + * @brief Finds or allocates a sink entry + * + * @param i_src_id GPD source ID address to search + */ + void gpSinkTableFindOrAllocateEntry( uint32_t i_src_id ); + + /** + * @brief Updates the sink table entry at the specified index. + * + * @param i_index The index of the requested sink table entry. + * @param i_entry An EmberGpSinkTableEntry struct containing a copy of the sink entry to be updated. + */ + void gpSinkSetEntry( uint8_t i_index, CEmberGpSinkTableEntryStruct& i_entry ); + + /** + * @brief Update the GP Proxy table based on a GP pairing. + */ + void gpProxyTableProcessGpPairing( CProcessGpPairingParam& i_param ); }; #ifdef USE_RARITAN diff --git a/src/example/CAppDemo.cpp b/src/example/CAppDemo.cpp index 521ef7a0..9e4cd2bd 100644 --- a/src/example/CAppDemo.cpp +++ b/src/example/CAppDemo.cpp @@ -23,18 +23,19 @@ CAppDemo::CAppDemo(IUartDriver& uartDriver, bool openGpCommissionning, bool openZigbeeCommissionning, unsigned int networkChannel, - const std::vector& gpDevList) : + const std::vector& gpDevicesList) : dongle(i_timer_factory, this), zb_messaging(dongle, i_timer_factory), zb_nwk(dongle, zb_messaging), - gp_sink(dongle), + gp_sink(dongle, zb_messaging), app_state(APP_NOT_INIT), db(), ezsp_version(6), reset_wanted(reset), openGpCommissionningAtStartup(openGpCommissionning), openZigbeeCommissionningAtStartup(openZigbeeCommissionning), - channel(networkChannel) + channel(networkChannel), + gpdList(gpDevicesList) { setAppState(APP_NOT_INIT); // uart @@ -46,10 +47,6 @@ CAppDemo::CAppDemo(IUartDriver& uartDriver, clogI << "CAppDemo open success !" << std::endl; dongle.registerObserver(this); gp_sink.registerObserver(this); - for (auto i : gpDevList) { - clogD << "Watching source ID 0x" << std::hex << std::setw(8) << std::setfill('0') << i.getSourceId() << "\n"; - gp_sink.registerGpd(i.getSourceId()); - } setAppState(APP_INIT_IN_PROGRESS); } // save parameter @@ -193,7 +190,7 @@ bool CAppDemo::extractMultiClusterReport( std::vector payload ) validBuffer = extractClusterReport(payload, usedBytes); if (validBuffer) { - payload.erase(payload.begin(), payload.begin()+usedBytes); + payload.erase(payload.begin(), payload.begin()+static_cast(usedBytes)); } } return validBuffer; @@ -274,6 +271,18 @@ void CAppDemo::handleEzspRxMessage( EEzspCmd i_cmd, std::vector i_msg_r { setAppState(APP_READY); + gp_sink.init(); + + if (this->openGpCommissionningAtStartup) { + // If requested to do so, immediately open a GP commissioning session + gp_sink.openCommissioningSession(); + } + else if( gpdList.size() ) + { + gp_sink.registerGpds(gpdList); + } + + if (this->openZigbeeCommissionningAtStartup) { // If requested to do so, open the zigbee network for a specific duration, so new devices can join zb_nwk.openNetwork(60); diff --git a/src/example/CAppDemo.h b/src/example/CAppDemo.h index f2ff8b19..a6ace9bd 100644 --- a/src/example/CAppDemo.h +++ b/src/example/CAppDemo.h @@ -29,7 +29,7 @@ typedef enum class CAppDemo : public CEzspDongleObserver, CGpObserver { public: - CAppDemo(IUartDriver& uartDriver, ITimerFactory &i_timer_factory, bool reset=false, bool openGpCommissionning=false, bool openZigbeeCommissionning=false, unsigned int networkChannel=11, const std::vector& gpDevList={}); + CAppDemo(IUartDriver& uartDriver, ITimerFactory &i_timer_factory, bool reset=false, bool openGpCommissionning=false, bool openZigbeeCommissionning=false, unsigned int networkChannel=11, const std::vector& gpDevicesList={}); /** * Callback @@ -59,5 +59,5 @@ class CAppDemo : public CEzspDongleObserver, CGpObserver bool openGpCommissionningAtStartup; /* Do we open GP commissionning at dongle initialization? */ bool openZigbeeCommissionningAtStartup; /* Do we open the Zigbee network at dongle initialization? */ unsigned int channel; /*!< The Zigbee channel on which to create the network (if reset_wanted is set) */ + std::vector gpdList; /*!< The list of GP devices we are monitoring */ }; - diff --git a/src/example/mainEzspTest.cpp b/src/example/mainEzspTest.cpp index 43ed2740..1528c7b1 100644 --- a/src/example/mainEzspTest.cpp +++ b/src/example/mainEzspTest.cpp @@ -25,6 +25,7 @@ #include #include #include +//#include // For debug static void writeUsage(const char* progname, FILE *f) { fprintf(f,"\n"); @@ -37,7 +38,26 @@ static void writeUsage(const char* progname, FILE *f) { fprintf(f,"-G (--open-gp-commissionning) : open the Green Power commissionning session at startup\n"); fprintf(f,"-u (--serial-port) : use a specific serial port (default: '/dev/ttyUSB0')\n"); fprintf(f,"-r (--reset-to-channel) : force re-creation of a network on the specified channel (discards previously existing network)\n"); - fprintf(f,"-s (--source-id) : enables receiving from a device with this source-id, formatted as a 8-digit hexadecimal string (eg: 'ffae1245') (repeated -s options are allowed)\n"); + fprintf(f,"-s (--source-id) : adds a device to the monitored list, based on its source-id & key, id being formatted as a 8-digit hexadecimal string (eg: 'ffae1245'), and key as a 16-byte/32-digit hex string (repeated -s options are allowed)\n"); +} + +/** + * @brief Convert an ASCII character representing one hexadecimal digit ([0-9a-fA-F]) to its value (0-15) + * + * @param[in] hDigit The input printable ASCII character + * @param[out] byte The value of the hexadecimal digit as a uint8_t nibble (0-15) + * @return true if the conversion succeeded +**/ +static bool hexDigitToNibble(const char hDigit, uint8_t& byte) { + if (hDigit>='a' && hDigit<='f') + byte = static_cast(hDigit + 10 - 'a'); + else if (hDigit>='A' && hDigit<='F') + byte = static_cast(hDigit + 10 - 'A'); + else if (hDigit>='0' && hDigit<='9') + byte = static_cast(hDigit - '0'); + else + return false; + return true; } int main(int argc, char **argv) { @@ -76,17 +96,50 @@ int main(int argc, char **argv) { switch (c) { case 's': { - std::stringstream sourceId; - sourceId << std::hex << optarg; - unsigned int sourceIdValue; - sourceId >> sourceIdValue; - if (sourceIdValue(-1)) { /* Protection against overflow */ - gpDevDataList.push_back(CGpDevice(sourceIdValue, CGpDevice::UNKNOWN_KEY)); + std::istringstream gpDevDataStream(optarg); + std::string gpDevSourceIdstr; + if (std::getline(gpDevDataStream, gpDevSourceIdstr, '/')) { + std::stringstream gpDevSourceIdStream; + gpDevSourceIdStream << std::hex << gpDevSourceIdstr; + unsigned int sourceIdValue; + gpDevSourceIdStream >> sourceIdValue; + if (sourceIdValue(-1)) { /* Protection against overflow */ + //std::cerr << "Read source ID part of arg: " << std::hex << std::setw(8) << std::setfill('0') << sourceIdValue << "\n"; + std::string gpDevKeyStr; + gpDevDataStream >> gpDevKeyStr; /* Read everything after the separator, which should be the key */ + //std::cerr << "Read key part of arg: " << gpDevKeyStr << "\n"; + if (gpDevKeyStr.length() != 32) { + clogE << "Invalid key length: " << gpDevKeyStr << " (should be 16-bytes long).\n"; + exit(1); + } + else { + EmberKeyData keyValue(CGpDevice::UNKNOWN_KEY); + if (gpDevKeyStr != "") { + std::vector argAsBytes; + for (unsigned int i = 0; i < gpDevKeyStr.length(); i += 2) { + uint8_t hiNibble; + if (!hexDigitToNibble(gpDevKeyStr[i], hiNibble)) { + clogE << "Invalid character '" << gpDevKeyStr[i] << "' at position " << i+1 << " in key " << gpDevKeyStr << "\n"; /* Note: 1st char is identified by a position=1 for readability */ + exit(1); + } + uint8_t loNibble; + if (!hexDigitToNibble(gpDevKeyStr[i+1], loNibble)) { + clogE << "Invalid character '" << gpDevKeyStr[i+1] << "' at position " << i+2 << " in key " << gpDevKeyStr << "\n"; /* Note: 1st char is identified by a position=1 for readability */ + exit(1); + } + argAsBytes.push_back(static_cast(hiNibble << 4) | loNibble); + } + //for (uint8_t loop=0; loop