diff --git a/doc/spinel-protocol-src/spinel-data-packing.md b/doc/spinel-protocol-src/spinel-data-packing.md index a222e73d2..578b6d978 100644 --- a/doc/spinel-protocol-src/spinel-data-packing.md +++ b/doc/spinel-protocol-src/spinel-data-packing.md @@ -42,6 +42,8 @@ Char | Name | Description `s` | DATATYPE_INT16 | Signed 16-bit integer. `L` | DATATYPE_UINT32 | Unsigned 32-bit integer. `l` | DATATYPE_INT32 | Signed 32-bit integer. + `X` | DATATYPE_UINT64 | Unsigned 64-bit integer. + `x` | DATATYPE_INT64 | Signed 64-bit integer. `i` | DATATYPE_UINT_PACKED | Packed Unsigned Integer. See (#packed-unsigned-integer). `6` | DATATYPE_IPv6ADDR | IPv6 Address. (Big-endian) `E` | DATATYPE_EUI64 | EUI-64 Address. (Big-endian) diff --git a/src/ncp/spinel.c b/src/ncp/spinel.c index 92f0f5b53..82fd161e5 100644 --- a/src/ncp/spinel.c +++ b/src/ncp/spinel.c @@ -337,6 +337,26 @@ spinel_datatype_vunpack_(bool in_place, const uint8_t *data_ptr, spinel_size_t d break; } + case SPINEL_DATATYPE_INT64_C: + case SPINEL_DATATYPE_UINT64_C: + { + uint64_t *arg_ptr = va_arg(args->obj, uint64_t *); + require_action(data_len >= sizeof(uint64_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + uint32_t l32 = (uint32_t)((data_ptr[3] << 24) | (data_ptr[2] << 16) | (data_ptr[1] << 8) | data_ptr[0]); + uint32_t h32 = (uint32_t)((data_ptr[7] << 24) | (data_ptr[6] << 16) | (data_ptr[5] << 8) | data_ptr[4]); + + *arg_ptr = ((uint64_t)l32) | (((uint64_t)h32) << 32); + } + + ret += sizeof(uint64_t); + data_ptr += sizeof(uint64_t); + data_len -= sizeof(uint64_t); + break; + } + case SPINEL_DATATYPE_IPv6ADDR_C: { require_action(data_len >= sizeof(spinel_ipv6addr_t), bail, (ret = -1, errno = EOVERFLOW)); @@ -748,6 +768,34 @@ spinel_datatype_vpack_(uint8_t *data_ptr, spinel_size_t data_len_max, const char break; } + case SPINEL_DATATYPE_INT64_C: + case SPINEL_DATATYPE_UINT64_C: + { + uint64_t arg = (uint64_t)va_arg(args->obj, uint64_t); + + ret += sizeof(uint64_t); + + if (data_len_max >= sizeof(uint64_t)) + { + data_ptr[7] = (arg >> 56) & 0xff; + data_ptr[6] = (arg >> 48) & 0xff; + data_ptr[5] = (arg >> 40) & 0xff; + data_ptr[4] = (arg >> 32) & 0xff; + data_ptr[3] = (arg >> 24) & 0xff; + data_ptr[2] = (arg >> 16) & 0xff; + data_ptr[1] = (arg >> 8) & 0xff; + data_ptr[0] = (arg >> 0) & 0xff; + data_ptr += sizeof(uint64_t); + data_len_max -= sizeof(uint64_t); + } + else + { + data_len_max = 0; + } + + break; + } + case SPINEL_DATATYPE_IPv6ADDR_C: { spinel_ipv6addr_t *arg = va_arg(args->obj, spinel_ipv6addr_t *); diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h index 96d0b092a..789ebce80 100644 --- a/src/ncp/spinel.h +++ b/src/ncp/spinel.h @@ -1407,6 +1407,8 @@ enum SPINEL_DATATYPE_INT16_C = 's', SPINEL_DATATYPE_UINT32_C = 'L', SPINEL_DATATYPE_INT32_C = 'l', + SPINEL_DATATYPE_UINT64_C = 'X', + SPINEL_DATATYPE_INT64_C = 'x', SPINEL_DATATYPE_UINT_PACKED_C = 'i', SPINEL_DATATYPE_IPv6ADDR_C = '6', SPINEL_DATATYPE_EUI64_C = 'E', @@ -1429,6 +1431,8 @@ typedef char spinel_datatype_t; #define SPINEL_DATATYPE_INT16_S "s" #define SPINEL_DATATYPE_UINT32_S "L" #define SPINEL_DATATYPE_INT32_S "l" +#define SPINEL_DATATYPE_UINT64_S "X" +#define SPINEL_DATATYPE_INT64_S "x" #define SPINEL_DATATYPE_UINT_PACKED_S "i" #define SPINEL_DATATYPE_IPv6ADDR_S "6" #define SPINEL_DATATYPE_EUI64_S "E" diff --git a/src/ncp/spinel_decoder.cpp b/src/ncp/spinel_decoder.cpp index 08d0472e9..2811481d4 100644 --- a/src/ncp/spinel_decoder.cpp +++ b/src/ncp/spinel_decoder.cpp @@ -172,6 +172,39 @@ exit: return error; } +otError SpinelDecoder::ReadUint64(uint64_t &aUint64) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(mIndex + sizeof(uint64_t) <= mEnd, error = OT_ERROR_PARSE); + + aUint64 = ((static_cast(mFrame[mIndex + 0]) << 0) | + (static_cast(mFrame[mIndex + 1]) << 8) | + (static_cast(mFrame[mIndex + 2]) << 16) | + (static_cast(mFrame[mIndex + 3]) << 24) | + (static_cast(mFrame[mIndex + 4]) << 32) | + (static_cast(mFrame[mIndex + 5]) << 40) | + (static_cast(mFrame[mIndex + 6]) << 48) | + (static_cast(mFrame[mIndex + 7]) << 56)); + + mIndex += sizeof(uint64_t); + +exit: + return error; +} + +otError SpinelDecoder::ReadInt64(int64_t &aInt64) +{ + otError error = OT_ERROR_NONE; + uint64_t u64; + + SuccessOrExit(error = ReadUint64(u64)); + aInt64 = static_cast(u64); + +exit: + return error; +} + otError SpinelDecoder::ReadUintPacked(unsigned int &aUint) { otError error = OT_ERROR_NONE; diff --git a/src/ncp/spinel_decoder.hpp b/src/ncp/spinel_decoder.hpp index e7a55fedc..46faa0208 100644 --- a/src/ncp/spinel_decoder.hpp +++ b/src/ncp/spinel_decoder.hpp @@ -208,6 +208,32 @@ public: */ otError ReadUint32(uint32_t &aUint32); + /** + * This method decodes and reads an `int64_t` value form the frame. + * + * On success, the read position gets updated. + * + * @param[out] aInt64 Reference to variable to output the read value. + * + * @retval OT_ERROR_NONE Successfully read the value. + * @retval OT_ERROR_PARSE Failed to parse/decode the value. + * + */ + otError ReadInt64(int64_t &aInt64); + + /** + * This method decodes and reads an `uint64_t` value form the frame. + * + * On success, the read position gets updated. + * + * @param[out] aUint64 Reference to variable to output the read value. + * + * @retval OT_ERROR_NONE Successfully read the value. + * @retval OT_ERROR_PARSE Failed to parse/decode the value. + * + */ + otError ReadUint64(uint64_t &aUint64); + /** * This method decodes (using spinel packed integer format) and reads an unsigned integer value form the frame. * diff --git a/src/ncp/spinel_encoder.cpp b/src/ncp/spinel_encoder.cpp index 0223cb4c5..1266573af 100644 --- a/src/ncp/spinel_encoder.cpp +++ b/src/ncp/spinel_encoder.cpp @@ -135,6 +135,23 @@ exit: return error; } +otError SpinelEncoder::WriteUint64(uint64_t aUint64) +{ + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 0) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 8) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 16) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 24) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 32) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 40) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 48) & 0xff)); + SuccessOrExit(error = mNcpBuffer.InFrameFeedByte((aUint64 >> 56) & 0xff)); + +exit: + return error; +} + otError SpinelEncoder::WriteUintPacked(unsigned int aUint) { uint8_t buffer[6]; diff --git a/src/ncp/spinel_encoder.hpp b/src/ncp/spinel_encoder.hpp index 90bf1c465..0fa96fab8 100644 --- a/src/ncp/spinel_encoder.hpp +++ b/src/ncp/spinel_encoder.hpp @@ -278,6 +278,42 @@ public: */ otError WriteInt32(int32_t aInt32) { return WriteUint32(static_cast(aInt32)); } + /** + * This method encodes and writes a `uint64_t` value to current input frame. + * + * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this + * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. + * + * If no buffer space is available, this method will discard and clear the current input frame and return the + * error status `OT_ERROR_NO_BUFS`. + * + * @param[in] aUint64 The value to add to input frame. + * + * @retval OT_ERROR_NONE Successfully added given value to the frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add the value. + * @retval OT_ERROR_INVALID_STATE `BeginFrame()` has not been called earlier to start the frame. + * + */ + otError WriteUint64(uint64_t aUint64); + + /** + * This method encodes and writes an `int64_t` value to current input frame. + * + * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this + * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. + * + * If no buffer space is available, this method will discard and clear the current input frame and return the + * error status `OT_ERROR_NO_BUFS`. + * + * @param[in] aInt64 The value to add to input frame. + * + * @retval OT_ERROR_NONE Successfully added given value to the frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add the value. + * @retval OT_ERROR_INVALID_STATE `BeginFrame()` has not been called earlier to start the frame. + * + */ + otError WriteInt64(int64_t aInt64) { return WriteUint64(static_cast(aInt64)); } + /** * This method encodes (using spinel packed integer format) and writes a value to current input frame. * diff --git a/tests/unit/test_spinel_decoder.cpp b/tests/unit/test_spinel_decoder.cpp index 056e85269..945d10646 100644 --- a/tests/unit/test_spinel_decoder.cpp +++ b/tests/unit/test_spinel_decoder.cpp @@ -101,7 +101,8 @@ void TestSpinelDecoder(void) const int16_t kInt16 = -567; const uint32_t kUint32 = 0xdeadbeef; const int32_t kInt32 = -123455678L; - + const uint64_t kUint64 = 0xfe10dc32ba549876ULL; + const int64_t kInt64 = -9197712039090021561LL; const unsigned int kUint_1 = 9; const unsigned int kUint_2 = 0xa3; @@ -136,6 +137,8 @@ void TestSpinelDecoder(void) int16_t i16; uint32_t u32; int32_t i32; + uint64_t u64; + int64_t i64; unsigned int u_1, u_2, u_3, u_4; const spinel_ipv6addr_t *ip6Addr; const spinel_eui48_t *eui48; @@ -164,6 +167,8 @@ void TestSpinelDecoder(void) SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_INT32_S + SPINEL_DATATYPE_UINT64_S + SPINEL_DATATYPE_INT64_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S @@ -184,6 +189,8 @@ void TestSpinelDecoder(void) kInt16, kUint32, kInt32, + kUint64, + kInt64, kUint_1, kUint_2, kUint_3, @@ -216,6 +223,8 @@ void TestSpinelDecoder(void) SuccessOrQuit(decoder.ReadInt16(i16), "ReadInt16() failed."); SuccessOrQuit(decoder.ReadUint32(u32), "ReadUint32() failed."); SuccessOrQuit(decoder.ReadInt32(i32), "ReadUint32() failed."); + SuccessOrQuit(decoder.ReadUint64(u64), "ReadUint64() failed."); + SuccessOrQuit(decoder.ReadInt64(i64), "ReadUint64() failed."); // Check the state VerifyOrQuit(decoder.GetReadLength() != 0, "GetReadLength() failed."); @@ -224,6 +233,7 @@ void TestSpinelDecoder(void) VerifyOrQuit(decoder.IsAllRead() == false, "IsAllRead() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_1), "ReadUintPacked() failed."); + SuccessOrQuit(decoder.ReadUintPacked(u_2), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_3), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_4), "ReadUintPacked() failed."); @@ -246,7 +256,9 @@ void TestSpinelDecoder(void) VerifyOrQuit(u16 == kUint16, "ReadUint16() parse failed."); VerifyOrQuit(i16 == kInt16, "ReadInt16() parse failed."); VerifyOrQuit(u32 == kUint32, "ReadUint32() parse failed."); - VerifyOrQuit(i32 == kInt32, "ReadUint32() parse failed."); + VerifyOrQuit(i32 == kInt32, "ReadInt32() parse failed."); + VerifyOrQuit(u64 == kUint64, "ReadUint64() parse failed."); + VerifyOrQuit(i64 == kInt64, "ReadInt64() parse failed."); VerifyOrQuit(u_1 == kUint_1, "ReadUintPacked() parse failed."); VerifyOrQuit(u_2 == kUint_2, "ReadUintPacked() parse failed."); VerifyOrQuit(u_3 == kUint_3, "ReadUintPacked() parse failed."); @@ -292,6 +304,8 @@ void TestSpinelDecoder(void) // Save position decoder.SavePosition(); + SuccessOrQuit(decoder.ReadUint64(u64), "ReadUint64() failed."); + SuccessOrQuit(decoder.ReadInt64(i64), "ReadUint64() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_1), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_2), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_3), "ReadUintPacked() failed."); @@ -306,6 +320,8 @@ void TestSpinelDecoder(void) VerifyOrQuit(i16 == kInt16, "ReadInt16() parse failed."); VerifyOrQuit(u32 == kUint32, "ReadUint32() parse failed."); VerifyOrQuit(i32 == kInt32, "ReadUint32() parse failed."); + VerifyOrQuit(u64 == kUint64, "ReadUint64() parse failed."); + VerifyOrQuit(i64 == kInt64, "ReadInt64() parse failed."); VerifyOrQuit(u_1 == kUint_1, "ReadUintPacked() parse failed."); VerifyOrQuit(u_2 == kUint_2, "ReadUintPacked() parse failed."); VerifyOrQuit(u_3 == kUint_3, "ReadUintPacked() parse failed."); @@ -314,12 +330,16 @@ void TestSpinelDecoder(void) SuccessOrQuit(decoder.ResetToSaved(), "ResetToSaved() failed"); + SuccessOrQuit(decoder.ReadUint64(u64), "ReadUint64() failed."); + SuccessOrQuit(decoder.ReadInt64(i64), "ReadUint64() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_1), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_2), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_3), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_4), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr), "ReadIp6Addr() failed."); + VerifyOrQuit(u64 == kUint64, "ReadUint64() parse failed."); + VerifyOrQuit(i64 == kInt64, "ReadInt64() parse failed."); VerifyOrQuit(u_1 == kUint_1, "ReadUintPacked() parse failed."); VerifyOrQuit(u_2 == kUint_2, "ReadUintPacked() parse failed."); VerifyOrQuit(u_3 == kUint_3, "ReadUintPacked() parse failed."); @@ -329,12 +349,16 @@ void TestSpinelDecoder(void) // Go back to save position again. SuccessOrQuit(decoder.ResetToSaved(), "ResetToSaved() failed"); + SuccessOrQuit(decoder.ReadUint64(u64), "ReadUint64() failed."); + SuccessOrQuit(decoder.ReadInt64(i64), "ReadUint64() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_1), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_2), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_3), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadUintPacked(u_4), "ReadUintPacked() failed."); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr), "ReadIp6Addr() failed."); + VerifyOrQuit(u64 == kUint64, "ReadUint64() parse failed."); + VerifyOrQuit(i64 == kInt64, "ReadInt64() parse failed."); VerifyOrQuit(u_1 == kUint_1, "ReadUintPacked() parse failed."); VerifyOrQuit(u_2 == kUint_2, "ReadUintPacked() parse failed."); VerifyOrQuit(u_3 == kUint_3, "ReadUintPacked() parse failed."); diff --git a/tests/unit/test_spinel_encoder.cpp b/tests/unit/test_spinel_encoder.cpp index 4ca952b6b..1c4017c73 100644 --- a/tests/unit/test_spinel_encoder.cpp +++ b/tests/unit/test_spinel_encoder.cpp @@ -117,6 +117,8 @@ void TestSpinelEncoder(void) const int16_t kInt16 = -567; const uint32_t kUint32 = 0xdeadbeef; const int32_t kInt32 = -123455678L; + const uint64_t kUint64 = 0xfe10dc32ba549876ULL; + const int64_t kInt64 = -9197712039090021561LL; const unsigned int kUint_1 = 9; const unsigned int kUint_2 = 0xa3; const unsigned int kUint_3 = 0x8765; @@ -149,6 +151,8 @@ void TestSpinelEncoder(void) int16_t i16; uint32_t u32; int32_t i32; + uint64_t u64; + int64_t i64; unsigned int u_1, u_2, u_3, u_4; spinel_ipv6addr_t *ip6Addr; spinel_eui48_t *eui48; @@ -172,6 +176,8 @@ void TestSpinelEncoder(void) SuccessOrQuit(encoder.WriteInt16(kInt16), "WriteInt16() failed."); SuccessOrQuit(encoder.WriteUint32(kUint32), "WriteUint32() failed."); SuccessOrQuit(encoder.WriteInt32(kInt32), "WriteUint32() failed."); + SuccessOrQuit(encoder.WriteUint64(kUint64), "WriteUint64() failed."); + SuccessOrQuit(encoder.WriteInt64(kInt64), "WriteUint64() failed."); SuccessOrQuit(encoder.WriteUintPacked(kUint_1), "WriteUintPacked() failed."); SuccessOrQuit(encoder.WriteUintPacked(kUint_2), "WriteUintPacked() failed."); SuccessOrQuit(encoder.WriteUintPacked(kUint_3), "WriteUintPacked() failed."); @@ -200,6 +206,8 @@ void TestSpinelEncoder(void) SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_INT32_S + SPINEL_DATATYPE_UINT64_S + SPINEL_DATATYPE_INT64_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S @@ -219,6 +227,8 @@ void TestSpinelEncoder(void) &i16, &u32, &i32, + &u64, + &i64, &u_1, &u_2, &u_3, @@ -241,6 +251,8 @@ void TestSpinelEncoder(void) VerifyOrQuit(i16 == kInt16, "WriteInt16() parse failed."); VerifyOrQuit(u32 == kUint32, "WriteUint32() parse failed."); VerifyOrQuit(i32 == kInt32, "WriteUint32() parse failed."); + VerifyOrQuit(u64 == kUint64, "WriteUint64() parse failed."); + VerifyOrQuit(i64 == kInt64, "WriteUint64() parse failed."); VerifyOrQuit(u_1 == kUint_1, "WriteUintPacked() parse failed."); VerifyOrQuit(u_2 == kUint_2, "WriteUintPacked() parse failed."); VerifyOrQuit(u_3 == kUint_3, "WriteUintPacked() parse failed.");