diff --git a/configure.ac b/configure.ac index 042a4c4eb..aa7e592d1 100644 --- a/configure.ac +++ b/configure.ac @@ -429,6 +429,7 @@ include/crypto/Makefile include/platform/Makefile src/Makefile src/cli/Makefile +src/ncp/Makefile src/core/Makefile third_party/Makefile third_party/mbedtls/Makefile @@ -436,6 +437,7 @@ examples/Makefile examples/platform/Makefile examples/platform/posix/Makefile examples/cli/Makefile +examples/ncp/Makefile tests/Makefile tests/scripts/Makefile tests/unit/Makefile diff --git a/examples/Makefile.am b/examples/Makefile.am index 213b6232f..0fac8e3a0 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -33,6 +33,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am DIST_SUBDIRS = \ platform \ cli \ + ncp \ $(NULL) # Always build (e.g. for 'make all') these subdirectories. @@ -40,6 +41,7 @@ DIST_SUBDIRS = \ SUBDIRS = \ platform \ cli \ + ncp \ $(NULL) # Always pretty (e.g. for 'make pretty') these subdirectories. diff --git a/examples/ncp/Makefile.am b/examples/ncp/Makefile.am new file mode 100644 index 000000000..e495eacae --- /dev/null +++ b/examples/ncp/Makefile.am @@ -0,0 +1,53 @@ +# +# Copyright (c) 2016, Nest Labs, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +bin_PROGRAMS = ncp + +ncp_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/examples \ + -I$(top_srcdir)/examples/platform/posix \ + -I$(top_srcdir)/third_party \ + $(OPENTHREAD_TARGET_DEFINES) \ + $(NULL) + +ncp_LDADD = \ + $(top_builddir)/src/ncp/libopenthread-ncp.a \ + $(top_builddir)/src/core/libopenthread.a \ + $(top_builddir)/examples/platform/posix/libopenthread-posix.a \ + $(top_builddir)/third_party/mbedtls/libmbedcrypto.a \ + -lpthread \ + $(NULL) + +ncp_SOURCES = main.cpp + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/examples/ncp/main.cpp b/examples/ncp/main.cpp new file mode 100644 index 000000000..65a0dd67a --- /dev/null +++ b/examples/ncp/main.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include + +struct gengetopt_args_info args_info; + +Thread::Ncp sNcp; + +void otSignalTaskletPending(void) +{ +} + +int main(int argc, char *argv[]) +{ + uint32_t atomic_state; + + memset(&args_info, 0, sizeof(args_info)); + + if (cmdline_parser(argc, argv, &args_info) != 0) + { + exit(1); + } + + hwAlarmInit(); + hwRadioInit(); + hwRandomInit(); + + otInit(); + + sNcp.Start(); + + while (1) + { + otProcessNextTasklet(); + + atomic_state = otPlatAtomicBegin(); + + if (!otAreTaskletsPending()) + { + hwSleep(); + } + + otPlatAtomicEnd(atomic_state); + } + + return 0; +} diff --git a/src/Makefile.am b/src/Makefile.am index 6b43f3ce2..d089b8cf2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,6 +33,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am DIST_SUBDIRS = \ cli \ core \ + ncp \ $(NULL) # Always build (e.g. for 'make all') these subdirectories. @@ -40,6 +41,7 @@ DIST_SUBDIRS = \ SUBDIRS = \ cli \ core \ + ncp \ $(NULL) # Always pretty (e.g. for 'make pretty') these subdirectories. diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index d35f927a4..1fcb4beb4 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -45,6 +45,11 @@ namespace Thread { +// This needs to not be static until the NCP +// the OpenThread API is capable enough for +// of of the features in the NCP. +ThreadNetif *sThreadNetif; + #ifdef __cplusplus extern "C" { #endif @@ -55,8 +60,6 @@ extern "C" { static otDEFINE_ALIGNED_VAR(sThreadNetifRaw, sizeof(ThreadNetif), uint64_t); -static ThreadNetif *sThreadNetif; - static void HandleActiveScanResult(void *aContext, Mac::Frame *aFrame); void otInit() diff --git a/src/ncp/Makefile.am b/src/ncp/Makefile.am new file mode 100644 index 000000000..a4c09d869 --- /dev/null +++ b/src/ncp/Makefile.am @@ -0,0 +1,82 @@ +# +# Copyright (c) 2016, Nest Labs, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +EXTRA_DIST = \ + PROTOCOL.md \ + $(NULL) + +# Pull in the sources that comprise the OpenThread NCP library. + +lib_LIBRARIES = libopenthread-ncp.a + +libopenthread_ncp_a_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/third_party \ + $(OPENTHREAD_TARGET_DEFINES) \ + $(NULL) + +libopenthread_ncp_a_CXXFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/third_party \ + $(OPENTHREAD_TARGET_DEFINES) \ + $(NULL) + +libopenthread_ncp_a_SOURCES = \ + hdlc.cpp \ + hdlc.hpp \ + flen.cpp \ + flen.hpp \ + ncp.cpp \ + ncp.hpp \ + ncp_base.cpp \ + ncp_base.hpp \ + spinel.c spinel.h \ + $(NULL) + +include_HEADERS = \ + $(NULL) + +noinst_PROGRAMS = spinel-test +spinel_test_SOURCES = spinel.c +spinel_test_CFLAGS = -DSPINEL_SELF_TEST=1 + +TESTS = spinel-test + +install-headers: install-includeHEADERS + +if OPENTHREAD_BUILD_COVERAGE +CLEANFILES = $(wildcard *.gcda *.gcno) +endif # OPENTHREAD_BUILD_COVERAGE + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/ncp/PROTOCOL.md b/src/ncp/PROTOCOL.md new file mode 100644 index 000000000..56f0959c9 --- /dev/null +++ b/src/ncp/PROTOCOL.md @@ -0,0 +1,1235 @@ +Spinel Host Controller Interface +================================ + +Updated: 2016-05-18 + +Written by: Robert Quattlebaum + +THIS DOCUMENT IS A WORK IN PROGRESS AND SUBJECT TO CHANGE. + +Copyright (c) 2016 Nest Labs, All Rights Reserved + +## 0. Abstract ## + +This document describes a general management protocol for enabling a host +device to communicate with and manage a network co-processor(NCP). + +While initially designed to support Thread-based NCPs, the NCP protocol +has been designed with a layered approach that allows it to be easily +adapted to other network protocols. + +## 1. Definitions ## + + * **NCP**: Network Control Processor + * **Host**: Computer or Micro-controller which controls the NCP. + * **TID**: Transaction Identifier (0-15) + * **IID**: Interface Identifier (0-3) + +## 2. Introduction ## + +This Network Co-Processor (NCP) protocol was designed to enable a host +device to communicate with and manage a NCP while also achieving the +following goals: + + * Adopt a layered approach to the protocol design, allowing future + support for other network protocols. + * Minimize the number of required commands/methods by providing a + rich, property-based API. + * Support NCPs capable of being connected to more than one network + at a time. + * Gracefully handle the addition of new features and capabilities + without necessarily breaking backward compatibility. + * Be as minimal and light-weight as possible without unnecessarily + sacrificing flexibility. + +On top of this core framework, we define the properties and commands +to enable various features and network protocols. + +## 3. Frame Format ## + +A frame is defined simply as the concatenation of + + * A header byte + * A command (up to three bytes) + * An optional command payload + + FRAME = HEADER CMD [CMD_PAYLOAD] + +Octets: | 1 | 1-3 | *n* +--------|--------|-----|----------------- +Fields: | HEADER | CMD | *[CMD_PAYLOAD]* + + +### 3.1. Header Format ### + +The header byte is broken down as follows: + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |1|R|IID| TID | + +-+-+-+-+-+-+-+-+ + +#### 3.1.1. Flag Bit #### + +The most significant header bit is always set to 1 to allow this +protocol to be line compatible with BTLE HCI. By setting the first +bit, we can disambiguate between Spinel frames and HCI frames (which +always start with either `0x01` or `0x04`) without any additional +framing overhead. + +#### 3.1.2. Reserved Bit #### + +The reserved bit (`R`) is reserved for future use. The sender MUST +set this bit to zero, and the receiver MUST ignore frames with this +bit set. + +#### 3.1.3. Interface Identifier (IID) #### + +The Interface Identifier (IID) is a number between 0 and 3 which +identifies which subinterface the frame is intended for. This allows +the protocol to support connecting to more than one network at once. +The first subinterface (0) is considered the primary subinterface and +MUST be supported. Support for all other subinterfaces is OPTIONAL. + +#### 3.1.4. Transaction Identifier (TID) #### + +The least significant bits of the header represent the Transaction +Identifier(TID). The TID is used for correlating responses to the +commands which generated them. + +When a command is sent from the host, any reply to that command sent +by the NCP will use the same value for the TID. When the host receives +a frame that matches the TID of the command it sent, it can easily +recognize that frame as the actual response to that command. + +The TID value of zero (0) is used for commands to which a correlated +response is not expected or needed, such as for unsolicited update +commands sent to the host from the NCP. + +#### 3.2. Command Identifier (CMD) #### + +The command identifier is a 21-bit unsigned integer encoded in up to +three bytes using the packed unsigned integer format described in +section 7.2. This encoding allows for up to 2,097,152 individual +commands, with the first 127 commands represented as a single byte. +Command identifiers larger than 2,097,151 are explicitly forbidden. + +CID Range | Description +----------------------|------------------ +0 - 63 | Reserved for core commands +64 - 15,359 | *UNALLOCATED* +15,360 - 16,383 | Vendor-specific +16,384 - 1,999,999 | *UNALLOCATED* +2,000,000 - 2,097,151 | Experimental use only + +#### 3.3. Command Payload (Optional) #### + +Depending on the semantics of the command in question, a payload MAY +be included in the frame. The exact composition and length of the +payload is defined by the command identifier. + +## 4. Commands + +* CMD 0: (Host->NCP) `CMD_NOOP` +* CMD 1: (Host->NCP) `CMD_RESET` +* CMD 2: (Host->NCP) `CMD_PROP_VALUE_GET` +* CMD 3: (Host->NCP) `CMD_PROP_VALUE_SET` +* CMD 4: (Host->NCP) `CMD_PROP_VALUE_INSERT` +* CMD 5: (Host->NCP) `CMD_PROP_VALUE_REMOVE` +* CMD 6: (NCP->Host) `CMD_PROP_VALUE_IS` +* CMD 7: (NCP->Host) `CMD_PROP_VALUE_INSERTED` +* CMD 8: (NCP->Host) `CMD_PROP_VALUE_REMOVED` +* CMD 9: (Host->NCP) `CMD_NET_SAVE` (See section B.1.1.) +* CMD 10: (Host->NCP) `CMD_NET_CLEAR` (See section B.1.2.) +* CMD 11: (Host->NCP) `CMD_NET_RECALL` (See section B.1.3.) +* CMD 12: (NCP->Host) `CMD_HBO_OFFLOAD` (See section C.1.1.) +* CMD 13: (NCP->Host) `CMD_HBO_RECLAIM` (See section C.1.2.) +* CMD 14: (NCP->Host) `CMD_HBO_DROP` (See section C.1.3.) +* CMD 15: (Host->NCP) `CMD_HBO_OFFLOADED` (See section C.1.4.) +* CMD 16: (Host->NCP) `CMD_HBO_RECLAIMED` (See section C.1.5.) +* CMD 17: (Host->NCP) `CMD_HBO_DROPPED` (See section C.1.6.) + +### 4.1. CMD 0: (Host->NCP) `CMD_NOOP` + +Octets: | 1 | 1 +--------|--------|---------- +Fields: | HEADER | CMD_NOOP + +No-Operation command. Induces the NCP to send a success status back to +the host. This is primarily used for livliness checks. + +The command payload for this command SHOULD be empty. The receiver +MUST ignore any non-empty command payload. + +There is no error condition for this command. + + + +### 4.2. CMD 1: (Host->NCP) `CMD_RESET` + +Octets: | 1 | 1 +--------|--------|---------- +Fields: | HEADER | CMD_RESET + +Reset NCP command. Causes the NCP to perform a software reset. Due to +the nature of this command, the TID is ignored. The host should +instead wait for a `CMD_PROP_VALUE_IS` command from the NCP indicating +`PROP_LAST_STATUS` has been set to `STATUS_RESET_SOFTWARE`. + +The command payload for this command SHOULD be empty. The receiver +MUST ignore any non-empty command payload. + +If an error occurs, the value of `PROP_LAST_STATUS` will be emitted +instead with the value set to the generated status code for the error. + + + +### 4.3. CMD 2: (Host->NCP) `CMD_PROP_VALUE_GET` + +Octets: | 1 | 1 | 1-3 +--------|--------|--------------------|--------- +Fields: | HEADER | CMD_PROP_VALUE_GET | PROP_ID + +Get property value command. Causes the NCP to emit a +`CMD_PROP_VALUE_IS` command for the given property identifier. + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2. + +If an error occurs, the value of `PROP_LAST_STATUS` will be emitted +instead with the value set to the generated status code for the error. + + + +### 4.4. CMD 3: (Host->NCP) `CMD_PROP_VALUE_SET` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|--------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_SET | PROP_ID | PROP_VALUE + +Set property value command. Instructs the NCP to set the given +property to the specific given value, replacing any previous value. + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the property value. The exact format of the property value is defined +by the property. + +If an error occurs, the value of `PROP_LAST_STATUS` will be emitted +with the value set to the generated status code for the error. + + + +### 4.5. CMD 4: (Host->NCP) `CMD_PROP_VALUE_INSERT` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|-----------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_INSERT | PROP_ID | VALUE_TO_INSERT + +Insert value into property command. Instructs the NCP to insert the +given value into a list-oriented property, without removing other +items in the list. The resulting order of items in the list is defined +by the individual property being operated on. + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the value to be inserted. The exact format of the value is defined by +the property. + +If an error occurs, the value of `PROP_LAST_STATUS` will be emitted +with the value set to the generated status code for the error. + + + +### 4.6. CMD 5: (Host->NCP) `CMD_PROP_VALUE_REMOVE` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|-----------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_REMOVE | PROP_ID | VALUE_TO_REMOVE + +Remove value from property command. Instructs the NCP to remove the +given value from a list-oriented property, without affecting other +items in the list. The resulting order of items in the list is defined +by the individual property being operated on. + +Note that this command operates *by value*, not by index! + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the value to be inserted. The exact format of the value is defined by +the property. + +If an error occurs, the value of `PROP_LAST_STATUS` will be emitted +with the value set to the generated status code for the error. + + +### 4.7. CMD 6: (NCP->Host) `CMD_PROP_VALUE_IS` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|-------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_IS | PROP_ID | PROP_VALUE + +Property value notification command. This command can be sent by the +NCP in response to a previous command from the host, or it can be sent +by the NCP in an unsolicited fashion to notify the host of various +state changes asynchronously. + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the current value of the given property. + + + +### 4.8. CMD 7: (NCP->Host) `CMD_PROP_VALUE_INSERTED` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|-------------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_INSERTED | PROP_ID | PROP_VALUE + +Property value insertion notification command. This command can be +sent by the NCP in response to the `CMD_PROP_VALUE_INSERT` command, or +it can be sent by the NCP in an unsolicited fashion to notify the host +of various state changes asynchronously. + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the value that was inserted into the given property. + +The resulting order of items in the list is defined by the given +property. + +### 4.9. CMD 8: (NCP->Host) `CMD_PROP_VALUE_REMOVED` + +Octets: | 1 | 1 | 1-3 | *n* +--------|--------|------------------------|---------|------------ +Fields: | HEADER | CMD_PROP_VALUE_REMOVED | PROP_ID | PROP_VALUE + +Property value removal notification command. This command can be sent +by the NCP in response to the `CMD_PROP_VALUE_REMOVE` command, or it +can be sent by the NCP in an unsolicited fashion to notify the host of +various state changes asynchronously. + +Note that this command operates *by value*, not by index! + +The payload for this command is the property identifier encoded in the +packed unsigned integer format described in section 7.2, followed by +the value that was removed from the given property. + +The resulting order of items in the list is defined by the given +property. + + + + + + +## 5. General Properties + +While the majority of the properties that allow the configuration +of network connectivity are network protocol specific, there are +several properties that are required in all implementations. + +* 0: `PROP_LAST_STATUS` +* 1: `PROP_PROTOCOL_VERSION` +* 2: `PROP_CAPABILITIES` +* 3: `PROP_NCP_VERSION` +* 4: `PROP_INTERFACE_COUNT` +* 5: `PROP_POWER_STATE` +* 6: `PROP_HWADDR` +* 7: `PROP_LOCK` +* 8: `PROP_HBO_MEM_MAX` (See section C.2.1.) +* 9: `PROP_HBO_BLOCK_MAX` (See section C.2.2.) +* 112: `PROP_STREAM_DEBUG` +* 113: `PROP_STREAM_RAW` +* 114: `PROP_STREAM_NET` +* 115: `PROP_STREAM_NET_INSECURE` + + + + +### 5.1. PROP 0: `PROP_LAST_STATUS` + +* Type: Read-Only +* Encoding: `i` + +Octets: | 1-3 +--------|------------------ +Fields: | PROP_LAST_STATUS + +Describes the status of the last operation. Encoded as a packed +unsigned integer. + +This property is emitted often to indicate the result status of +pretty much any Host-to-NCP operation. + +It is emitted automatically at NCP startup with a value indicating +the reset reason. + +See section 6 for the complete list of status codes. + +### 5.2. PROP 1: `PROP_PROTOCOL_VERSION` + +* Type: Read-Only +* Encoding: `iiii` + +Octets: | 1-3 | 1-3 | 1-3 | 1-3 +--------|----------------|---------------|---------------|------------------- +Fields: | INTERFACE_TYPE | MAJOR_VERSION | MINOR_VERSION | VENDOR_IDENTIFIER + +Describes the protocol version information. This property contains +four fields, each encoded as a packed unsigned integer: + + * Interface Type + * Major Version Number + * Minor Version Number + * Vendor Identifier + +#### 5.2.1 Interface Type #### + +This integer identifies what the network protocol for this NCP. +Currently defined values are: + + * 1: ZigBee + * 2: ZigBeeIP + * 3: Thread + * TBD: BlueTooth Low Energy (BTLE) + +The host MUST enter a FAULT state if it does not recognize the +protocol given by the NCP. + +#### 5.2.2 Major Version Number #### + +The major version number is used to identify large and incompatible +differences between protocol versions. + +The host MUST enter a FAULT state if it does not explicitly support +the given major version number. + +#### 5.2.3 Minor Version Number #### + +The minor version number is used to identify small but otherwise +compatible differences between protocol versions. A mismatch between +the advertised minor version number and the minor version that is +supported by the host SHOULD NOT be fatal to the operation of the +host. + +#### 5.2.3 Vendor Identifier #### + + +### 5.3. PROP 2: `PROP_CAPABILITIES` + +* Type: Read-Only +* Packed-Encoding: `A(i)` + +Octets: | 1-3 | 1-3 | ... +--------|-------|-------|----- +Fields: | CAP_1 | CAP_2 | ... + +Describes the supported capabilities of this NCP. Encoded as a list of +packed unsigned integers. + +A capability is defined as a 21-bit integer that describes a subset of +functionality which is supported by the NCP. + +Currently defined values are: + + * 1: `CAP_LOCK` + * 2: `CAP_NET_SAVE` + * 3: `CAP_HBO`: Host Block Offload (See Section C.) + * 4: `CAP_POWER_SAVE` + * 16: `CAP_802_15_4_2003` + * 17: `CAP_802_15_4_2006` + * 18: `CAP_802_15_4_2011` + * 21: `CAP_802_15_4_PIB` + * 24: `CAP_802_15_4_2450MHZ_OQPSK` + * 25: `CAP_802_15_4_915MHZ_OQPSK` + * 26: `CAP_802_15_4_868MHZ_OQPSK` + * 27: `CAP_802_15_4_915MHZ_BPSK` + * 28: `CAP_802_15_4_868MHZ_BPSK` + * 29: `CAP_802_15_4_915MHZ_ASK` + * 30: `CAP_802_15_4_868MHZ_ASK` + * 48: `CAP_ROLE_ROUTER` + * 49: `CAP_ROLE_SLEEPY` + * 52: `CAP_NET_THREAD_1_0` + +Additionally, future capability allocations SHALL be made from the +following allocation plan: + +Capability Range | Description +----------------------|------------------ +0 - 127 | Reserved for core capabilities +64 - 15,359 | *UNALLOCATED* +15,360 - 16,383 | Vendor-specific +16,384 - 1,999,999 | *UNALLOCATED* +2,000,000 - 2,097,151 | Experimental use only + +### 5.4. PROP 3: `PROP_NCP_VERSION` + +* Type: Read-Only +* Packed-Encoding: `U` + +Octets: | *n* +--------|------------------- +Fields: | `NCP_VESION_STRING` + +Contains a string which describes the firmware currently running on +the NCP. Encoded as a zero-terminated UTF-8 string. + +The format of the string is not strictly defined, but it is intended +to present similarly to the "User-Agent" string from HTTP. The +RECOMMENDED format of the string is as follows: + + /[][; ]; + +Examples: + + * `OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04` + * `ConnectIP/2.0b125 s1 ALPHA; Sept 24 2015 20:49:19` + +### 5.5. PROP 4: `PROP_INTERFACE_COUNT` + +* Type: Read-Only +* Packed-Encoding: `C` + +Octets: | 1 +--------|----------------- +Fields: | `INTERFACE_COUNT` + +Describes the number of concurrent interfaces supported by this NCP. +Since the concurrent interface mechanism is still TBD, this value MUST +always be one. + +This value is encoded as an unsigned 8-bit integer. + +### 5.6. PROP 5: `PROP_POWER_STATE` + +* Type: Read-Write +* Packed-Encoding: `C` + +Octets: | 1 +--------|------------------ +Fields: | `PROP_POWER_STATE` + +Describes the current power state of the NCP. By writing to this +property you can manage the lower state of the NCP. Enumeration is +encoded as a single unsigned byte. + +Defined values are: + + * 0: `POWER_STATE_OFFLINE`: NCP is physically powered off. + (Enumerated for completeness sake, not expected on the wire) + * 1: `POWER_STATE_DEEP_SLEEP`: Almost everything on the NCP is shut + down, but can still be resumed via a command or interrupt. + * 2: `POWER_STATE_STANDBY`: NCP is in the lowest power state that + can still be awoken by an event from the radio (e.g. waiting for + alarm) + * 3: `POWER_STATE_LOW_POWER`: NCP is responsive (and possibly + connected), but using less power. (e.g. "Sleepy" child node) + * 4: `POWER_STATE_ONLINE`: NCP is fully powered. (e.g. "Parent" + node) + +### 5.7. PROP 6: `PROP_HWADDR` + +* Type: Read-Only* +* Packed-Encoding: `E` + +Octets: | 8 +--------|------------ +Fields: | PROP_HWADDR + +The static EUI64 address of the device. This value is read-only, but +may be writable under certain vendor-defined circumstances. + +### 5.8. PROP 6: `PROP_LOCK` + +* Type: Read-Write +* Packed-Encoding: `b` + +Octets: | 1 +--------|------------ +Fields: | PROP_LOCK + +Property lock. Used for grouping changes to several properties to +take effect at once, or to temporarily prevent the automatic updating +of property values. When this property is set, the execution of the +NCP is effectively frozen until it is cleared. + +This property is only supported if the `CAP_LOCK` capability is present. + +### 5.9. PROP 112: `PROP_STREAM_DEBUG` + +* Type: Read-Only-Stream +* Packed-Encoding: `U` + +Octets: | *n* +--------|------------ +Fields: | UTF8_DATA + +This property is a streaming property, meaning that you cannot explicitly +fetch the value of this property. The stream provides human-readable debugging +output which may be displayed in the host logs. + +To receive the debugging stream, you wait for `CMD_PROP_VALUE_IS` commands for +this property from the NCP. + +### 5.10. PROP 113: `PROP_STREAM_RAW` + +* Type: Read-Write-Stream +* Packed-Encoding: `DD` + +Octets: | 2 | *n* | *n* +--------|----------------|------------|---------------- +Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA + +This stream provides the capability of sending and receiving raw packets +to and from the radio. The exact format of the frame metadata and data is +dependent on the MAC and PHY being used. + +This property is a streaming property, meaning that you cannot explicitly +fetch the value of this property. To receive traffic, you wait for +`CMD_PROP_VALUE_IS` commands with this property id from the NCP. + +Implementations may optionally support the ability to transmit arbitrary +raw packets. If this capability is supported, you may call `CMD_PROP_VALUE_SET` +on this property with the value of the raw packet. + +Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format +of the metadata is defined by the associated MAC and PHY being used. + +### 5.11. PROP 114: `PROP_STREAM_NET` + +* Type: Read-Write-Stream +* Packed-Encoding: `DD` + +Octets: | 2 | *n* | *n* +--------|----------------|------------|---------------- +Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA + +This stream provides the capability of sending and receiving data packets +to and from the currently attached network. The exact format of the frame +metadata and data is dependent on the network protocol being used. + +This property is a streaming property, meaning that you cannot explicitly +fetch the value of this property. To receive traffic, you wait for +`CMD_PROP_VALUE_IS` commands with this property id from the NCP. + +To send network packets, you call `CMD_PROP_VALUE_SET` on this property with +the value of the packet. + +Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format +of the metadata is defined by the associated network protocol. + +### 5.12. PROP 114: `PROP_STREAM_NET_INSECURE` + +* Type: Read-Write-Stream +* Packed-Encoding: `DD` + +Octets: | 2 | *n* | *n* +--------|----------------|------------|---------------- +Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA + +This stream provides the capability of sending and receiving unencrypted +and unauthenticated data packets to and from nearby devices for the +purposes of device commissioning. The exact format of the frame +metadata and data is dependent on the network protocol being used. + +This property is a streaming property, meaning that you cannot explicitly +fetch the value of this property. To receive traffic, you wait for +`CMD_PROP_VALUE_IS` commands with this property id from the NCP. + +To send network packets, you call `CMD_PROP_VALUE_SET` on this property with +the value of the packet. + +Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format +of the metadata is defined by the associated network protocol. + + +## 6. Status Codes + + * 0: `STATUS_OK`: Operation has completed successfully. + * 1: `STATUS_FAILURE`: Operation has failed for some undefined + reason. + * 2: `STATUS_UNIMPLEMENTED` + * 3: `STATUS_INVALID_ARGUMENT` + * 4: `STATUS_INVALID_STATE` + * 5: `STATUS_INVALID_COMMAND` + * 6: `STATUS_INVALID_INTERFACE` + * 7: `STATUS_INTERNAL_ERROR` + * 8: `STATUS_SECURITY_ERROR` + * 9: `STATUS_PARSE_ERROR` + * 10: `STATUS_IN_PROGRESS` + * 11: `STATUS_NOMEM` + * 12: `STATUS_BUSY` + * 13: `STATUS_PROPERTY_NOT_FOUND` + * 14: `STATUS_PACKET_DROPPED` + * 15: `STATUS_EMPTY` + * 16-111: *RESERVED* + * 112-127: Reset Causes + * 112: `STATUS_RESET_POWER_ON` + * 113: `STATUS_RESET_EXTERNAL` + * 114: `STATUS_RESET_SOFTWARE` + * 115: `STATUS_RESET_FAULT` + * 116: `STATUS_RESET_CRASH` + * 117: `STATUS_RESET_ASSERT` + * 118: `STATUS_RESET_OTHER` + * 119-127: *RESERVED-RESET-CODES* + * 128 - 15,359: *UNALLOCATED* + * 15,360 - 16,383: Vendor-specific + * 16,384 - 1,999,999: *UNALLOCATED* + * 2,000,000 - 2,097,151: Experimental Use Only (MUST NEVER be used + in production!) + +## 7. Data Packing + +Data serialization for properties is performed using a light-weight +data packing format which was loosely inspired by D-Bus. The format of +a serialization is defined by a specially formatted string. + +Goals: + + * Be very lightweight and favor direct representation of values. + * Use an easily readable and memorable format string. + * Support lists and structures. + * Allow properties to be appended to structures while maintaining + backward compatibility. + +Each primitive datatype has an ASCII character associated with it. +Structures can be represented as strings of these characters. For +example: + + * `"C"`: A single unsigned byte. + * `"C6U"`: A single unsigned byte, followed by a 128-bit IPv6 + address, followed by a zero-terminated UTF8 string. + * `"A(6)"`: An array of IPv6 addresses + +In each case, the data is represented exactly as described. For +example, an array of 10 IPv6 address is stored as 160 bytes. + +### 7.1 Primitive Types + + * 0: `DATATYPE_NULL` + * `'.'`: `DATATYPE_VOID`: Empty data type. Used internally. + * `'b'`: `DATATYPE_BOOL`: Boolean value. Encoded in 8-bits as either + `0x00` or `0x01`. All other values are illegal. + * `'C'`: `DATATYPE_UINT8`: Unsigned 8-bit integer. + * `'c'`: `DATATYPE_INT8`: Signed 8-bit integer. + * `'S'`: `DATATYPE_UINT16`: Unsigned 16-bit integer. (Little-endian) + * `'s'`: `DATATYPE_INT16`: Signed 16-bit integer. (Little-endian) + * `'L'`: `DATATYPE_UINT32`: Unsigned 32-bit integer. (Little-endian) + * `'l'`: `DATATYPE_INT32`: Signed 32-bit integer. (Little-endian) + * `'i'`: `DATATYPE_UINT_PACKED`: Packed Unsigned Integer. (See + section 7.2) + * `'6'`: `DATATYPE_IPv6ADDR`: IPv6 Address. (Big-endian) + * `'E'`: `DATATYPE_EUI64`: EUI-64 Address. (Big-endian) + * `'e'`: `DATATYPE_EUI48`: EUI-48 Address. (Big-endian) + * `'D'`: `DATATYPE_DATA`: Arbitrary Data. (See section 7.3) + * `'U'`: `DATATYPE_UTF8`: Zero-terminated UTF8-encoded string. + * `'T'`: `DATATYPE_STRUCT`: Structured datatype. Compound type. (See + section 7.4) + * `'A'`: `DATATYPE_ARRAY`: Array of datatypes. Compound type. (See + section 7.5) + +### 7.2 Packed Unsigned Integer + +For certain types of integers, such command or property identifiers, +usually have a value on the wire that is less than 127. However, in +order to not preclude the use of values larger than 256, we would need +to add an extra byte. Doing this would add an extra byte to the vast +majority of instances, which can add up in terms of bandwidth. + +The packed unsigned integer format is inspired by the encoding scheme +in UTF8 identical to the unsigned integer format in EXI. + +For all values less than 127, the packed form of the number is simply +a single byte which directly represents the number. For values larger +than 127, the following process is used to encode the value: + +1. The unsigned integer is broken up into *n* 7-bit chunks and placed + into *n* octets, leaving the most significant bit of each octet + unused. +2. Order the octets from least-significant to most-significant. + (Little-endian) +3. Clear the most significant bit of the most significant octet. Set + the least significant bit on all other octets. + +Where *n* is the smallest number of 7-bit chunks you can use to +represent the given value. + +Take the value 1337, for example: + + 1337 => 0x0539 + => [39 0A] + => [B9 0A] + +To decode the value, you collect the 7-bit chunks until you find an +octet with the most significant bit clear. + +### 7.3 Data Blobs + +Data blobs are special datatypes in that the data that they contain +does not inherently define the size of the data. This means that if +the length of the data blob isn't *implied*, then the length of the +blob must be prepended as a packed unsigned integer. + +The length of a data blob is *implied* only when it is the last +datatype in a given buffer. This works because we already know the +size of the buffer, and the length of the data is simply the rest of +the size of the buffer. + +For example, let's say we have a buffer that is encoded with the +datatype signature of `CLLD`. In this case, it is pretty easy to tell +where the start and end of the data blob is: the start is 9 bytes from +the start of the buffer, and its length is the length of the buffer +minus 9. (9 is the number of bytes taken up by a byte and two longs) + +However, things are a little different with `CLDL`. Since our datablob +is no longer the last item in the signature, the length must be +prepended. + +If you are a little confused, keep reading. This theme comes up in a a +few different ways in the following sections. + +When a length is prepended, the length is encoded as a little-endian +unsigned 16-bit integer. + +### 7.4 Structured Data + +The structured data type is a way of bundling together a bunch of data +into a single data structure. This may at first seem useless. What is +the difference between `T(Cii)` and just `Cii`? The answer is, in that +particular case, nothing: they are stored in exactly the same way. + +However, one case where the structure datatype makes a difference is +when you compare `T(Cii)L` to `CiiL`: they end up being represented +entirely differently. This is because the structured data type follows +the exact same semantics as the data blob type: if it isn't the last +datatype in a signature, *it must be prepended with a length*. This is +useful because it allows for new datatypes to be appended to the +structure's signature while remaining *backward parsing +compatibility*. + +More explicitly, if you take data that was encoded with `T(Cii6)L`, +you can still decode it as `T(Cii)L`. + +Let's take, for example, the property `PROP_IPv6_ADDR_TABLE`. +Conceptually it is just a list of IPv6 addresses, so we can encode it +as `A(6c)`. However, if we ever want to associate more data with the +type (like flags), we break our backward compatibility if we add +another member and use `A(6cC)`. To allow for data to be added without +breaking backward compatibility, we use the structured data type from +the start: `A(T(6c))`. Then when we add a new member to the structure +(`A(T(6cC))`), we don't break backward compatibility. + +It's also worth noting that `T(Cii)L` also parses as `DL`. You could +then take the resultant data blob and parse it as `Cii`. + +When a length is prepended, the length is encoded as a little-endian +unsigned 16-bit integer. + +### 7.5 Arrays + +An array is simply a concatenated set of *n* data encodings. For example, +the type `A(6)` is simply a list of IPv6 addresses---one after the other. + +Just like the data blob type and the structured data type, the length +of the entire array must be prepended *unless* the array is the last +type in a given signature. Thus, `A(C)` (An array of unsigned bytes) +encodes identically to `D`. + +When a length is prepended, the length is encoded as a little-endian +unsigned 16-bit integer. + +## A. Framing Protocol + +Since this NCP protocol is defined independently of framing, any +number of framing protocols could be used successfully. However, +in the interests of cross-compatibility, we recommend using +HDLC-Lite for framing when using this NCP protocol with a UART. + +A SPI-specific framing mechanism is currently TBD. + +## B. Feature: Network Save + +The network save feature is an optional NCP capability that, when +present, allows the host to save and recall network credentials and +state to and from nonvolatile storage. + +The presence of this feature can be detected by checking for the +presence of the `CAP_NET_SAVE` capability in `PROP_CAPABILITIES`. + +### B.1. Commands + +#### B.1.1. CMD 9: (Host->NCP) `CMD_NET_SAVE` + +Octets: | 1 | 1 +--------|--------|-------------- +Fields: | HEADER | CMD_NET_SAVE + +Save network state command. Saves any current network credentials and +state necessary to reconnect to the current network to non-volatile +memory. + +This operation affects non-volatile memory only. The current network +information stored in volatile memory is unaffected. + +The response to this command is always a `CMD_PROP_VALUE_IS` for +`PROP_LAST_STATUS`, indicating the result of the operation. + +This command is only available if the `CAP_NET_SAVE` capability is +set. + + + +#### B.1.2. CMD 10: (Host->NCP) `CMD_NET_CLEAR` + +Octets: | 1 | 1 +--------|--------|--------------- +Fields: | HEADER | CMD_NET_CLEAR + +Clear saved network state command. Clears any previously saved network +credentials and state previously stored by `CMD_NET_SAVE` from +non-volatile memory. + +This operation affects non-volatile memory only. The current network +information stored in volatile memory is unaffected. + +The response to this command is always a `CMD_PROP_VALUE_IS` for +`PROP_LAST_STATUS`, indicating the result of the operation. + +This command is only available if the `CAP_NET_SAVE` capability is +set. + + + +#### B.1.3. CMD 11: (Host->NCP) `CMD_NET_RECALL` + +Octets: | 1 | 1 +--------|--------|---------------- +Fields: | HEADER | CMD_NET_RECALL + +Recall saved network state command. Recalls any previously saved +network credentials and state previously stored by `CMD_NET_SAVE` from +non-volatile memory. + +This command will typically generated several unsolicited property +updates as the network state is loaded. At the conclusion of loading, +the authoritative response to this command is always a +`CMD_PROP_VALUE_IS` for `PROP_LAST_STATUS`, indicating the result of +the operation. + +This command is only available if the `CAP_NET_SAVE` capability is +set. + + +## C. Feature: Host Buffer Offload + +The memory on an NCP may be much more limited than the memory on +the host processor. In such situations, it is sometimes useful +for the NCP to offload buffers to the host processor temporarily +so that it can perform other operations. + +Host buffer offload is an optional NCP capability that, when +present, allows the NCP to store data buffers on the host processor +that can be recalled at a later time. + +The presence of this feature can be detected by the host by +checking for the presence of the `CAP_HBO` +capability in `PROP_CAPABILITIES`. + +### C.1. Commands + +#### C.1.1. CMD 12: (NCP->Host) `CMD_HBO_OFFLOAD` + +* Argument-Encoding: `LscD` + * `OffloadId`: 32-bit unique block identifier + * `Expiration`: In seconds-from-now + * `Priority`: Critical, High, Medium, Low + * `Data`: Data to offload + +#### C.1.2. CMD 13: (NCP->Host) `CMD_HBO_RECLAIM` + * Argument-Encoding: `Lb` + * `OffloadId`: 32-bit unique block identifier + * `KeepAfterReclaim`: If not set to true, the block will be + dropped by the host after it is sent to the NCP. + +#### C.1.3. CMD 14: (NCP->Host) `CMD_HBO_DROP` + +* Argument-Encoding: `L` + * `OffloadId`: 32-bit unique block identifier + +#### C.1.1. CMD 15: (Host->NCP) `CMD_HBO_OFFLOADED` + +* Argument-Encoding: `Li` + * `OffloadId`: 32-bit unique block identifier + * `Status`: Status code for the result of the operation. + +#### C.1.2. CMD 16: (Host->NCP) `CMD_HBO_RECLAIMED` + +* Argument-Encoding: `LiD` + * `OffloadId`: 32-bit unique block identifier + * `Status`: Status code for the result of the operation. + * `Data`: Data that was previously offloaded (if any) + +#### C.1.3. CMD 17: (Host->NCP) `CMD_HBO_DROPPED` + +* Argument-Encoding: `Li` + * `OffloadId`: 32-bit unique block identifier + * `Status`: Status code for the result of the operation. + +### C.2. Properties + +#### C.2.1. PROP 6: `PROP_HBO_MEM_MAX` + +* Type: Read-Write +* Packed-Encoding: `L` + +Octets: | 4 +--------|----------------- +Fields: | `PROP_HBO_MEM_MAX` + +Describes the number of bytes that may be offloaded from the NCP to +the host. Default value is zero, so this property must be set by the +host to a non-zero value before the NCP will begin offloading blocks. + +This value is encoded as an unsigned 32-bit integer. + +This property is only available if the `CAP_HBO` +capability is present in `PROP_CAPABILITIES`. + +#### C.2.1. PROP 7: `PROP_HBO_BLOCK_MAX` + +* Type: Read-Write +* Packed-Encoding: `S` + +Octets: | 2 +--------|----------------- +Fields: | `PROP_HBO_BLOCK_MAX` + +Describes the number of blocks that may be offloaded from the NCP to +the host. Default value is 32. Setting this value to zero will cause +host block offload to be effectively disabled. + +This value is encoded as an unsigned 16-bit integer. + +This property is only available if the `CAP_HBO` +capability is present in `PROP_CAPABILITIES`. + + + + +## D. Protocol: Thread + +This section describes all of the properties and semantics required +for managing a thread NCP. + +### D.1. PHY Properties +#### D.1.1. PROP x: `PROP_PHY_ENABLED` +* Type: Read-Only +* Packed-Encoding: `b` + +#### D.1.4. PROP x: `PROP_PHY_CHAN` +* Type: Read-Write +* Packed-Encoding: `C` + +#### D.1.5. PROP x: `PROP_PHY_CHAN_SUPPORTED` +* Type: Read-Only +* Packed-Encoding: `A(C)` +* Unit: List of channels + +#### D.1.3. PROP x: `PROP_PHY_FREQ` +* Type: Read-Only +* Packed-Encoding: `L` +* Unit: Kilohertz + +#### D.1.6. PROP x: `PROP_PHY_CCA_THRESHOLD` +* Type: Read-Write +* Packed-Encoding: `c` +* Unit: dBm + +#### D.1.7. PROP x: `PROP_PHY_TX_POWER` +* Type: Read-Write +* Packed-Encoding: `c` +* Unit: dBm + +#### D.1.6. PROP x: `PROP_PHY_RSSI` +* Type: Read-Only +* Packed-Encoding: `c` +* Unit: dBm + +#### D.1.7. PROP x: `PROP_PHY_RAW_STREAM_ENABLED` +* Type: Read-Write +* Packed-Encoding: `b` + +Set to true to enable raw frames to be emitted from `PROP_STREAM_RAW`. + + + +### D.2. MAC Properties + + +#### D.2.1. PROP x: `PROP_MAC_SCAN_STATE` +* Type: Read-Write +* Packed-Encoding: `C` +* Unit: Enumeration + +Possible Values: + +* 0: `SCAN_STATE_IDLE` +* 1: `SCAN_STATE_BEACON` +* 2: `SCAN_STATE_ENERGY` + +Set to `SCAN_STATE_BEACON` to start an active scan. +Beacons will be emitted from `PROP_MAC_SCAN_BEACON`. + +Set to `SCAN_STATE_ENERGY` to start an energy scan. +Channel energy will be reported by alternating emissions +of `PROP_PHY_CHAN` and `PROP_PHY_RSSI`. + +Values switches to `SCAN_STATE_IDLE` when scan is complete. + +#### D.2.2. PROP x: `PROP_MAC_SCAN_MASK` +* Type: Read-Write +* Packed-Encoding: `A(C)` +* Unit: List of channels to scan + +#### D.2.3. PROP x: `PROP_MAC_SCAN_BEACON` +* Type: Read-Only-Stream +* Packed-Encoding: `CcT(ESSC)T(i).` + +chan,rssi,(laddr,saddr,panid,lqi),(proto,xtra) + +chan,rssi,(laddr,saddr,panid,lqi),(proto,flags,networkid,xpanid) [CcT(ESSC)T(iCUD.).] + +#### D.2.4. PROP x: `PROP_MAC_15_4_LADDR` +* Type: Read-Write +* Packed-Encoding: `E` + +#### D.2.5. PROP x: `PROP_MAC_15_4_SADDR` +* Type: Read-Write +* Packed-Encoding: `S` + +#### D.2.6. PROP x: `PROP_MAC_15_4_PANID` +* Type: Read-Write +* Packed-Encoding: `S` + + + + + +### D.3. NET Properties + +#### D.3.1. PROP x: `PROP_NET_SAVED` +* Type: Read-Only +* Packed-Encoding: `b` + +#### D.3.2. PROP x: `PROP_NET_ENABLED` +* Type: Read-Only +* Packed-Encoding: `b` + +#### D.3.3. PROP x: `PROP_NET_STATE` +* Type: Read-Write +* Packed-Encoding: `C` +* Unit: Enumeration + +Values: + +* 0: `NET_STATE_OFFLINE` +* 1: `NET_STATE_DETACHED` +* 2: `NET_STATE_ATTACHING` +* 3: `NET_STATE_ATTACHED` + +#### D.3.4. PROP x: `PROP_NET_ROLE` +* Type: Read-Write +* Packed-Encoding: `C` +* Unit: Enumeration + +Values: + +* 0: `NET_ROLE_NONE` +* 1: `NET_ROLE_CHILD` +* 2: `NET_ROLE_ROUTER` +* 3: `NET_ROLE_LEADER` + +#### D.3.5. PROP x: `PROP_NET_NETWORK_NAME` +* Type: Read-Write +* Packed-Encoding: `U` + +#### D.3.6. PROP x: `PROP_NET_XPANID` +* Type: Read-Write +* Packed-Encoding: `D` + +#### D.3.7. PROP x: `PROP_NET_MASTER_KEY` +* Type: Read-Write +* Packed-Encoding: `D` + +#### D.3.8. PROP x: `PROP_NET_KEY_SEQUENCE` +* Type: Read-Write +* Packed-Encoding: `L` + +#### D.3.9. PROP x: `PROP_NET_PARTITION_ID` +* Type: Read-Write +* Packed-Encoding: `L` + + + +#### D.4. THREAD Properties + + +#### D.4.1. PROP x: `PROP_THREAD_LEADER` +* Type: Read-Write +* Packed-Encoding: `6` + +#### D.4.2. PROP x: `PROP_THREAD_PARENT` +* Type: Read-Write +* Packed-Encoding: `ES` +* LADDR, SADDR + +#### D.4.3. PROP x: `PROP_THREAD_CHILD_TABLE` +* Type: Read-Write +* Packed-Encoding: `A(T(ES))` +* LADDR, SADDR + + + +### D.5. IPv6 Properties + +#### D.5.1. PROP x: `PROP_IPV6_LL_ADDR` +* Type: Read-Only +* Packed-Encoding: `6` + +IPv6 Address + +#### D.5.2. PROP x: `PROP_IPV6_ML_ADDR` +* Type: Read-Only +* Packed-Encoding: `6` + +IPv6 Address + Prefix Length + +#### D.5.2. PROP x: `PROP_IPV6_ML_PREFIX` +* Type: Read-Write +* Packed-Encoding: `6C` + +IPv6 Prefix + Prefix Length + +#### D.5.3. PROP x: `PROP_IPV6_ADDRESS_TABLE` + +* Type: Read-Write +* Packed-Encoding: `A(T(6CLLC))` + +Array of structures containing: + +* `6`: IPv6 Address +* `C`: Network Prefix Length +* `L`: Valid Lifetime +* `L`: Preferred Lifetime +* `C`: Flags + +#### D.4.3. PROP x: `PROP_IPv6_ROUTE_TABLE` +* Type: Read-Write +* Packed-Encoding: `A(T(6C6))` + +Array of structures containing: + +* `6`: IPv6 Address +* `C`: Network Prefix Length +* `6`: Next Hop diff --git a/src/ncp/flen.cpp b/src/ncp/flen.cpp new file mode 100644 index 000000000..1072195eb --- /dev/null +++ b/src/ncp/flen.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements an FLEN encoder and decoder. + */ + +#include +#include + +namespace Thread { +namespace Flen { + +enum +{ + kFlagSequence = 0x7e, ///< FLen Flag value +}; + +ThreadError Encoder::Init(uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + + mOutBuf = aOutBuf; + + VerifyOrExit(aOutLength >= 3, error = kThreadError_NoBufs); + aOutBuf[0] = kFlagSequence; + // Leave the next two bytes empty. + aOutLength = 3; + mOutLength = 0; + +exit: + return error; +} + +ThreadError Encoder::Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(mOutOffset + 1 < aOutLength, error = kThreadError_NoBufs); + aOutBuf[mOutOffset++] = aInByte; + +exit: + return error; +} + +ThreadError Encoder::Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + + mOutOffset = 0; + + for (int i = 0; i < aInLength; i++) + { + SuccessOrExit(error = Encode(aInBuf[i], aOutBuf, aOutLength)); + } + +exit: + aOutLength = mOutOffset; + mOutLength += aOutLength; + return error; +} + +ThreadError Encoder::Finalize(uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(mOutOffset < aOutLength, error = kThreadError_NoBufs); + + mOutBuf[1] = (mOutLength >> 8); + mOutBuf[2] = (mOutLength & 0xFF); + + aOutLength = 0; + +exit: + return error; +} + + +Decoder::Decoder(uint8_t *aOutBuf, uint16_t aOutLength, FrameHandler aFrameHandler, void *aContext) +{ + mState = kStateNeedFlag; + mFrameHandler = aFrameHandler; + mContext = aContext; + mOutBuf = aOutBuf; + mOutOffset = 0; + mOutLength = aOutLength; +} + +void Decoder::Decode(const uint8_t *aInBuf, uint16_t aInLength) +{ + uint8_t byte; + + for (int i = 0; i < aInLength; i++) + { + byte = aInBuf[i]; + + switch (mState) + { + case kStateNeedFlag: + if (byte == kFlagSequence) + { + mState = kStateNeedLenH; + mOutOffset = 0; + } + + break; + + case kStateNeedLenH: + mReadLength = (byte << 8); + mState = kStateNeedLenL; + break; + + case kStateNeedLenL: + mReadLength += byte; + + if (mReadLength > mOutLength) + { + // Too big. + mState = kStateNeedFlag; + } + else + { + mState = kStateNeedData; + } + + break; + + case kStateNeedData: + mOutBuf[mOutOffset++] = byte; + + if (mOutOffset >= mReadLength) + { + mState = kStateNeedFlag; + mFrameHandler(mContext, mOutBuf, mReadLength); + } + + break; + } + } +} + +} // namespace Flen +} // namespace Thread diff --git a/src/ncp/flen.hpp b/src/ncp/flen.hpp new file mode 100644 index 000000000..ad485c50b --- /dev/null +++ b/src/ncp/flen.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for an FLEN (flag/length) encoder and decoder. + */ + +#ifndef FLEN_HPP_ +#define FLEN_HPP_ + +#include +#include + +namespace Thread { + +/** + * @namespace Thread::Flen + * + * @brief + * This namespace includes definitions for the FLEN encoder and decoder. + * + */ +namespace Flen { + +/** + * This class implements the FLEN encoder. + * + */ +class Encoder +{ +public: + /** + * This method begins an FLEN frame and puts the initial bytes into @p aOutBuf. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[inout] aOutLength On entry, the output buffer size; On exit, the output length. + * + * @retval kThreadError_None Successfully started the FLEN frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to start the FLEN frame. + * + */ + ThreadError Init(uint8_t *aOutBuf, uint16_t &aOutLength); + + /** + * This method encodes the frame. + * + * @param[in] aInBuf A pointer to the input buffer. + * @param[in] aInLength The number of bytes in @p aInBuf to encode. + * @param[out] aOutBuf A pointer to the output buffer. + * @param[out] aOutLength On exit, the number of bytes placed in @p aOutBuf. + * + * @retval kThreadError_None Successfully encoded the FLEN frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to encode the FLEN frame. + * + */ + ThreadError Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength); + + /** + * This method ends an FLEN frame and puts the initial bytes into @p aOutBuf. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[inout] aOutLength On entry, the output buffer size; On exit, the output length. + * + * @retval kThreadError_None Successfully ended the FLEN frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to end the FLEN frame. + * + */ + ThreadError Finalize(uint8_t *aOutBuf, uint16_t &aOutLength); + +private: + ThreadError Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength); + + uint8_t *mOutBuf; + uint16_t mOutOffset; + uint16_t mOutLength; +}; + +/** + * This class implements the FLEN decoder. + * + */ +class Decoder +{ +public: + /** + * This function pointer is called when a complete frame has been formed. + * + * @param[in] aContext A pointer to arbitrary context information. + * @param[in] aFrame A pointer to the frame. + * @param[in] aFrameLength The frame length in bytes. + * + */ + typedef void (*FrameHandler)(void *aContext, uint8_t *aFrame, uint16_t aFrameLength); + + /** + * This constructor initializes the decoder. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[in] aOutLength Size of the output buffer in bytes. + * @param[in] aFrameHandler A pointer to a function that is called when a complete frame is received. + * @param[in] aContext A pointer to arbitrary context information. + * + */ + Decoder(uint8_t *aOutBuf, uint16_t aOutLength, FrameHandler aFrameHandler, void *aContext); + + /** + * This method streams bytes into the decoder. + * + * @param[in] aInBuf A pointer to the input buffer. + * @param[in] aInLength The number of bytes in @p aInBuf. + * + */ + void Decode(const uint8_t *aInBuf, uint16_t aInLength); + +private: + enum State + { + kStateNeedFlag = 0, + kStateNeedLenH, + kStateNeedLenL, + kStateNeedData, + }; + State mState; + + FrameHandler mFrameHandler; + void *mContext; + + uint8_t *mOutBuf; + uint16_t mOutOffset; + uint16_t mOutLength; + uint16_t mReadLength; +}; + +} // namespace Flen +} // namespace Thread + +#endif // FLEN_HPP_ diff --git a/src/ncp/hdlc.cpp b/src/ncp/hdlc.cpp new file mode 100644 index 000000000..aceb0bae2 --- /dev/null +++ b/src/ncp/hdlc.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements an HDLC-lite encoder and decoder. + */ + +#include +#include + +namespace Thread { +namespace Hdlc { + +/** + * This method updates an FCS. + * + * @param[in] aFcs The FCS to update. + * @param[in] aByte The intput byte value. + * + * @returns The updated FCS. + * + */ +static uint16_t UpdateFcs(uint16_t aFcs, uint8_t aByte); + +enum +{ + kFlagSequence = 0x7e, ///< HDLC Flag value + kEscapeSequence = 0x7d, ///< HDLC Escape value +}; + +/** + * FCS lookup table + * + */ +enum +{ + kInitFcs = 0xffff, ///< Initial FCS value. + kGoodFcs = 0xf0b8, ///< Good FCS value. +}; + +uint16_t UpdateFcs(uint16_t aFcs, uint8_t aByte) +{ + static const uint16_t sFcsTable[256] = + { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 + }; + return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff]; +} + +ThreadError Encoder::Init(uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + + mFcs = kInitFcs; + + VerifyOrExit(aOutLength > 0, error = kThreadError_NoBufs); + aOutBuf[0] = kFlagSequence; + aOutLength = 1; + +exit: + return error; +} + +ThreadError Encoder::Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength) +{ + ThreadError error = kThreadError_None; + + mFcs = UpdateFcs(mFcs, aInByte); + + if (aInByte == kFlagSequence || aInByte == kEscapeSequence) + { + VerifyOrExit(mOutOffset + 2 < aOutLength, error = kThreadError_NoBufs); + aOutBuf[mOutOffset++] = kEscapeSequence; + aOutBuf[mOutOffset++] = aInByte ^ 0x20; + } + else + { + VerifyOrExit(mOutOffset + 1 < aOutLength, error = kThreadError_NoBufs); + aOutBuf[mOutOffset++] = aInByte; + } + +exit: + return error; +} + +ThreadError Encoder::Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + + mOutOffset = 0; + + for (int i = 0; i < aInLength; i++) + { + SuccessOrExit(error = Encode(aInBuf[i], aOutBuf, aOutLength)); + } + +exit: + aOutLength = mOutOffset; + return error; +} + +ThreadError Encoder::Finalize(uint8_t *aOutBuf, uint16_t &aOutLength) +{ + ThreadError error = kThreadError_None; + uint16_t fcs = mFcs; + + mOutOffset = 0; + + fcs ^= 0xffff; + + SuccessOrExit(error = Encode(fcs, aOutBuf, aOutLength)); + SuccessOrExit(error = Encode(fcs >> 8, aOutBuf, aOutLength)); + + VerifyOrExit(mOutOffset < aOutLength, error = kThreadError_NoBufs); + aOutBuf[mOutOffset++] = kFlagSequence; + + aOutLength = mOutOffset; + +exit: + return error; +} + +Decoder::Decoder(uint8_t *aOutBuf, uint16_t aOutLength, FrameHandler aFrameHandler, void *aContext) +{ + mState = kStateNoSync; + mFrameHandler = aFrameHandler; + mContext = aContext; + mOutBuf = aOutBuf; + mOutOffset = 0; + mOutLength = aOutLength; +} + +void Decoder::Decode(const uint8_t *aInBuf, uint16_t aInLength) +{ + uint8_t byte; + + for (int i = 0; i < aInLength; i++) + { + byte = aInBuf[i]; + + switch (mState) + { + case kStateNoSync: + if (byte == kFlagSequence) + { + mState = kStateSync; + mOutOffset = 0; + mFcs = kInitFcs; + } + + break; + + case kStateSync: + switch (byte) + { + case kEscapeSequence: + mState = kStateEscaped; + break; + + case kFlagSequence: + if (mOutOffset > 0) + { + if (mFcs == kGoodFcs) + { + mFrameHandler(mContext, mOutBuf, mOutOffset - 2); + } + + mOutOffset = 0; + mFcs = kInitFcs; + } + + break; + + default: + if (mOutOffset < mOutLength) + { + mFcs = UpdateFcs(mFcs, byte); + mOutBuf[mOutOffset++] = byte; + } + else + { + mState = kStateNoSync; + } + + break; + } + + break; + + case kStateEscaped: + if (mOutOffset < mOutLength) + { + byte ^= 0x20; + mFcs = UpdateFcs(mFcs, byte); + mOutBuf[mOutOffset++] = byte; + mState = kStateSync; + } + else + { + mState = kStateNoSync; + } + + + break; + } + } +} + +} // namespace Hdlc +} // namespace Thread diff --git a/src/ncp/hdlc.hpp b/src/ncp/hdlc.hpp new file mode 100644 index 000000000..8ee16a44b --- /dev/null +++ b/src/ncp/hdlc.hpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for an HDLC-lite encoder and decoer. + */ + +#ifndef HDLC_HPP_ +#define HDLC_HPP_ + +#include +#include + +namespace Thread { + +/** + * @namespace Thread::Hdlc + * + * @brief + * This namespace includes definitions for the HDLC-lite encoder and decoder. + * + */ +namespace Hdlc { + +/** + * This class implements the HDLC-lite encoder. + * + */ +class Encoder +{ +public: + /** + * This method begins an HDLC frame and puts the initial bytes into @p aOutBuf. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[inout] aOutLength On entry, the output buffer size; On exit, the output length. + * + * @retval kThreadError_None Successfully started the HDLC frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to start the HDLC frame. + * + */ + ThreadError Init(uint8_t *aOutBuf, uint16_t &aOutLength); + + /** + * This method encodes the frame. + * + * @param[in] aInBuf A pointer to the input buffer. + * @param[in] aInLength The number of bytes in @p aInBuf to encode. + * @param[out] aOutBuf A pointer to the output buffer. + * @param[out] aOutLength On exit, the number of bytes placed in @p aOutBuf. + * + * @retval kThreadError_None Successfully encoded the HDLC frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to encode the HDLC frame. + * + */ + ThreadError Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength); + + /** + * This method ends an HDLC frame and puts the initial bytes into @p aOutBuf. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[inout] aOutLength On entry, the output buffer size; On exit, the output length. + * + * @retval kThreadError_None Successfully ended the HDLC frame. + * @retval kThreadError_NoBufs Insufficient buffer space available to end the HDLC frame. + * + */ + ThreadError Finalize(uint8_t *aOutBuf, uint16_t &aOutLength); + +private: + ThreadError Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength); + + uint16_t mOutOffset; + uint16_t mFcs; +}; + +/** + * This class implements the HDLC-lite decoder. + * + */ +class Decoder +{ +public: + /** + * This function pointer is called when a complete frame has been formed. + * + * @param[in] aContext A pointer to arbitrary context information. + * @param[in] aFrame A pointer to the frame. + * @param[in] aFrameLength The frame length in bytes. + * + */ + typedef void (*FrameHandler)(void *aContext, uint8_t *aFrame, uint16_t aFrameLength); + + /** + * This constructor initializes the decoder. + * + * @param[in] aOutBuf A pointer to the output buffer. + * @param[in] aOutLength Size of the output buffer in bytes. + * @param[in] aFrameHandler A pointer to a function that is called when a complete frame is received. + * @param[in] aContext A pointer to arbitrary context information. + * + */ + Decoder(uint8_t *aOutBuf, uint16_t aOutLength, FrameHandler aFrameHandler, void *aContext); + + /** + * This method streams bytes into the decoder. + * + * @param[in] aInBuf A pointer to the input buffer. + * @param[in] aInLength The number of bytes in @p aInBuf. + * + */ + void Decode(const uint8_t *aInBuf, uint16_t aInLength); + +private: + enum State + { + kStateNoSync = 0, + kStateSync, + kStateEscaped, + }; + State mState; + + FrameHandler mFrameHandler; + void *mContext; + + uint8_t *mOutBuf; + uint16_t mOutOffset; + uint16_t mOutLength; + + uint16_t mFcs; +}; + +} // namespace Hdlc +} // namespace Thread + +#endif // HDLC_HPP_ diff --git a/src/ncp/ncp.cpp b/src/ncp/ncp.cpp new file mode 100644 index 000000000..f89d01767 --- /dev/null +++ b/src/ncp/ncp.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements an HDLC interface to the Thread stack. + */ + +#include +#include +#include + +namespace Thread { + +static Tasklet sSendDoneTask(&Ncp::SendDoneTask, NULL); +static Tasklet sReceiveTask(&Ncp::ReceiveTask, NULL); +static Ncp *sNcp; + +Ncp::Ncp(): + NcpBase(), + mFrameDecoder(mReceiveFrame, sizeof(mReceiveFrame), &HandleFrame, this) +{ + sNcp = this; +} + +ThreadError Ncp::Start() +{ + otPlatSerialEnable(); + return super_t::Start(); +} + +ThreadError Ncp::Stop() +{ + otPlatSerialDisable(); + return super_t::Stop(); +} + +uint16_t +Ncp::OutboundFrameGetRemaining(void) +{ + return static_cast(sizeof(mSendFrame) - (mSendFrameIter - mSendFrame)); +} + +ThreadError +Ncp::OutboundFrameBegin(void) +{ + ThreadError errorCode; + uint16_t outLength; + + mSendFrameIter = mSendFrame; + outLength = OutboundFrameGetRemaining(); + + errorCode = mFrameEncoder.Init(mSendFrameIter, outLength); + + if (errorCode == kThreadError_None) + { + mSendFrameIter += outLength; + } + + return errorCode; +} + +ThreadError +Ncp::OutboundFrameFeedData(const uint8_t *frame, uint16_t frameLength) +{ + ThreadError errorCode; + uint16_t outLength(OutboundFrameGetRemaining()); + + errorCode = mFrameEncoder.Encode(frame, frameLength, mSendFrameIter, outLength); + + if (errorCode == kThreadError_None) + { + mSendFrameIter += outLength; + } + + return errorCode; +} + +ThreadError +Ncp::OutboundFrameFeedMessage(Message &message) +{ + ThreadError errorCode; + uint16_t inLength; + uint16_t outLength; + uint8_t inBuf[16]; + + for (int offset = 0; offset < message.GetLength(); offset += sizeof(inBuf)) + { + outLength = OutboundFrameGetRemaining(); + inLength = message.Read(offset, sizeof(inBuf), inBuf); + + errorCode = OutboundFrameFeedData(inBuf, inLength); + + if (errorCode != kThreadError_None) + { + break; + } + } + + return errorCode; +} + +ThreadError +Ncp::OutboundFrameSend(void) +{ + ThreadError errorCode; + uint16_t outLength(OutboundFrameGetRemaining()); + + errorCode = mFrameEncoder.Finalize(mSendFrameIter, outLength); + + if (errorCode == kThreadError_None) + { + mSendFrameIter += outLength; + errorCode = otPlatSerialSend(mSendFrame, mSendFrameIter - mSendFrame); + } + + if (errorCode == kThreadError_None) + { + mSending = true; + } + + return errorCode; +} + +extern "C" void otPlatSerialSignalSendDone() +{ + sSendDoneTask.Post(); +} + +void Ncp::SendDoneTask(void *context) +{ + sNcp->SendDoneTask(); +} + +void Ncp::SendDoneTask() +{ + mSending = false; + + if (mSendMessage) + { + Message::Free(*mSendMessage); + mSendMessage = NULL; + } + + super_t::HandleSendDone(); +} + +extern "C" void otPlatSerialSignalReceive() +{ + sReceiveTask.Post(); +} + +void Ncp::ReceiveTask(void *context) +{ + sNcp->ReceiveTask(); +} + +void Ncp::ReceiveTask() +{ + const uint8_t *buf; + uint16_t bufLength; + + buf = otPlatSerialGetReceivedBytes(&bufLength); + + mFrameDecoder.Decode(buf, bufLength); + + otPlatSerialHandleReceiveDone(); +} + +void Ncp::HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength) +{ + sNcp->HandleFrame(aBuf, aBufLength); +} + +void Ncp::HandleFrame(uint8_t *aBuf, uint16_t aBufLength) +{ + super_t::HandleReceive(aBuf, aBufLength); +} + +} // namespace Thread diff --git a/src/ncp/ncp.hpp b/src/ncp/ncp.hpp new file mode 100644 index 000000000..d995ede13 --- /dev/null +++ b/src/ncp/ncp.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file contains definitions for an FLEN/HDLC interface to the OpenThread stack. + */ + +#ifndef NCP_HPP_ +#define NCP_HPP_ + +#include +#include +#include + +namespace Thread { + +class Ncp : public NcpBase +{ + typedef NcpBase super_t; + +public: + Ncp(); + + ThreadError Start(); + ThreadError Stop(); + + + virtual ThreadError OutboundFrameBegin(void); + virtual uint16_t OutboundFrameGetRemaining(void); + virtual ThreadError OutboundFrameFeedData(const uint8_t *frame, uint16_t frameLength); + virtual ThreadError OutboundFrameFeedMessage(Message &message); + virtual ThreadError OutboundFrameSend(void); + + static void HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength); + static void SendDoneTask(void *context); + static void ReceiveTask(void *context); + +private: + void HandleFrame(uint8_t *aBuf, uint16_t aBufLength); + void SendDoneTask(); + void ReceiveTask(); + + Hdlc::Encoder mFrameEncoder; + Hdlc::Decoder mFrameDecoder; + + uint8_t mSendFrame[1500]; + uint8_t mReceiveFrame[1500]; + uint8_t *mSendFrameIter; + Message *mSendMessage; +}; + +} // namespace Thread + +#endif // NCP_HPP_ diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp new file mode 100644 index 000000000..b4b6d3ad0 --- /dev/null +++ b/src/ncp/ncp_base.cpp @@ -0,0 +1,1572 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements a Spinel interface to the OpenThread stack. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { + +extern ThreadNetif *sThreadNetif; + +static spinel_status_t ThreadErrorToSpinelStatus(ThreadError error) +{ + spinel_status_t ret; + + switch (error) + { + case kThreadError_None: + ret = SPINEL_STATUS_OK; + break; + + case kThreadError_Failed: + ret = SPINEL_STATUS_FAILURE; + break; + + case kThreadError_Drop: + ret = SPINEL_STATUS_DROPPED; + break; + + case kThreadError_NoBufs: + ret = SPINEL_STATUS_NOMEM; + break; + + case kThreadError_Busy: + ret = SPINEL_STATUS_BUSY; + break; + + case kThreadError_Parse: + ret = SPINEL_STATUS_PARSE_ERROR; + break; + + case kThreadError_InvalidArgs: + ret = SPINEL_STATUS_INVALID_ARGUMENT; + break; + + case kThreadError_NotImplemented: + ret = SPINEL_STATUS_UNIMPLEMENTED; + break; + + case kThreadError_InvalidState: + ret = SPINEL_STATUS_INVALID_STATE; + break; + + default: + ret = SPINEL_STATUS_FAILURE; + break; + } + + return ret; +} + +NcpBase::NcpBase(): + mNetifHandler(&HandleUnicastAddressesChanged, this), + mUpdateAddressesTask(&RunUpdateAddressesTask, this) +{ + mChannelMask = (0xFFFF << 11); // Default to all channels +} + +ThreadError NcpBase::Start() +{ + assert(sThreadNetif != NULL); + sThreadNetif->RegisterHandler(mNetifHandler); + Ip6::Ip6::SetNcpReceivedHandler(&HandleReceivedDatagram, this); + return kThreadError_None; +} + +ThreadError NcpBase::Stop() +{ + return kThreadError_None; +} + +void NcpBase::HandleReceivedDatagram(void *context, Message &message) +{ + NcpBase *obj = reinterpret_cast(context); + obj->HandleReceivedDatagram(message); +} + +void NcpBase::HandleReceivedDatagram(Message &message) +{ + ThreadError errorCode; + + if (mSending == false) + { + errorCode = OutboundFrameBegin(); + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedPacked( + "CiiS", + SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + SPINEL_CMD_PROP_VALUE_IS, + SPINEL_PROP_STREAM_NET, + message.GetLength() + ); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedMessage(message); + } + + // TODO: Append any metadata here! + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameSend(); + } + + if (errorCode != kThreadError_None) + { + SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED); + } + } + else + { + if (mSendQueue.Enqueue(message) != kThreadError_None) + { + Message::Free(message); + } + } +} + +static NcpBase *gActiveScanContextHack = NULL; + +void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *result) +{ + if (gActiveScanContextHack) + { + gActiveScanContextHack->HandleActiveScanResult(result); + } +} + +void NcpBase::HandleActiveScanResult(otActiveScanResult *result) +{ + VerifyOrExit(mSending == false, ;); + + if (result) + { + uint8_t flags = (result->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT); + + if (result->mIsJoinable) + { + flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE; + } + + if (result->mIsNative) + { + flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE; + } + + //chan,rssi,(laddr,saddr,panid,lqi),(proto,flags,networkid,xpanid) [icT(ESSC)T(iCUD.).] + NcpBase::SendPropteryUpdate( + SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + SPINEL_CMD_PROP_VALUE_INSERTED, + SPINEL_PROP_MAC_SCAN_BEACON, + "CcT(ESSC)T(iCUD.).", + result->mChannel,//chan + result->mRssi,//rssi + result->mExtAddress.m8,// laddr + 0xFFFF, // saddr, Not given + result->mPanId,//panid + 0xFF,//lqi, not given + SPINEL_PROTOCOL_TYPE_THREAD,//proto + flags, + result->mNetworkName,//networkid + result->mExtPanId, sizeof(result->mExtPanId) //xpanid + ); + } + else + { + SendPropteryUpdate( + SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + SPINEL_CMD_PROP_VALUE_IS, + SPINEL_PROP_MAC_SCAN_STATE, + SPINEL_DATATYPE_UINT8_S, + SPINEL_SCAN_STATE_IDLE + ); + } + +exit: + return; +} + + +void NcpBase::HandleUnicastAddressesChanged(void *context) +{ + NcpBase *obj = reinterpret_cast(context); + obj->mUpdateAddressesTask.Post(); +} + +void NcpBase::RunUpdateAddressesTask(void *context) +{ + NcpBase *obj = reinterpret_cast(context); + obj->RunUpdateAddressesTask(); +} + +void NcpBase::RunUpdateAddressesTask() +{ + VerifyOrExit(mSending == false, ;); + + // It would really be preferable to have inserted/removed notifications + // for the individual addresses, rather than a single "changed" event. + HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_IPV6_ADDRESS_TABLE); + + HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_NET_STATE); + +exit: + return; +} + +// ============================================================ +// Serial channel message callbacks +// ============================================================ + +void NcpBase::HandleReceive(void *context, const uint8_t *buf, uint16_t bufLength) +{ + NcpBase *obj = reinterpret_cast(context); + obj->HandleReceive(buf, bufLength); +} + +void NcpBase::HandleReceive(const uint8_t *buf, uint16_t bufLength) +{ + uint8_t header = 0; + unsigned int command = 0; + spinel_ssize_t parsedLength; + const uint8_t *arg_ptr = NULL; + unsigned int arg_len = 0; + + parsedLength = spinel_datatype_unpack(buf, bufLength, "CiD", &header, &command, &arg_ptr, &arg_len); + + if (parsedLength == bufLength) + { + HandleCommand(header, command, arg_ptr, static_cast(arg_len)); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } +} + +void NcpBase::HandleCommand(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len) +{ + unsigned int propKey = 0; + spinel_ssize_t parsedLength; + const uint8_t *value_ptr; + unsigned int value_len; + + if ((SPINEL_HEADER_FLAG & header) != SPINEL_HEADER_FLAG) + { + // Skip + return; + } + + // We only support IID zero for now. + if (SPINEL_HEADER_GET_IID(header) != 0) + { + SendLastStatus(header, SPINEL_STATUS_INVALID_INTERFACE); + return; + } + + switch (command) + { + case SPINEL_CMD_NOOP: + SendLastStatus(header, SPINEL_STATUS_OK); + break; + + case SPINEL_CMD_RESET: + // TODO: Figure out how to actually perform a reset. + SendLastStatus(0, SPINEL_STATUS_RESET_SOFTWARE); + break; + + case SPINEL_CMD_PROP_VALUE_GET: + parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "i", &propKey); + + if (parsedLength > 0) + { + HandleCommandPropertyGet(header, static_cast(propKey)); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_CMD_PROP_VALUE_SET: + parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); + + if (parsedLength == arg_len) + { + HandleCommandPropertySet(header, static_cast(propKey), value_ptr, value_len); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_CMD_PROP_VALUE_INSERT: + parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); + + if (parsedLength == arg_len) + { + HandleCommandPropertyInsert(header, static_cast(propKey), value_ptr, value_len); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_CMD_PROP_VALUE_REMOVE: + parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); + + if (parsedLength == arg_len) + { + HandleCommandPropertyRemove(header, static_cast(propKey), value_ptr, value_len); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + default: + SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND); + break; + } +} + +void NcpBase::HandleCommandPropertyGet(uint8_t header, spinel_prop_key_t key) +{ + const uint8_t *ptr; + uint8_t len, tmp; + + if (mSending) + { + // If we are currently sending, can can queue up to one + // property get that we can execute immediately after + // the send is complete. + if (mQueuedGetHeader == 0) + { + mQueuedGetHeader = header; + mQueuedGetKey = key; + } + + return; + } + + switch (key) + { + case SPINEL_PROP_LOCK: + case SPINEL_PROP_MAC_SCAN_MASK: + case SPINEL_PROP_PHY_TX_POWER: + SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); + break; + + case SPINEL_PROP_LAST_STATUS: + SendPropteryUpdate(header, SPINEL_CMD_PROP_VALUE_IS, key, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus); + break; + + case SPINEL_PROP_PROTOCOL_VERSION: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S, + SPINEL_PROTOCOL_TYPE_THREAD, + SPINEL_PROTOCOL_VERSION_THREAD_MAJOR, + SPINEL_PROTOCOL_VERSION_THREAD_MINOR + ); + break; + + case SPINEL_PROP_CAPABILITIES: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT_PACKED_S + SPINEL_DATATYPE_UINT_PACKED_S + SPINEL_DATATYPE_UINT_PACKED_S, + SPINEL_CAP_ROLE_ROUTER, + SPINEL_CAP_NET_THREAD_1_0, + SPINEL_CAP_802_15_4_2450MHZ_OQPSK + ); + break; + + case SPINEL_PROP_NCP_VERSION: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UTF8_S, + PACKAGE_NAME "/" PACKAGE_VERSION "; " __DATE__ " " __TIME__ + ); + break; + + case SPINEL_PROP_INTERFACE_COUNT: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + 1 // Only one interface for now + ); + break; + + case SPINEL_PROP_POWER_STATE: + // Always online at the moment + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + SPINEL_POWER_STATE_ONLINE + ); + break; + + case SPINEL_PROP_NET_NETWORK_NAME: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UTF8_S, + otGetNetworkName() + ); + break; + + case SPINEL_PROP_MAC_15_4_PANID: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT16_S, + otGetPanId() + ); + break; + + case SPINEL_PROP_PHY_FREQ: + { + uint32_t freq_khz; + uint8_t chan = otGetChannel(); + + if (chan == 0) + { + freq_khz = 868300; + } + else if (chan < 11) + { + freq_khz = 906000 - (2000 * 1) + 2000 * (chan); + } + else if (chan < 26) + { + freq_khz = 2405 - (5000 * 11) + 5000 * (chan); + } + else + { + SendLastStatus(header, SPINEL_STATUS_FAILURE); + return; + } + + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT32_S, + &freq_khz + ); + break; + } + + case SPINEL_PROP_PHY_CHAN_SUPPORTED: + { + static const uint8_t supported_channels[] = + { + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26 + }; + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_DATA_S, + supported_channels, + sizeof(supported_channels) + ); + break; + } + + case SPINEL_PROP_PHY_CHAN: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + otGetChannel() + ); + break; + + case SPINEL_PROP_HWADDR: + case SPINEL_PROP_MAC_15_4_LADDR: + // TODO: Figure out how these cases should be differentiated. + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_EUI64_S, + otGetExtendedAddress() + ); + break; + + case SPINEL_PROP_MAC_15_4_SADDR: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT16_S, + sThreadNetif->GetMac().GetShortAddress() + ); + break; + + case SPINEL_PROP_NET_XPANID: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_DATA_S, + otGetExtendedPanId(), + sizeof(spinel_net_xpanid_t) + ); + break; + + case SPINEL_PROP_NET_MASTER_KEY: + ptr = otGetMasterKey(&len); + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_DATA_S, + ptr, + len + ); + break; + + case SPINEL_PROP_NET_KEY_SEQUENCE: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT32_S, + otGetKeySequenceCounter() + ); + break; + + case SPINEL_PROP_PHY_RSSI: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_INT8_S, + otPlatRadioGetNoiseFloor() + ); + break; + + case SPINEL_PROP_NET_PARTITION_ID: + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT32_S, + otGetPartitionId() + ); + break; + + case SPINEL_PROP_NET_ENABLED: + tmp = (otGetDeviceRole() != kDeviceRoleDisabled); + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + tmp + ); + break; + + case SPINEL_PROP_NET_STATE: + switch (otGetDeviceRole()) + { + case kDeviceRoleDisabled: + tmp = SPINEL_NET_STATE_OFFLINE; + break; + + case kDeviceRoleDetached: + tmp = SPINEL_NET_STATE_DETACHED; + break; + + case kDeviceRoleChild: + case kDeviceRoleRouter: + case kDeviceRoleLeader: + tmp = SPINEL_NET_STATE_ATTACHED; + break; + } + + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + tmp + ); + break; + + case SPINEL_PROP_NET_ROLE: + switch (otGetDeviceRole()) + { + case kDeviceRoleDisabled: + case kDeviceRoleDetached: + tmp = SPINEL_NET_ROLE_NONE; + break; + + case kDeviceRoleChild: + tmp = SPINEL_NET_ROLE_CHILD; + break; + + case kDeviceRoleRouter: + tmp = SPINEL_NET_ROLE_ROUTER; + break; + + case kDeviceRoleLeader: + tmp = SPINEL_NET_ROLE_LEADER; + break; + } + + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + tmp + ); + break; + + case SPINEL_PROP_THREAD_LEADER: + { + ThreadError errorCode; + Ip6::Address address; + errorCode = sThreadNetif->GetMle().GetLeaderAddress(address); + + if (errorCode) + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + else + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_IPv6ADDR_S, + &address + ); + } + + break; + } + + case SPINEL_PROP_IPV6_ML_PREFIX: + { + const uint8_t *ml_prefix = sThreadNetif->GetMle().GetMeshLocalPrefix(); + + if (ml_prefix) + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S, + ml_prefix, + 64 + ); + } + else + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_VOID_S + ); + } + + break; + } + + case SPINEL_PROP_IPV6_ADDRESS_TABLE: + HandleCommandPropertyGetAddressList(header); + break; + + case SPINEL_PROP_IPV6_ROUTE_TABLE: + HandleCommandPropertyGetRoutingTable(header); + break; + + case SPINEL_PROP_MAC_SCAN_STATE: + if (otActiveScanInProgress()) + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + SPINEL_SCAN_STATE_BEACON + ); + } + else + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + SPINEL_SCAN_STATE_IDLE + ); + } + + break; + + case SPINEL_PROP_STREAM_NET: + case SPINEL_PROP_STREAM_NET_INSECURE: + case SPINEL_PROP_STREAM_DEBUG: + case SPINEL_PROP_STREAM_RAW: + case SPINEL_PROP_MAC_SCAN_BEACON: + // These properties don't have a "Getter" + SendLastStatus(header, SPINEL_STATUS_FAILURE); + break; + + default: + SendLastStatus(header, SPINEL_STATUS_PROPERTY_NOT_FOUND); + break; + } +} + +void NcpBase::HandleCommandPropertySet(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, + uint16_t value_len) +{ + const uint8_t *ptr = NULL; + unsigned int i = 0; + uint16_t tmp; + const char *string = NULL; + spinel_ssize_t parsedLength; + spinel_size_t len; + ThreadError errorCode = kThreadError_None; + + switch (key) + { + case SPINEL_PROP_LAST_STATUS: + case SPINEL_PROP_PROTOCOL_VERSION: + case SPINEL_PROP_CAPABILITIES: + case SPINEL_PROP_NCP_VERSION: + case SPINEL_PROP_STREAM_DEBUG: + case SPINEL_PROP_MAC_SCAN_BEACON: + case SPINEL_PROP_NET_PARTITION_ID: + case SPINEL_PROP_PHY_FREQ: + case SPINEL_PROP_IPV6_ADDRESS_TABLE: + case SPINEL_PROP_IPV6_ROUTE_TABLE: + case SPINEL_PROP_PHY_RSSI: + case SPINEL_PROP_INTERFACE_COUNT: + // These properties don't have a "Setter" + SendLastStatus(header, SPINEL_STATUS_FAILURE); + break; + + case SPINEL_PROP_MAC_SCAN_MASK: + case SPINEL_PROP_POWER_STATE: + case SPINEL_PROP_PHY_TX_POWER: + SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); + break; + + case SPINEL_PROP_IPV6_ML_PREFIX: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_IPv6ADDR_S, + &ptr + ); + + if (parsedLength > 0) + { + errorCode = sThreadNetif->GetMle().SetMeshLocalPrefix(ptr); + HandleCommandPropertyGet(header, key); + } + else + { + errorCode = kThreadError_Parse; + } + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + + break; + + case SPINEL_PROP_NET_ENABLED: + { + bool value = false; + + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_BOOL_S, + &value + ); + + if (parsedLength > 0) + { + if (value == false) + { + errorCode = otDisable(); + } + else + { + errorCode = otEnable(); + } + } + else + { + errorCode = kThreadError_Parse; + } + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + + break; + } + + case SPINEL_PROP_NET_STATE: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT_PACKED_S, + &i + ); + + if (parsedLength > 0) + { + switch (i) + { + case SPINEL_NET_STATE_OFFLINE: + if (otGetDeviceRole() != kDeviceRoleDisabled) + { + errorCode = otDisable(); + } + + break; + + case SPINEL_NET_STATE_DETACHED: + if (otGetDeviceRole() == kDeviceRoleDisabled) + { + errorCode = otEnable(); + + if (errorCode == kThreadError_None) + { + errorCode = otBecomeDetached(); + } + } + else if (otGetDeviceRole() != kDeviceRoleDetached) + { + errorCode = otBecomeDetached(); + } + + break; + + case SPINEL_NET_STATE_ATTACHING: + case SPINEL_NET_STATE_ATTACHED: + if (otGetDeviceRole() == kDeviceRoleDisabled) + { + errorCode = otEnable(); + } + + if (otGetDeviceRole() == kDeviceRoleDetached) + { + errorCode = otBecomeRouter(); + + if (errorCode == kThreadError_None) + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_IS, + key, + SPINEL_DATATYPE_UINT8_S, + SPINEL_NET_STATE_ATTACHING + ); + return; + } + } + + break; + } + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_NET_ROLE: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT_PACKED_S, + &i + ); + + if (parsedLength > 0) + { + switch (i) + { + case SPINEL_NET_ROLE_NONE: + errorCode = kThreadError_InvalidArgs; + break; + + case SPINEL_NET_ROLE_ROUTER: + errorCode = otBecomeRouter(); + break; + + case SPINEL_NET_ROLE_LEADER: + errorCode = otBecomeLeader(); + break; + + case SPINEL_NET_ROLE_CHILD: + errorCode = otBecomeChild(kMleAttachAnyPartition); + break; + } + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_MAC_SCAN_STATE: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT_PACKED_S, + &i + ); + + if (parsedLength > 0) + { + switch (i) + { + case SPINEL_SCAN_STATE_IDLE: + errorCode = kThreadError_None; + break; + + case SPINEL_SCAN_STATE_BEACON: + gActiveScanContextHack = this; + errorCode = otActiveScan((mChannelMask >> kPhyMinChannel), 200, &HandleActiveScanResult_Jump); + break; + + case SPINEL_SCAN_STATE_ENERGY: + errorCode = kThreadError_NotImplemented; + break; + + default: + errorCode = kThreadError_InvalidArgs; + break; + } + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_STREAM_NET_INSECURE: + SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); + break; + + case SPINEL_PROP_STREAM_NET: + { + const uint8_t *frame_ptr(NULL); + unsigned int frame_len(0); + const uint8_t *meta_ptr(NULL); + unsigned int meta_len(0); + Message *message(Ip6::Ip6::NewMessage(0)); + + if (message == NULL) + { + errorCode = kThreadError_NoBufs; + } + else + { + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_DATA_S SPINEL_DATATYPE_DATA_S, + &frame_ptr, + &frame_len, + &meta_ptr, + &meta_len + ); + + // We ignore metadata for now. + (void)meta_ptr; + (void)meta_len; + + errorCode = message->Append(frame_ptr, frame_len); + } + + if (errorCode == kThreadError_None) + { + errorCode = Ip6::Ip6::HandleDatagram(*message, NULL, sThreadNetif->GetInterfaceId(), NULL, true); + } + + if (errorCode == kThreadError_None) + { + if (SPINEL_HEADER_GET_TID(header) != 0) + { + // Only send a successful status update if + // there was a transaction id in the header. + SendLastStatus(header, SPINEL_STATUS_OK); + } + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + + break; + } + + + case SPINEL_PROP_NET_NETWORK_NAME: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UTF8_S, + &string + ); + + if ((parsedLength > 0) && (string != NULL)) + { + errorCode = otSetNetworkName(string); + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_PHY_CHAN: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT_PACKED_S, + &i + ); + + if (parsedLength > 0) + { + errorCode = otSetChannel(static_cast(i)); + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + + case SPINEL_PROP_MAC_15_4_PANID: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT16_S, + &tmp + ); + + if (parsedLength > 0) + { + errorCode = otSetPanId(tmp); + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + + case SPINEL_PROP_NET_XPANID: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_DATA_S, + &ptr, + &len + ); + + if ((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t))) + { + otSetExtendedPanId(ptr); + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_NET_MASTER_KEY: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_DATA_S, + &ptr, + &len + ); + + if ((parsedLength > 0) && (len < 100)) + { + errorCode = otSetMasterKey(ptr, static_cast(len)); + + if (errorCode == kThreadError_None) + { + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + case SPINEL_PROP_NET_KEY_SEQUENCE: + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + SPINEL_DATATYPE_UINT32_S, + &i + ); + + if (parsedLength > 0) + { + otSetKeySequenceCounter(i); + HandleCommandPropertyGet(header, key); + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + + default: + SendLastStatus(header, SPINEL_STATUS_PROPERTY_NOT_FOUND); + break; + } + +} + +void NcpBase::HandleCommandPropertyGetAddressList(uint8_t header) +{ + ThreadError errorCode; + + errorCode = OutboundFrameBegin(); + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_IPV6_ADDRESS_TABLE); + } + + for (const otNetifAddress *address = otGetUnicastAddresses(); address; address = address->mNext) + { + if (errorCode != kThreadError_None) + { + break; + } + + errorCode = OutboundFrameFeedPacked( + "T(6CLL).", + &address->mAddress, + address->mPrefixLength, + address->mPreferredLifetime, + address->mValidLifetime + ); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameSend(); + } + + if (errorCode != kThreadError_None) + { + SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR); + } +} + +void NcpBase::HandleCommandPropertyGetRoutingTable(uint8_t header) +{ + SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); +} + +void NcpBase::HandleCommandPropertyInsert(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, + uint16_t value_len) +{ + spinel_ssize_t parsedLength; + ThreadError errorCode = kThreadError_None; + + switch (key) + { + case SPINEL_PROP_IPV6_ADDRESS_TABLE: + { + otNetifAddress netif_addr = {}; + otIp6Address *addr_ptr; + + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + "6CLL", + &addr_ptr, + &netif_addr.mPrefixLength, + &netif_addr.mPreferredLifetime, + &netif_addr.mValidLifetime + ); + + if (parsedLength > 0) + { + netif_addr.mAddress = *addr_ptr; + errorCode = otAddUnicastAddress(&netif_addr); + + if (errorCode == kThreadError_None) + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_INSERTED, + key, + value_ptr, + value_len + ); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + } + + default: + SendLastStatus(header, SPINEL_STATUS_FAILURE); + break; + } +} + +void NcpBase::HandleCommandPropertyRemove(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, + uint16_t value_len) +{ + spinel_ssize_t parsedLength; + ThreadError errorCode = kThreadError_None; + + switch (key) + { + case SPINEL_PROP_IPV6_ADDRESS_TABLE: + { + otNetifAddress netif_addr = {}; + otIp6Address *addr_ptr; + + parsedLength = spinel_datatype_unpack( + value_ptr, + value_len, + "6CLL", + &addr_ptr, + &netif_addr.mPrefixLength, + &netif_addr.mPreferredLifetime, + &netif_addr.mValidLifetime + ); + + if (parsedLength > 0) + { + netif_addr.mAddress = *addr_ptr; + errorCode = otRemoveUnicastAddress(&netif_addr); + + if (errorCode == kThreadError_None) + { + SendPropteryUpdate( + header, + SPINEL_CMD_PROP_VALUE_REMOVED, + key, + value_ptr, + value_len + ); + } + else + { + SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); + } + } + else + { + SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); + } + + break; + } + + default: + SendLastStatus(header, SPINEL_STATUS_FAILURE); + break; + } +} + +void NcpBase::SendLastStatus(uint8_t header, spinel_status_t lastStatus) +{ + if (SPINEL_HEADER_GET_IID(header) == 0) + { + mLastStatus = lastStatus; + } + + SendPropteryUpdate(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_LAST_STATUS, "i", lastStatus); +} + +void NcpBase::SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, const char *pack_format, ...) +{ + ThreadError errorCode; + va_list args; + + errorCode = OutboundFrameBegin(); + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedPacked("Cii", header, command, key); + } + + if (errorCode == kThreadError_None) + { + va_start(args, pack_format); + errorCode = OutboundFrameFeedVPacked(pack_format, args); + va_end(args); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameSend(); + } +} + +void NcpBase::SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, const uint8_t *value_ptr, + uint16_t value_len) +{ + ThreadError errorCode; + + errorCode = OutboundFrameBegin(); + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedPacked("Cii", header, command, key); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedData(value_ptr, value_len); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameSend(); + } +} + +void NcpBase::SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, Message &message) +{ + ThreadError errorCode; + + errorCode = OutboundFrameBegin(); + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedPacked("Cii", header, command, key); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameFeedMessage(message); + } + + if (errorCode == kThreadError_None) + { + errorCode = OutboundFrameSend(); + } +} + +ThreadError +NcpBase::OutboundFrameFeedVPacked(const char *pack_format, va_list args) +{ + uint8_t buf[64]; + ThreadError errorCode = kThreadError_NoBufs; + spinel_ssize_t packed_len; + + packed_len = spinel_datatype_vpack(buf, sizeof(buf), pack_format, args); + + if ((packed_len > 0) && (packed_len <= sizeof(buf))) + { + errorCode = OutboundFrameFeedData(buf, packed_len); + } + + return errorCode; +} + +ThreadError +NcpBase::OutboundFrameFeedPacked(const char *pack_format, ...) +{ + ThreadError errorCode; + va_list args; + + va_start(args, pack_format); + errorCode = OutboundFrameFeedVPacked(pack_format, args); + va_end(args); + + return errorCode; +} + +void NcpBase::HandleSendDone(void *context) +{ + NcpBase *obj = reinterpret_cast(context); + obj->HandleSendDone(); +} + +void NcpBase::HandleSendDone() +{ + if (mSendQueue.GetHead() != NULL) + { + Message &message(*mSendQueue.GetHead()); + HandleReceivedDatagram(message); + mSendQueue.Dequeue(message); + } + + if (mQueuedGetHeader != 0) + { + HandleCommandPropertyGet(mQueuedGetHeader, mQueuedGetKey); + mQueuedGetHeader = 0; + } +} + +} // namespace Thread diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp new file mode 100644 index 000000000..01e31b1a3 --- /dev/null +++ b/src/ncp/ncp_base.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file contains definitions a spinel interface to the OpenThread stack. + */ + +#ifndef NCP_BASE_HPP_ +#define NCP_BASE_HPP_ + +#include +#include +#include + +#include "spinel.h" + +namespace Thread { + +class NcpBase +{ +public: + NcpBase(); + + virtual ThreadError Start(); + virtual ThreadError Stop(); + + virtual ThreadError OutboundFrameBegin(void) = 0; + virtual uint16_t OutboundFrameGetRemaining(void) = 0; + virtual ThreadError OutboundFrameFeedData(const uint8_t *frame, uint16_t frameLength) = 0; + virtual ThreadError OutboundFrameFeedMessage(Message &message) = 0; + virtual ThreadError OutboundFrameFeedPacked(const char *pack_format, ...); + virtual ThreadError OutboundFrameFeedVPacked(const char *pack_format, va_list args); + virtual ThreadError OutboundFrameSend(void) = 0; + + static void HandleReceivedDatagram(void *context, Message &message); + void HandleReceivedDatagram(Message &message); + +protected: + static void HandleReceive(void *context, const uint8_t *buf, uint16_t bufLength); + void HandleReceive(const uint8_t *buf, uint16_t bufLength); + + static void HandleSendDone(void *context); + void HandleSendDone(); + + bool mSending; + +private: + void HandleCommand(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len); + void HandleCommandReset(uint8_t header); + void HandleCommandNoop(uint8_t header); + void HandleCommandPropertyGet(uint8_t header, spinel_prop_key_t key); + void HandleCommandPropertySet(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len); + void HandleCommandPropertyInsert(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len); + void HandleCommandPropertyRemove(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len); + + void HandleCommandPropertyGetAddressList(uint8_t header); + void HandleCommandPropertyGetRoutingTable(uint8_t header); + + void SendLastStatus(uint8_t header, spinel_status_t lastStatus); + + void SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, const uint8_t *value_ptr, + uint16_t value_len); + void SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, Message &message); + void SendPropteryUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, const char *format, ...); + + + static void HandleActiveScanResult_Jump(otActiveScanResult *result); + void HandleActiveScanResult(otActiveScanResult *result); + static void HandleUnicastAddressesChanged(void *context); + + static void RunUpdateAddressesTask(void *context); + + Ip6::NetifHandler mNetifHandler; + + spinel_status_t mLastStatus; + uint32_t mChannelMask; + + uint8_t mQueuedGetHeader; + spinel_prop_key_t mQueuedGetKey; + + void RunUpdateAddressesTask(); + Tasklet mUpdateAddressesTask; + + void RunSendScanResultsTask(); + +protected: + MessageQueue mSendQueue; +}; + +} // namespace Thread + +#endif // NCP_BASE_HPP_ diff --git a/src/ncp/spinel.c b/src/ncp/spinel.c new file mode 100644 index 000000000..c349966cb --- /dev/null +++ b/src/ncp/spinel.c @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ------------------------------------------------------------------- + * + * ## Unit Test ## + * + * This file includes its own unit test. To compile the unit test, + * simply compile this file with the macro SPINEL_SELF_TEST set to 1. + * For example: + * + * cc spinel.c -Wall -DSPINEL_SELF_TEST=1 -o spinel + * + * ------------------------------------------------------------------- + */ + +// ---------------------------------------------------------------------------- +// MARK: - +// MARK: Headers + +#include "spinel.h" + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// MARK: - + +#ifndef assert_printf +#define assert_printf(fmt, ...) \ +fprintf(stderr, \ + __FILE__ ":%d: " fmt "\n", \ + __LINE__, \ + __VA_ARGS__) +#endif + +#ifndef require_action +#define require_action(c, l, a) \ + do { if (!(c)) { \ + assert_printf("Requirement Failed (%s)", # c); \ + a; \ + goto l; \ + } } while (0) +#endif + +#ifndef require +#define require(c, l) require_action(c, l, {}) +#endif + +// ---------------------------------------------------------------------------- +// MARK: - + +spinel_ssize_t +spinel_packed_uint_decode(const uint8_t *bytes, spinel_size_t len, unsigned int *value_ptr) +{ + spinel_ssize_t ret = 0; + unsigned int value = 0; + + int i = 0; + + do + { + if (len < sizeof(uint8_t)) + { + ret = -1; + break; + } + + value |= ((bytes[0] & 0x7F) << i); + i += 7; + ret += sizeof(uint8_t); + bytes += sizeof(uint8_t); + len -= sizeof(uint8_t); + } + while ((bytes[-1] & 0x80) == 0x80); + + if ((ret > 0) && (value_ptr != NULL)) + { + *value_ptr = value; + } + + return ret; +} + +spinel_ssize_t +spinel_packed_uint_size(unsigned int value) +{ + spinel_size_t ret; + + if (value < (1 << 7)) + { + ret = 1; + } + else if (value < (1 << 14)) + { + ret = 2; + } + else if (value < (1 << 21)) + { + ret = 3; + } + else if (value < (1 << 28)) + { + ret = 4; + } + else + { + ret = 5; + } + + return ret; +} + +spinel_ssize_t +spinel_packed_uint_encode(uint8_t *bytes, spinel_size_t len, unsigned int value) +{ + const spinel_ssize_t encoded_size = spinel_packed_uint_size(value); + + if (len >= encoded_size) + { + spinel_size_t i; + + for (i = 0; i != encoded_size - 1; ++i) + { + *bytes++ = (value & 0x7F) | 0x80; + value = (value >> 7); + } + + *bytes++ = (value & 0x7F); + } + + return encoded_size; +} + +const char * +spinel_next_packed_datatype(const char *pack_format) +{ + int depth = 0; + + do + { + switch (*++pack_format) + { + case '(': + depth++; + break; + + case ')': + depth--; + + if (depth == 0) + { + pack_format++; + } + + break; + } + } + while ((depth > 0) && *pack_format != 0); + + return pack_format; +} + +spinel_ssize_t +spinel_datatype_unpack(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, ...) +{ + spinel_ssize_t ret; + va_list args; + va_start(args, pack_format); + + ret = spinel_datatype_vunpack(data_ptr, data_len, pack_format, args); + + va_end(args); + return ret; +} + +spinel_ssize_t +spinel_datatype_vunpack(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, va_list args) +{ + spinel_ssize_t ret = 0; + + for (; *pack_format != 0; pack_format = spinel_next_packed_datatype(pack_format)) + { + if (*pack_format == ')') + { + // Don't go past the end of a struct. + break; + } + + switch ((spinel_datatype_t)pack_format[0]) + { + case SPINEL_DATATYPE_BOOL_C: + { + bool *arg_ptr = va_arg(args, bool *); + require_action(data_len >= sizeof(uint8_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = data_ptr[0]; + } + + ret += sizeof(uint8_t); + data_ptr += sizeof(uint8_t); + data_len -= sizeof(uint8_t); + break; + } + + case SPINEL_DATATYPE_INT8_C: + case SPINEL_DATATYPE_UINT8_C: + { + uint8_t *arg_ptr = va_arg(args, uint8_t *); + require_action(data_len >= sizeof(uint8_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = data_ptr[0]; + } + + ret += sizeof(uint8_t); + data_ptr += sizeof(uint8_t); + data_len -= sizeof(uint8_t); + break; + } + + case SPINEL_DATATYPE_INT16_C: + case SPINEL_DATATYPE_UINT16_C: + { + uint16_t *arg_ptr = va_arg(args, uint16_t *); + require_action(data_len >= sizeof(uint16_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = ((data_ptr[1] << 8) | data_ptr[0]); + } + + ret += sizeof(uint16_t); + data_ptr += sizeof(uint16_t); + data_len -= sizeof(uint16_t); + break; + } + + case SPINEL_DATATYPE_INT32_C: + case SPINEL_DATATYPE_UINT32_C: + { + uint32_t *arg_ptr = va_arg(args, uint32_t *); + require_action(data_len >= sizeof(uint32_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = ((data_ptr[3] << 24) | (data_ptr[2] << 16) | (data_ptr[1] << 8) | data_ptr[0]); + } + + ret += sizeof(uint32_t); + data_ptr += sizeof(uint32_t); + data_len -= sizeof(uint32_t); + break; + } + + case SPINEL_DATATYPE_IPv6ADDR_C: + { + spinel_ipv6addr_t **arg_ptr = va_arg(args, spinel_ipv6addr_t **); + require_action(data_len >= sizeof(spinel_ipv6addr_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = (spinel_ipv6addr_t *)data_ptr; + } + + ret += sizeof(spinel_ipv6addr_t); + data_ptr += sizeof(spinel_ipv6addr_t); + data_len -= sizeof(spinel_ipv6addr_t); + break; + } + + case SPINEL_DATATYPE_EUI64_C: + { + spinel_eui64_t **arg_ptr = va_arg(args, spinel_eui64_t **); + require_action(data_len >= sizeof(spinel_eui64_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = (spinel_eui64_t *)data_ptr; + } + + ret += sizeof(spinel_eui64_t); + data_ptr += sizeof(spinel_eui64_t); + data_len -= sizeof(spinel_eui64_t); + break; + } + + case SPINEL_DATATYPE_EUI48_C: + { + spinel_eui48_t **arg_ptr = va_arg(args, spinel_eui48_t **); + require_action(data_len >= sizeof(spinel_eui48_t), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = (spinel_eui48_t *)data_ptr; + } + + ret += sizeof(spinel_eui48_t); + data_ptr += sizeof(spinel_eui48_t); + data_len -= sizeof(spinel_eui48_t); + break; + } + + case SPINEL_DATATYPE_UINT_PACKED_C: + { + uint32_t *arg_ptr = va_arg(args, uint32_t *); + spinel_ssize_t pui_len = spinel_packed_uint_decode(data_ptr, data_len, arg_ptr); + + require(pui_len > 0, bail); + + require(pui_len <= data_len, bail); + + ret += pui_len; + data_ptr += pui_len; + data_len -= pui_len; + break; + } + + case SPINEL_DATATYPE_UTF8_C: + { + const char **arg_ptr = va_arg(args, const char **); + ssize_t len = strnlen((const char *)data_ptr, data_len) + 1; + require_action((len <= data_len) || (data_ptr[data_len - 1] != 0), bail, (ret = -1, errno = EOVERFLOW)); + + if (arg_ptr) + { + *arg_ptr = (const char *)data_ptr; + } + + ret += len; + data_ptr += len; + data_len -= len; + break; + } + + case SPINEL_DATATYPE_DATA_C: + { + spinel_ssize_t pui_len = 0; + uint16_t block_len = 0; + const uint8_t *block_ptr = data_ptr; + const uint8_t **block_ptr_ptr = va_arg(args, const uint8_t **); + unsigned int *block_len_ptr = va_arg(args, unsigned int *); + char nextformat = *spinel_next_packed_datatype(pack_format); + + if ((nextformat != 0) && (nextformat != ')')) + { + pui_len = spinel_datatype_unpack(data_ptr, data_len, SPINEL_DATATYPE_UINT16_S, &block_len); + //pui_len = spinel_packed_uint_decode(data_ptr, data_len, &block_len); + block_ptr += pui_len; + + require(pui_len > 0, bail); + require(block_len < SPINEL_FRAME_MAX_SIZE, bail); + } + else + { + block_len = data_len; + pui_len = 0; + } + + require_action(data_len >= (block_len + pui_len), bail, (ret = -1, errno = EOVERFLOW)); + + if (NULL != block_ptr_ptr) + { + *block_ptr_ptr = block_ptr; + } + + if (NULL != block_len_ptr) + { + *block_len_ptr = block_len; + } + + block_len += pui_len; + ret += block_len; + data_ptr += block_len; + data_len -= block_len; + break; + } + + case SPINEL_DATATYPE_STRUCT_C: + { + spinel_ssize_t pui_len = 0; + uint16_t block_len = 0; + unsigned int actual_len = 0; + const uint8_t *block_ptr = data_ptr; + char nextformat = *spinel_next_packed_datatype(pack_format); + + if ((nextformat != 0) && (nextformat != ')')) + { + pui_len = spinel_datatype_unpack(data_ptr, data_len, SPINEL_DATATYPE_UINT16_S, &block_len); + block_ptr += pui_len; + + require(pui_len > 0, bail); + require(block_len < SPINEL_FRAME_MAX_SIZE, bail); + } + else + { + block_len = data_len; + pui_len = 0; + } + + require_action(data_len >= (block_len + pui_len), bail, (ret = -1, errno = EOVERFLOW)); + + actual_len = spinel_datatype_vunpack(block_ptr, block_len, pack_format + 2, args); + + require_action((int)actual_len > -1, bail, (ret = -1, errno = EOVERFLOW)); + + if (pui_len) + { + block_len += pui_len; + } + else + { + block_len = actual_len; + } + + ret += block_len; + data_ptr += block_len; + data_len -= block_len; + break; + } + + case '.': + // Skip. + break; + + case SPINEL_DATATYPE_ARRAY_C: + default: + // Unsupported Type! + ret = -1; + errno = EINVAL; + goto bail; + } + } + + return ret; + +bail: + return ret; +} + +spinel_ssize_t +spinel_datatype_pack(uint8_t *data_ptr, spinel_size_t data_len_max, const char *pack_format, ...) +{ + int ret; + va_list args; + va_start(args, pack_format); + + ret = spinel_datatype_vpack(data_ptr, data_len_max, pack_format, args); + + va_end(args); + return ret; +} + +spinel_ssize_t +spinel_datatype_vpack(uint8_t *data_ptr, spinel_size_t data_len_max, const char *pack_format, va_list args) +{ + spinel_ssize_t ret = 0; + + for (; *pack_format != 0; pack_format = spinel_next_packed_datatype(pack_format)) + { + if (*pack_format == ')') + { + // Don't go past the end of a struct. + break; + } + + switch ((spinel_datatype_t)*pack_format) + { + case SPINEL_DATATYPE_BOOL_C: + { + bool arg = va_arg(args, int); + ret += sizeof(uint8_t); + + if (data_len_max >= sizeof(uint8_t)) + { + data_ptr[0] = (arg != false); + data_ptr += sizeof(uint8_t); + data_len_max -= sizeof(uint8_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_INT8_C: + case SPINEL_DATATYPE_UINT8_C: + { + uint8_t arg = va_arg(args, int); + ret += sizeof(uint8_t); + + if (data_len_max >= sizeof(uint8_t)) + { + data_ptr[0] = arg; + data_ptr += sizeof(uint8_t); + data_len_max -= sizeof(uint8_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_INT16_C: + case SPINEL_DATATYPE_UINT16_C: + { + uint16_t arg = va_arg(args, int); + ret += sizeof(uint16_t); + + if (data_len_max >= sizeof(uint16_t)) + { + data_ptr[1] = (arg >> 8); + data_ptr[0] = (arg >> 0); + data_ptr += sizeof(uint16_t); + data_len_max -= sizeof(uint16_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_INT32_C: + case SPINEL_DATATYPE_UINT32_C: + { + uint32_t arg = va_arg(args, int); + ret += sizeof(uint32_t); + + if (data_len_max >= sizeof(uint32_t)) + { + data_ptr[3] = (arg >> 24); + data_ptr[2] = (arg >> 16); + data_ptr[1] = (arg >> 8); + data_ptr[0] = (arg >> 0); + data_ptr += sizeof(uint32_t); + data_len_max -= sizeof(uint32_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_IPv6ADDR_C: + { + spinel_ipv6addr_t *arg = va_arg(args, spinel_ipv6addr_t *); + ret += sizeof(spinel_ipv6addr_t); + + if (data_len_max >= sizeof(spinel_ipv6addr_t)) + { + *(spinel_ipv6addr_t *)data_ptr = *arg; + data_ptr += sizeof(spinel_ipv6addr_t); + data_len_max -= sizeof(spinel_ipv6addr_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_EUI48_C: + { + spinel_eui48_t *arg = va_arg(args, spinel_eui48_t *); + ret += sizeof(spinel_eui48_t); + + if (data_len_max >= sizeof(spinel_eui48_t)) + { + *(spinel_eui48_t *)data_ptr = *arg; + data_ptr += sizeof(spinel_eui48_t); + data_len_max -= sizeof(spinel_eui48_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_EUI64_C: + { + spinel_eui64_t *arg = va_arg(args, spinel_eui64_t *); + ret += sizeof(spinel_eui64_t); + + if (data_len_max >= sizeof(spinel_eui64_t)) + { + *(spinel_eui64_t *)data_ptr = *arg; + data_ptr += sizeof(spinel_eui64_t); + data_len_max -= sizeof(spinel_eui64_t); + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_UINT_PACKED_C: + { + uint32_t arg = va_arg(args, uint32_t); + spinel_ssize_t encoded_size = spinel_packed_uint_encode(data_ptr, data_len_max, arg); + ret += encoded_size; + + if (data_len_max >= encoded_size) + { + data_ptr += encoded_size; + data_len_max -= encoded_size; + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_UTF8_C: + { + const char *string_arg = va_arg(args, const char *); + size_t string_arg_len = 0; + + if (string_arg) + { + string_arg_len = strlen(string_arg) + 1; + } + else + { + string_arg = ""; + string_arg_len = 1; + } + + ret += string_arg_len; + + if (data_len_max >= string_arg_len) + { + memcpy(data_ptr, string_arg, string_arg_len); + + data_ptr += string_arg_len; + data_len_max -= string_arg_len; + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_DATA_C: + { + uint8_t *arg = va_arg(args, uint8_t *); + uint32_t data_size_arg = va_arg(args, uint32_t); + spinel_ssize_t size_len = 0; + char nextformat = *spinel_next_packed_datatype(pack_format); + + if (nextformat != 0 && nextformat != ')') + { + size_len = spinel_datatype_pack(data_ptr, data_len_max, SPINEL_DATATYPE_UINT16_S, data_size_arg); + require_action(size_len > 0, bail, {ret = -1; errno = EINVAL;}); + } + + ret += size_len + data_size_arg; + + if (data_len_max >= size_len + data_size_arg) + { + data_ptr += size_len; + data_len_max -= size_len; + + memcpy(data_ptr, arg, data_size_arg); + + data_ptr += data_size_arg; + data_len_max -= data_size_arg; + } + else + { + data_len_max = 0; + } + + break; + } + + case SPINEL_DATATYPE_STRUCT_C: + { + spinel_ssize_t struct_len = 0; + spinel_ssize_t size_len = 0; + char nextformat = *spinel_next_packed_datatype(pack_format); + + require_action(pack_format[1] == '(', bail, {ret = -1; errno = EINVAL;}); + + // First we figure out the size of the struct + { + va_list subargs; + va_copy(subargs, args); + struct_len = spinel_datatype_vpack(NULL, 0, pack_format + 2, subargs); + va_end(subargs); + } + + if (nextformat != 0 && nextformat != ')') + { + size_len = spinel_datatype_pack(data_ptr, data_len_max, SPINEL_DATATYPE_UINT16_S, struct_len); + require_action(size_len > 0, bail, {ret = -1; errno = EINVAL;}); + } + + ret += size_len + struct_len; + + if (struct_len + size_len <= data_len_max) + { + data_ptr += size_len; + data_len_max -= size_len; + + struct_len = spinel_datatype_vpack(data_ptr, data_len_max, pack_format + 2, args); + + data_ptr += struct_len; + data_len_max -= struct_len; + } + else + { + data_len_max = 0; + } + + break; + } + + case '.': + // Skip. + break; + + default: + // Unsupported Type! + ret = -1; + errno = EINVAL; + goto bail; + + } + } + +bail: + return ret; +} + +// ---------------------------------------------------------------------------- +// MARK: - + +const char * +spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) +{ + const char *ret = "UNKNOWN"; + + switch (prop_key) + { + case SPINEL_PROP_LAST_STATUS: + ret = "PROP_LAST_STATUS"; + break; + + case SPINEL_PROP_PROTOCOL_VERSION: + ret = "PROP_PROTOCOL_VERSION"; + break; + + case SPINEL_PROP_CAPABILITIES: + ret = "PROP_CAPABILITIES"; + break; + + case SPINEL_PROP_NCP_VERSION: + ret = "PROP_NCP_VERSION"; + break; + + case SPINEL_PROP_INTERFACE_COUNT: + ret = "PROP_INTERFACE_COUNT"; + break; + + case SPINEL_PROP_POWER_STATE: + ret = "PROP_POWER_STATE"; + break; + + case SPINEL_PROP_HWADDR: + ret = "PROP_HWADDR"; + break; + + case SPINEL_PROP_LOCK: + ret = "PROP_LOCK"; + break; + + case SPINEL_PROP_HBO_MEM_MAX: + ret = "PROP_HBO_MEM_MAX"; + break; + + case SPINEL_PROP_HBO_BLOCK_MAX: + ret = "PROP_HBO_BLOCK_MAX"; + break; + + case SPINEL_PROP_STREAM_DEBUG: + ret = "PROP_STREAM_DEBUG"; + break; + + case SPINEL_PROP_STREAM_RAW: + ret = "PROP_STREAM_RAW"; + break; + + case SPINEL_PROP_STREAM_NET: + ret = "PROP_STREAM_NET"; + break; + + case SPINEL_PROP_STREAM_NET_INSECURE: + ret = "PROP_STREAM_NET_INSECURE"; + break; + + case SPINEL_PROP_PHY_ENABLED: + ret = "PROP_PHY_ENABLED"; + break; + + case SPINEL_PROP_PHY_CHAN: + ret = "PROP_PHY_CHAN"; + break; + + case SPINEL_PROP_PHY_CHAN_SUPPORTED: + ret = "PROP_PHY_CHAN_SUPPORTED"; + break; + + case SPINEL_PROP_PHY_FREQ: + ret = "PROP_PHY_FREQ"; + break; + + case SPINEL_PROP_PHY_CCA_THRESHOLD: + ret = "PROP_PHY_CCA_THRESHOLD"; + break; + + case SPINEL_PROP_PHY_TX_POWER: + ret = "PROP_PHY_TX_POWER"; + break; + + case SPINEL_PROP_PHY_RSSI: + ret = "PROP_PHY_RSSI"; + break; + + case SPINEL_PROP_PHY_RAW_STREAM_ENABLED: + ret = "PROP_PHY_RAW_STREAM_ENABLED"; + break; + + case SPINEL_PROP_MAC_SCAN_STATE: + ret = "PROP_MAC_SCAN_STATE"; + break; + + case SPINEL_PROP_MAC_SCAN_MASK: + ret = "PROP_MAC_SCAN_MASK"; + break; + + case SPINEL_PROP_MAC_SCAN_BEACON: + ret = "PROP_MAC_SCAN_BEACON"; + break; + + case SPINEL_PROP_MAC_15_4_LADDR: + ret = "PROP_MAC_15_4_LADDR"; + break; + + case SPINEL_PROP_MAC_15_4_SADDR: + ret = "PROP_MAC_15_4_SADDR"; + break; + + case SPINEL_PROP_MAC_15_4_PANID: + ret = "PROP_MAC_15_4_PANID"; + break; + + case SPINEL_PROP_NET_SAVED: + ret = "PROP_NET_SAVED"; + break; + + case SPINEL_PROP_NET_ENABLED: + ret = "PROP_NET_ENABLED"; + break; + + case SPINEL_PROP_NET_STATE: + ret = "PROP_NET_STATE"; + break; + + case SPINEL_PROP_NET_ROLE: + ret = "PROP_NET_ROLE"; + break; + + case SPINEL_PROP_NET_NETWORK_NAME: + ret = "PROP_NET_NETWORK_NAME"; + break; + + case SPINEL_PROP_NET_XPANID: + ret = "PROP_NET_XPANID"; + break; + + case SPINEL_PROP_NET_MASTER_KEY: + ret = "PROP_NET_MASTER_KEY"; + break; + + case SPINEL_PROP_NET_KEY_SEQUENCE: + ret = "PROP_NET_KEY_SEQUENCE"; + break; + + case SPINEL_PROP_NET_PARTITION_ID: + ret = "PROP_NET_PARTITION_ID"; + break; + + case SPINEL_PROP_THREAD_LEADER: + ret = "PROP_THREAD_LEADER"; + break; + + case SPINEL_PROP_THREAD_PARENT: + ret = "PROP_THREAD_PARENT"; + break; + + case SPINEL_PROP_THREAD_CHILD_TABLE: + ret = "PROP_THREAD_CHILD_TABLE"; + break; + + case SPINEL_PROP_IPV6_LL_ADDR: + ret = "PROP_IPV6_LL_ADDR"; + break; + + case SPINEL_PROP_IPV6_ML_ADDR: + ret = "PROP_IPV6_ML_ADDR"; + break; + + case SPINEL_PROP_IPV6_ML_PREFIX: + ret = "PROP_IPV6_ML_PREFIX"; + break; + + case SPINEL_PROP_IPV6_ADDRESS_TABLE: + ret = "PROP_IPV6_ADDRESS_TABLE"; + break; + + case SPINEL_PROP_IPV6_ROUTE_TABLE: + ret = "PROP_IPV6_ROUTE_TABLE"; + break; + + default: + break; + } + + return ret; +} + + +/* -------------------------------------------------------------------------- */ + +#if SPINEL_SELF_TEST + +#include +#include + + +int +main(void) +{ + int ret = -1; + + const char static_string[] = "static_string"; + uint8_t buffer[1024]; + ssize_t len; + + len = spinel_datatype_pack(buffer, sizeof(buffer), "CiiLU", 0x88, 9, 0xA3, 0xDEADBEEF, static_string); + + if (len != 22) + { + printf("error:%d: len != 22; (%d)\n", __LINE__, (int)len); + goto bail; + } + + { + uint8_t c = 0; + unsigned int i1 = 0; + unsigned int i2 = 0; + uint32_t l = 0; + const char *str = NULL; + + len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "CiiLU", &c, &i1, &i2, &l, &str); + + if (len != 22) + { + printf("error:%d: len != 22; (%d)\n", __LINE__, (int)len); + goto bail; + } + + if (c != 0x88) + { + printf("error: x != 0x88; (%d)\n", c); + goto bail; + } + + if (i1 != 9) + { + printf("error: i1 != 9; (%d)\n", i1); + goto bail; + } + + if (i2 != 0xA3) + { + printf("error: i2 != 0xA3; (0x%02X)\n", i2); + goto bail; + } + + if (l != 0xDEADBEEF) + { + printf("error: l != 0xDEADBEEF; (0x%08X)\n", l); + goto bail; + } + + if (strcmp(str, static_string) != 0) + { + printf("error:%d: strcmp(str,static_string) != 0\n", __LINE__); + goto bail; + } + } + + // ----------------------------------- + + memset(buffer, 0xAA, sizeof(buffer)); + + len = spinel_datatype_pack(buffer, sizeof(buffer), "CiT(iL)U", 0x88, 9, 0xA3, 0xDEADBEEF, static_string); + + if (len != 24) + { + printf("error:%d: len != 24; (%d)\n", __LINE__, (int)len); + goto bail; + } + + + + { + uint8_t c = 0; + unsigned int i1 = 0; + unsigned int i2 = 0; + uint32_t l = 0; + const char *str = NULL; + + len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "CiT(iL)U", &c, &i1, &i2, &l, &str); + + if (len != 24) + { + printf("error:%d: len != 24; (%d)\n", __LINE__, (int)len); + goto bail; + } + + if (c != 0x88) + { + printf("error: x != 0x88; (%d)\n", c); + goto bail; + } + + if (i1 != 9) + { + printf("error: i1 != 9; (%d)\n", i1); + goto bail; + } + + if (i2 != 0xA3) + { + printf("error: i2 != 0xA3; (0x%02X)\n", i2); + goto bail; + } + + if (l != 0xDEADBEEF) + { + printf("error: l != 0xDEADBEEF; (0x%08X)\n", l); + goto bail; + } + + if (strcmp(str, static_string) != 0) + { + printf("error:%d: strcmp(str,static_string) != 0\n", __LINE__); + goto bail; + } + } + + + + printf("OK\n"); + ret = 0; + return ret; + +bail: + printf("FAILURE\n"); + return ret; +} + +#endif // #if SPINEL_SELF_TEST diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h new file mode 100644 index 000000000..566bbb28a --- /dev/null +++ b/src/ncp/spinel.h @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SPINEL_HEADER_INCLUDED +#define SPINEL_HEADER_INCLUDED 1 + +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +// ---------------------------------------------------------------------------- + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +# if defined(__GNUC__) && !SPINEL_EMBEDDED +# define SPINEL_API_EXTERN extern __attribute__ ((visibility ("default"))) +# define SPINEL_API_NONNULL_ALL __attribute__((nonnull)) +# define SPINEL_API_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# endif // ifdef __GNUC__ +#endif // ifndef DOXYGEN_SHOULD_SKIP_THIS + +#ifndef SPINEL_API_EXTERN +# define SPINEL_API_EXTERN extern +#endif + +#ifndef SPINEL_API_NONNULL_ALL +# define SPINEL_API_NONNULL_ALL +#endif + +#ifndef SPINEL_API_WARN_UNUSED_RESULT +# define SPINEL_API_WARN_UNUSED_RESULT +#endif + +// ---------------------------------------------------------------------------- + +#define SPINEL_PROTOCOL_VERSION_THREAD_MAJOR 0 +#define SPINEL_PROTOCOL_VERSION_THREAD_MINOR 0 + +#define SPINEL_FRAME_MAX_SIZE 1300 + +// ---------------------------------------------------------------------------- + +typedef enum +{ + SPINEL_STATUS_OK = 0, //!< Operation has completed successfully. + SPINEL_STATUS_FAILURE = 1, //!< Operation has failed for some undefined reason. + + SPINEL_STATUS_UNIMPLEMENTED = 2, + SPINEL_STATUS_INVALID_ARGUMENT = 3, + SPINEL_STATUS_INVALID_STATE = 4, + SPINEL_STATUS_INVALID_COMMAND = 5, + SPINEL_STATUS_INVALID_INTERFACE = 6, + SPINEL_STATUS_INTERNAL_ERROR = 7, + SPINEL_STATUS_SECURITY_ERROR = 8, + SPINEL_STATUS_PARSE_ERROR = 9, + SPINEL_STATUS_IN_PROGRESS = 10, + SPINEL_STATUS_NOMEM = 11, + SPINEL_STATUS_BUSY = 12, + SPINEL_STATUS_PROPERTY_NOT_FOUND = 12, + SPINEL_STATUS_DROPPED = 14, + SPINEL_STATUS_EMPTY = 15, + + SPINEL_STATUS_RESET__BEGIN = 112, + SPINEL_STATUS_RESET_POWER_ON = SPINEL_STATUS_RESET__BEGIN + 0, + SPINEL_STATUS_RESET_EXTERNAL = SPINEL_STATUS_RESET__BEGIN + 1, + SPINEL_STATUS_RESET_SOFTWARE = SPINEL_STATUS_RESET__BEGIN + 2, + SPINEL_STATUS_RESET_FAULT = SPINEL_STATUS_RESET__BEGIN + 3, + SPINEL_STATUS_RESET_CRASH = SPINEL_STATUS_RESET__BEGIN + 4, + SPINEL_STATUS_RESET_ASSERT = SPINEL_STATUS_RESET__BEGIN + 5, + SPINEL_STATUS_RESET_OTHER = SPINEL_STATUS_RESET__BEGIN + 6, + SPINEL_STATUS_RESET__END = 128, + + + SPINEL_STATUS_VENDOR__BEGIN = 15360, + SPINEL_STATUS_VENDOR__END = 16384, + + SPINEL_STATUS_EXPERIMENTAL__BEGIN = 2000000, + SPINEL_STATUS_EXPERIMENTAL__END = 2097152, +} spinel_status_t; + +typedef enum +{ + SPINEL_NET_STATE_OFFLINE = 0, + SPINEL_NET_STATE_DETACHED = 1, + SPINEL_NET_STATE_ATTACHING = 2, + SPINEL_NET_STATE_ATTACHED = 3, +} spinel_net_state_t; + +typedef enum +{ + SPINEL_NET_ROLE_NONE = 0, + SPINEL_NET_ROLE_CHILD = 1, + SPINEL_NET_ROLE_ROUTER = 2, + SPINEL_NET_ROLE_LEADER = 3, +} spinel_net_role_t; + +typedef enum +{ + SPINEL_SCAN_STATE_IDLE = 0, + SPINEL_SCAN_STATE_BEACON = 1, + SPINEL_SCAN_STATE_ENERGY = 2, +} spinel_scan_state_t; + +typedef enum +{ + SPINEL_POWER_STATE_OFFLINE = 0, + SPINEL_POWER_STATE_DEEP_SLEEP = 1, + SPINEL_POWER_STATE_STANDBY = 2, + SPINEL_POWER_STATE_LOW_POWER = 3, + SPINEL_POWER_STATE_ONLINE = 4, +} spinel_power_state_t; + +enum +{ + SPINEL_PROTOCOL_TYPE_ZIGBEE = 1, + SPINEL_PROTOCOL_TYPE_ZIGBEE_IP = 2, + SPINEL_PROTOCOL_TYPE_THREAD = 3, +}; + +typedef struct +{ + uint8_t bytes[8]; +} spinel_eui64_t; + +typedef struct +{ + uint8_t bytes[8]; +} spinel_net_xpanid_t; + +typedef struct +{ + uint8_t bytes[6]; +} spinel_eui48_t; + +typedef struct in6_addr spinel_ipv6addr_t; +typedef int spinel_ssize_t; +typedef unsigned int spinel_size_t; +typedef uint8_t spinel_tid_t; + +enum +{ + SPINEL_CMD_NOOP = 0, + SPINEL_CMD_RESET = 1, + SPINEL_CMD_PROP_VALUE_GET = 2, + SPINEL_CMD_PROP_VALUE_SET = 3, + SPINEL_CMD_PROP_VALUE_INSERT = 4, + SPINEL_CMD_PROP_VALUE_REMOVE = 5, + SPINEL_CMD_PROP_VALUE_IS = 6, + SPINEL_CMD_PROP_VALUE_INSERTED = 7, + SPINEL_CMD_PROP_VALUE_REMOVED = 8, + + SPINEL_CMD_NET_SAVE = 9, + SPINEL_CMD_NET_CLEAR = 10, + SPINEL_CMD_NET_RECALL = 11, + + SPINEL_CMD_HBO_OFFLOAD = 12, + SPINEL_CMD_HBO_RECLAIM = 13, + SPINEL_CMD_HBO_DROP = 14, + SPINEL_CMD_HBO_OFFLOADED = 15, + SPINEL_CMD_HBO_RECLAIMED = 16, + SPINEL_CMD_HBO_DROPED = 17, + + SPINEL_CMD_NEST__BEGIN = 15296, + SPINEL_CMD_NEST__END = 15360, + + SPINEL_CMD_VENDOR__BEGIN = 15360, + SPINEL_CMD_VENDOR__END = 16384, + + SPINEL_CMD_EXPERIMENTAL__BEGIN = 2000000, + SPINEL_CMD_EXPERIMENTAL__END = 2097152, +}; + +enum +{ + SPINEL_CAP_LOCK = 1, + SPINEL_CAP_NET_SAVE = 2, + SPINEL_CAP_HBO = 3, + SPINEL_CAP_POWER_SAVE = 4, + + SPINEL_CAP_802_15_4__BEGIN = 16, + SPINEL_CAP_802_15_4_2003 = (SPINEL_CAP_802_15_4__BEGIN + 0), + SPINEL_CAP_802_15_4_2006 = (SPINEL_CAP_802_15_4__BEGIN + 1), + SPINEL_CAP_802_15_4_2011 = (SPINEL_CAP_802_15_4__BEGIN + 2), + SPINEL_CAP_802_15_4_PIB = (SPINEL_CAP_802_15_4__BEGIN + 5), + SPINEL_CAP_802_15_4_2450MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 8), + SPINEL_CAP_802_15_4_915MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 9), + SPINEL_CAP_802_15_4_868MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 10), + SPINEL_CAP_802_15_4_915MHZ_BPSK = (SPINEL_CAP_802_15_4__BEGIN + 11), + SPINEL_CAP_802_15_4_868MHZ_BPSK = (SPINEL_CAP_802_15_4__BEGIN + 12), + SPINEL_CAP_802_15_4_915MHZ_ASK = (SPINEL_CAP_802_15_4__BEGIN + 13), + SPINEL_CAP_802_15_4_868MHZ_ASK = (SPINEL_CAP_802_15_4__BEGIN + 14), + SPINEL_CAP_802_15_4__END = 32, + + SPINEL_CAP_ROLE__BEGIN = 48, + SPINEL_CAP_ROLE_ROUTER = (SPINEL_CAP_ROLE__BEGIN + 0), + SPINEL_CAP_ROLE_SLEEPY = (SPINEL_CAP_ROLE__BEGIN + 1), + SPINEL_CAP_ROLE__END = 52, + + SPINEL_CAP_NET__BEGIN = 52, + SPINEL_CAP_NET_THREAD_1_0 = (SPINEL_CAP_NET__BEGIN + 0), + SPINEL_CAP_NET__END = 64, + + SPINEL_CAP_NEST__BEGIN = 15296, + SPINEL_CAP_NEST_LEGACY_INTERFACE = (SPINEL_CAP_NEST__BEGIN + 0), + SPINEL_CAP_NEST_LEGACY_NET_WAKE = (SPINEL_CAP_NEST__BEGIN + 1), + SPINEL_CAP_NEST_TRANSMIT_HOOK = (SPINEL_CAP_NEST__BEGIN + 2), + SPINEL_CAP_NEST__END = 15360, + + SPINEL_CAP_VENDOR__BEGIN = 15360, + SPINEL_CAP_VENDOR__END = 16384, + + SPINEL_CAP_EXPERIMENTAL__BEGIN = 2000000, + SPINEL_CAP_EXPERIMENTAL__END = 2097152, +}; + +typedef enum +{ + SPINEL_PROP_LAST_STATUS = 0, // status [i] + SPINEL_PROP_PROTOCOL_VERSION = 1, // interface type, major, minor, vendor [i,i,i,i] + SPINEL_PROP_CAPABILITIES = 2, // capability list [A(i)] + SPINEL_PROP_NCP_VERSION = 3, // version string [U] + SPINEL_PROP_INTERFACE_COUNT = 4, // Interface count [C] + SPINEL_PROP_POWER_STATE = 5, // PowerState [C] + SPINEL_PROP_HWADDR = 6, // PermEUI64 [E] + SPINEL_PROP_LOCK = 7, // PropLock [b] + SPINEL_PROP_HBO_MEM_MAX = 8, // Max offload mem [S] + SPINEL_PROP_HBO_BLOCK_MAX = 9, // Max offload block [S] + + SPINEL_PROP_PHY__BEGIN = 0x20, + SPINEL_PROP_PHY_ENABLED = SPINEL_PROP_PHY__BEGIN + 0, // [b] + SPINEL_PROP_PHY_CHAN = SPINEL_PROP_PHY__BEGIN + 1, // [C] + SPINEL_PROP_PHY_CHAN_SUPPORTED = SPINEL_PROP_PHY__BEGIN + 2, // [A(C)] + SPINEL_PROP_PHY_FREQ = SPINEL_PROP_PHY__BEGIN + 3, // kHz [L] + SPINEL_PROP_PHY_CCA_THRESHOLD = SPINEL_PROP_PHY__BEGIN + 4, // dBm [c] + SPINEL_PROP_PHY_TX_POWER = SPINEL_PROP_PHY__BEGIN + 5, // [c] + SPINEL_PROP_PHY_RSSI = SPINEL_PROP_PHY__BEGIN + 6, // dBm [c] + SPINEL_PROP_PHY_RAW_STREAM_ENABLED = SPINEL_PROP_PHY__BEGIN + 7, // [C] + SPINEL_PROP_PHY__END = 0x30, + + SPINEL_PROP_MAC__BEGIN = 0x30, + SPINEL_PROP_MAC_SCAN_STATE = SPINEL_PROP_MAC__BEGIN + 0, // [C] + SPINEL_PROP_MAC_SCAN_MASK = SPINEL_PROP_MAC__BEGIN + 1, // [A(C)] + SPINEL_PROP_MAC_SCAN_PERIOD = SPINEL_PROP_MAC__BEGIN + 2, // ms-per-channel [S] + SPINEL_PROP_MAC_SCAN_BEACON = SPINEL_PROP_MAC__BEGIN + 3, // chan,rssi,(laddr,saddr,panid,lqi),(proto,xtra) [CcT(ESSC)T(i).] + SPINEL_PROP_MAC_15_4_LADDR = SPINEL_PROP_MAC__BEGIN + 4, // [E] + SPINEL_PROP_MAC_15_4_SADDR = SPINEL_PROP_MAC__BEGIN + 5, // [S] + SPINEL_PROP_MAC_15_4_PANID = SPINEL_PROP_MAC__BEGIN + 6, // [S] + SPINEL_PROP_MAC__END = 0x40, + + SPINEL_PROP_NET__BEGIN = 0x40, + SPINEL_PROP_NET_SAVED = SPINEL_PROP_NET__BEGIN + 0, // [b] + SPINEL_PROP_NET_ENABLED = SPINEL_PROP_NET__BEGIN + 1, // [b] + SPINEL_PROP_NET_STATE = SPINEL_PROP_NET__BEGIN + 2, // [C] + SPINEL_PROP_NET_ROLE = SPINEL_PROP_NET__BEGIN + 3, // [C] + SPINEL_PROP_NET_NETWORK_NAME = SPINEL_PROP_NET__BEGIN + 4, // [U] + SPINEL_PROP_NET_XPANID = SPINEL_PROP_NET__BEGIN + 5, // [D] + SPINEL_PROP_NET_MASTER_KEY = SPINEL_PROP_NET__BEGIN + 6, // [D] + SPINEL_PROP_NET_KEY_SEQUENCE = SPINEL_PROP_NET__BEGIN + 7, // [L] + SPINEL_PROP_NET_PARTITION_ID = SPINEL_PROP_NET__BEGIN + 8, // [L] + SPINEL_PROP_NET__END = 0x50, + + SPINEL_PROP_THREAD__BEGIN = 0x50, + SPINEL_PROP_THREAD_LEADER = SPINEL_PROP_THREAD__BEGIN + 0, // [6] + SPINEL_PROP_THREAD_PARENT = SPINEL_PROP_THREAD__BEGIN + 1, // LADDR, SADDR [ES] + SPINEL_PROP_THREAD_CHILD_TABLE = SPINEL_PROP_THREAD__BEGIN + 2, // [A(T(ES))] + SPINEL_PROP_THREAD__END = 0x60, + + SPINEL_PROP_IPV6__BEGIN = 0x60, + SPINEL_PROP_IPV6_LL_ADDR = SPINEL_PROP_IPV6__BEGIN + 0, // [6] + SPINEL_PROP_IPV6_ML_ADDR = SPINEL_PROP_IPV6__BEGIN + 1, // [6C] + SPINEL_PROP_IPV6_ML_PREFIX = SPINEL_PROP_IPV6__BEGIN + 2, // [6C] + SPINEL_PROP_IPV6_ADDRESS_TABLE = SPINEL_PROP_IPV6__BEGIN + 3, // array(ipv6addr,prefixlen,flags) [A(6CL)] + SPINEL_PROP_IPV6_ROUTE_TABLE = SPINEL_PROP_IPV6__BEGIN + 4, // array(ipv6prefix,prefixlen,nexthop,flags) [A(6C6L)] + SPINEL_PROP_IPV6__END = 0x70, + + SPINEL_PROP_STREAM__BEGIN = 112, + SPINEL_PROP_STREAM_DEBUG = SPINEL_PROP_STREAM__BEGIN + 0, // [U] + SPINEL_PROP_STREAM_RAW = SPINEL_PROP_STREAM__BEGIN + 1, // [D] + SPINEL_PROP_STREAM_NET = SPINEL_PROP_STREAM__BEGIN + 2, // [D] + SPINEL_PROP_STREAM_NET_INSECURE = SPINEL_PROP_STREAM__BEGIN + 3, // [D] + SPINEL_PROP_STREAM__END = 128, + + SPINEL_PROP_15_4_PIB__BEGIN = 1024, + // For direct access to the 802.15.4 PID. + // Individual registers are fetched using + // `SPINEL_PROP_15_4_PIB__BEGIN+[PIB_IDENTIFIER]` + // Only supported if SPINEL_CAP_15_4_PIB is set. + SPINEL_PROP_15_4_PIB__END = 1280, + + SPINEL_PROP_NEST__BEGIN = 15296, + SPINEL_PROP_NEST__END = 15360, + + SPINEL_PROP_VENDOR__BEGIN = 15360, + SPINEL_PROP_VENDOR__END = 16384, + + SPINEL_PROP_EXPERIMENTAL__BEGIN = 2000000, + SPINEL_PROP_EXPERIMENTAL__END = 2097152, +} spinel_prop_key_t; + +// ---------------------------------------------------------------------------- + +#define SPINEL_HEADER_FLAG 0x80 + +#define SPINEL_HEADER_TID_SHIFT 0 +#define SPINEL_HEADER_TID_MASK (15 << SPINEL_HEADER_TID_SHIFT) + +#define SPINEL_HEADER_IID_SHIFT 4 +#define SPINEL_HEADER_IID_MASK (3 << SPINEL_HEADER_IID_SHIFT) + +#define SPINEL_HEADER_IID_0 (0 << SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_IID_1 (1 << SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_IID_2 (2 << SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_IID_3 (3 << SPINEL_HEADER_IID_SHIFT) + +#define SPINEL_HEADER_GET_IID(x) (((x) & SPINEL_HEADER_IID_MASK) >> SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_GET_TID(x) (spinel_tid_t)(((x)&SPINEL_HEADER_TID_MASK)>>SPINEL_HEADER_TID_SHIFT) + +#define SPINEL_GET_NEXT_TID(x) (spinel_tid_t)((x)>=0xF?1:(x)+1) + +#define SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT 4 +#define SPINEL_BEACON_THREAD_FLAG_VERSION_MASK (0xf << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT) +#define SPINEL_BEACON_THREAD_FLAG_JOINABLE (1 << 0) +#define SPINEL_BEACON_THREAD_FLAG_NATIVE (1 << 3) + +// ---------------------------------------------------------------------------- + +enum +{ + SPINEL_DATATYPE_NULL_C = 0, + SPINEL_DATATYPE_VOID_C = '.', + SPINEL_DATATYPE_BOOL_C = 'b', + SPINEL_DATATYPE_UINT8_C = 'C', + SPINEL_DATATYPE_INT8_C = 'c', + SPINEL_DATATYPE_UINT16_C = 'S', + SPINEL_DATATYPE_INT16_C = 's', + SPINEL_DATATYPE_UINT32_C = 'L', + SPINEL_DATATYPE_INT32_C = 'l', + SPINEL_DATATYPE_UINT_PACKED_C = 'i', + SPINEL_DATATYPE_IPv6ADDR_C = '6', + SPINEL_DATATYPE_EUI64_C = 'E', + SPINEL_DATATYPE_EUI48_C = 'e', + SPINEL_DATATYPE_DATA_C = 'D', + SPINEL_DATATYPE_UTF8_C = 'U', //!< Zero-Terminated UTF8-Encoded String + SPINEL_DATATYPE_STRUCT_C = 'T', + SPINEL_DATATYPE_ARRAY_C = 'A', +}; + +typedef char spinel_datatype_t; + +#define SPINEL_DATATYPE_NULL_S "" +#define SPINEL_DATATYPE_VOID_S "." +#define SPINEL_DATATYPE_BOOL_S "b" +#define SPINEL_DATATYPE_UINT8_S "C" +#define SPINEL_DATATYPE_INT8_S "c" +#define SPINEL_DATATYPE_UINT16_S "S" +#define SPINEL_DATATYPE_INT16_S "s" +#define SPINEL_DATATYPE_UINT32_S "L" +#define SPINEL_DATATYPE_INT32_S "l" +#define SPINEL_DATATYPE_UINT_PACKED_S "i" +#define SPINEL_DATATYPE_IPv6ADDR_S "6" +#define SPINEL_DATATYPE_EUI64_S "E" +#define SPINEL_DATATYPE_EUI48_S "e" +#define SPINEL_DATATYPE_DATA_S "D" +#define SPINEL_DATATYPE_UTF8_S "U" //!< Zero-Terminated UTF8-Encoded String +#define SPINEL_DATATYPE_STRUCT_S "T" +#define SPINEL_DATATYPE_ARRAY_S "A" + +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_pack(uint8_t *data_out, spinel_size_t data_len, + const char *pack_format, ...); +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vpack(uint8_t *data_out, spinel_size_t data_len, + const char *pack_format, va_list args); +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_unpack(const uint8_t *data_in, spinel_size_t data_len, + const char *pack_format, ...); +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, spinel_size_t data_len, + const char *pack_format, va_list args); + +SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_decode(const uint8_t *bytes, spinel_size_t len, + unsigned int *value); +SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_encode(uint8_t *bytes, spinel_size_t len, unsigned int value); +SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_size(unsigned int value); + +SPINEL_API_EXTERN const char *spinel_next_packed_datatype(const char *pack_format); + +// ---------------------------------------------------------------------------- + +__END_DECLS + +#endif /* defined(SPINEL_HEADER_INCLUDED) */