Project import
diff --git a/openthread/CONTRIBUTING.md b/openthread/CONTRIBUTING.md
new file mode 100644
index 0000000..2a78c3b
--- /dev/null
+++ b/openthread/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement]
+(https://cla.developers.google.com/about/google-individual)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### Code style
+Before submitting a pull request make sure to run:
+
+ make pretty
+
+This will format the source code according to the OpenThread code style. This
+command requires the `astyle` tool. Please refer to your operating system
+documentation to find information on how to install `astyle`.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the
+[Software Grant and Corporate Contributor License Agreement]
+(https://cla.developers.google.com/about/google-corporate).
+
diff --git a/openthread/LICENSE b/openthread/LICENSE
new file mode 100644
index 0000000..8417008
--- /dev/null
+++ b/openthread/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2016, The OpenThread Authors.
+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.
diff --git a/openthread/NOTICE b/openthread/NOTICE
new file mode 100644
index 0000000..6c1dad8
--- /dev/null
+++ b/openthread/NOTICE
@@ -0,0 +1,19 @@
+OpenThread is an open source implementation of the Thread 1.0.1 Final Specification.
+The Thread 1.0.1 Final Specification is promulgated by the Thread Group. The Thread
+Group is a non-profit organization formed for the purposes of defining one or
+more specifications, best practices, reference architectures, implementation
+guidelines and certification programs to promote the availability of compliant
+implementations of the Thread protocol. Information on becoming a Member, including
+information about the benefits thereof, can be found at http://threadgroup.org.
+
+OpenThread is not affiliated with or endorsed by the Thread Group. Implementation
+of this OpenThread code does not assure compliance with the Thread 1.0.1 Final
+Specification and does not convey the right to identify any final product as Thread
+certified. Members of the Thread Group may hold patents and other intellectual
+property rights relating to the Thread 1.0.1 Final Specification, ownership and
+licenses of which are subject to the Thread Group’s IP Policies, and not this license.
+
+The included copyright to the OpenThread code is subject to the license in the
+LICENSE file, and all other rights and licenses are expressly reserved.
+No warranty or assurance is made with respect to additional rights that may be
+required to implement this code.
diff --git a/openthread/README.google b/openthread/README.google
new file mode 100644
index 0000000..8be24c8
--- /dev/null
+++ b/openthread/README.google
@@ -0,0 +1,13 @@
+URL: https://github.com/openthread/openthread/tree/5298a515c374ec31518ed176d1a9dd6033370c32
+Version: 5298a515c374ec31518ed176d1a9dd6033370c32
+License: BSD-3
+License File: LICENSE
+
+Description:
+OpenThread is an open-source implementation of the Thread networking
+protocol. With OpenThread, Nest is making the technology used in Nest
+products more broadly available to accelerate the development of
+products for the connected home.
+
+Local Modifications:
+Stripped all unused files.
diff --git a/openthread/README.md b/openthread/README.md
new file mode 100644
index 0000000..9052f86
--- /dev/null
+++ b/openthread/README.md
@@ -0,0 +1,126 @@
+[![OpenThread][ot-logo]][ot-repo]
+[![Build Status][ot-travis-svg]][ot-travis]
+[![Build Status][ot-appveyor-svg]][ot-appveyor]
+[![Coverage Status][ot-codecov-svg]][ot-codecov]
+
+---
+
+OpenThread is an open-source implementation of the [Thread][thread]
+networking protocol. Nest has released OpenThread to make the technology
+used in Nest products more broadly available to developers to accelerate
+the development of products for the connected home.
+
+The Thread specification defines an IPv6-based reliable, secure and
+low-power wireless device-to-device communication protocol for home
+applications. More information about Thread can be found on
+[threadgroup.org](http://threadgroup.org/).
+
+[thread]: http://threadgroup.org/technology/ourtechnology
+[ot-repo]: https://github.com/openthread/openthread
+[ot-logo]: doc/images/openthread_logo.png
+[ot-travis]: https://travis-ci.org/openthread/openthread
+[ot-travis-svg]: https://travis-ci.org/openthread/openthread.svg?branch=master
+[ot-appveyor]: https://ci.appveyor.com/project/jwhui/openthread
+[ot-appveyor-svg]: https://ci.appveyor.com/api/projects/status/r5qwyhn9p26nmfk3?svg=true
+[ot-codecov]: https://codecov.io/gh/openthread/openthread
+[ot-codecov-svg]: https://codecov.io/gh/openthread/openthread/branch/master/graph/badge.svg
+
+## Features ##
+
+ * Highly portable: OS and platform agnostic with a radio
+ abstraction layer
+ * Implements the End Device, Router, Leader and Border Router roles
+ * Small memory footprint
+
+OpenThread implements all Thread networking layers including IPv6,
+6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, and
+Mesh Routing.
+
+
+# Who is supporting OpenThread #
+
+![OpenThread Contributor Logos](doc/images/openthread_contrib.png)
+
+Nest, along with ARM, Atmel, a subsidiary of Microchip Technology,
+Dialog Semiconductor, Microsoft Corporation, Nordic Semiconductor,
+Qualcomm Technologies, Inc. (a subsidiary of Qualcomm Incorporated),
+and Texas Instruments Incorporated are contributing to the ongoing
+development of OpenThread.
+
+
+# Getting started #
+
+The easiest way to get started is to run the CLI example in
+`/examples/apps/cli`. See the
+[CLI example README](examples/apps/cli/README.md)
+for more details.
+
+
+## What's included ##
+
+In the repo you'll find the following directories and files:
+
+File/Folder | Provides
+--------------|----------------------------------------------------------------
+`doc` | Doxygen docs
+`examples` | Sample applications demonstrating OpenThread
+`include` | Public API header files
+`src` | Core implementation of the Thread standard and related add-ons
+`tests` | Unit and Thread conformance tests
+`third_party` | Third-party code used by OpenThread
+`tools` | Helpful utilities related to the OpenThread project
+
+
+## Documentation ##
+
+The Doxygen reference docs are [hosted online][ot-docs] and generated
+as part of the build.
+
+[ot-docs]: http://openthread.github.io/openthread/
+
+
+# Getting help #
+
+Submit bugs and feature requests to [issue tracker][ot-issues]. Usage
+questions? Post your questions to [Stack Overflow][stackoverflow] using the
+[`openthread` tag][ot-tag]. We also use Google Groups for discussion
+and announcements:
+
+ * [openthread-announce](https://groups.google.com/forum/#!forum/openthread-announce)
+ \- subscribe for release notes and new updates on OpenThread
+ * [openthread-users](https://groups.google.com/forum/#!forum/openthread-users)
+ \- the best place for users to discuss OpenThread and interact with
+ the OpenThread team
+ * [openthread-devel](https://groups.google.com/forum/#!forum/openthread-devel)
+ \- team members discuss the on-going development of OpenThread
+
+[ot-issues]: https://github.com/openthread/openthread/issues
+[stackoverflow]: http://stackoverflow.com/
+[ot-tag]: http://stackoverflow.com/questions/tagged/openthread
+
+
+# Versioning #
+
+OpenThread follows [the Semantic Versioning guidelines][semver] for
+release cycle transparency and to maintain backwards compatibility.
+OpenThread's versioning is independent of the Thread protocol
+specification version but will clearly indicate which version of the
+specification it currently supports.
+
+[semver]: http://semver.org/
+
+
+# Contributing #
+
+See the [`CONTRIBUTING.md`](CONTRIBUTING.md) file for more information.
+
+
+# License #
+
+OpenThread is released under the [BSD 3-Clause license](LICENSE). See
+the [`LICENSE`](LICENSE) file for more information.
+
+Please only use the OpenThread name and marks when accurately
+referencing this software distribution. Do not use the marks in
+a way that suggests you are endorsed by or otherwise affiliated with
+Nest, Google, or The Thread Group.
diff --git a/openthread/src/ncp/PROTOCOL.md b/openthread/src/ncp/PROTOCOL.md
new file mode 100644
index 0000000..f5aff68
--- /dev/null
+++ b/openthread/src/ncp/PROTOCOL.md
@@ -0,0 +1,7 @@
+Spinel NCP Protocol Documentation
+=================================
+
+This document has moved into the top level `doc` folder and renamed
+[`draft-spinel-protocol.txt`](../../doc/draft-spinel-protocol.txt)
+and [`draft-spinel-protocol.html`](../../doc/draft-spinel-protocol.html).
+
diff --git a/openthread/src/ncp/spinel.c b/openthread/src/ncp/spinel.c
new file mode 100644
index 0000000..9449d6d
--- /dev/null
+++ b/openthread/src/ncp/spinel.c
@@ -0,0 +1,1996 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+// ----------------------------------------------------------------------------
+// MARK: -
+
+// IAR's errno.h apparently doesn't define EOVERFLOW.
+#ifndef EOVERFLOW
+// There is no real good choice for what to set
+// errno to in this case, so we just pick the
+// value '1' somewhat arbitrarily.
+#define EOVERFLOW 1
+#endif
+
+// IAR's errno.h apparently doesn't define EINVAL.
+#ifndef EINVAL
+// There is no real good choice for what to set
+// errno to in this case, so we just pick the
+// value '1' somewhat arbitrarily.
+#define EINVAL 1
+#endif
+
+#ifdef _KERNEL_MODE
+#define va_copy(destination, source) ((destination) = (source))
+#undef errno
+#define assert_printf(fmt, ...)
+#endif
+
+#if defined(errno) && SPINEL_PLATFORM_DOESNT_IMPLEMENT_ERRNO_VAR
+#error "SPINEL_PLATFORM_DOESNT_IMPLEMENT_ERRNO_VAR is set but errno is already defined."
+#endif
+
+// Work-around for platforms that don't implement the `errno` variable.
+#if !defined(errno) && SPINEL_PLATFORM_DOESNT_IMPLEMENT_ERRNO_VAR
+static int spinel_errno_workaround_;
+#define errno spinel_errno_workaround_
+#endif // SPINEL_PLATFORM_DOESNT_IMPLEMENT_ERRNO_VAR
+
+#ifndef assert_printf
+#if SPINEL_PLATFORM_DOESNT_IMPLEMENT_FPRINTF
+#define assert_printf(fmt, ...) \
+ printf(__FILE__ ":%d: " fmt "\n", __LINE__, __VA_ARGS__)
+#else // if SPINEL_PLATFORM_DOESNT_IMPLEMENT_FPRINTF
+#define assert_printf(fmt, ...) \
+ fprintf(stderr, __FILE__ ":%d: " fmt "\n", __LINE__, __VA_ARGS__)
+#endif // else SPINEL_PLATFORM_DOESNT_IMPLEMENT_FPRINTF
+#endif
+
+#if !HAVE_STRNLEN
+// Provide a working strnlen if the platform doesn't have one.
+static size_t spinel_strnlen_(const char *s, size_t maxlen)
+{
+ size_t ret;
+ for (ret = 0; (ret < maxlen) && (s[ret] != 0); ret++)
+ {
+ // Empty loop.
+ }
+ return ret;
+}
+#define strnlen spinel_strnlen_
+#endif
+
+#ifndef require_action
+#if SPINEL_PLATFORM_SHOULD_LOG_ASSERTS
+#define require_action(c, l, a) \
+ do { if (!(c)) { \
+ assert_printf("Requirement Failed (%s)", # c); \
+ a; \
+ goto l; \
+ } } while (0)
+#else // if DEBUG
+#define require_action(c, l, a) \
+ do { if (!(c)) { \
+ a; \
+ goto l; \
+ } } while (0)
+#endif // else DEBUG
+#endif // ifndef require_action
+
+#ifndef require
+#define require(c, l) require_action(c, l, {})
+#endif
+
+
+typedef struct {
+ va_list obj;
+} va_list_obj;
+
+// ----------------------------------------------------------------------------
+// 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 |= (unsigned int)((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_ssize_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 ((spinel_ssize_t)len >= encoded_size)
+ {
+ spinel_ssize_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;
+}
+
+static spinel_ssize_t
+spinel_datatype_vunpack_(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, va_list_obj *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->obj, 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->obj, 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->obj, uint16_t *);
+ require_action(data_len >= sizeof(uint16_t), bail, (ret = -1, errno = EOVERFLOW));
+
+ if (arg_ptr)
+ {
+ *arg_ptr = (uint16_t)((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->obj, uint32_t *);
+ require_action(data_len >= sizeof(uint32_t), bail, (ret = -1, errno = EOVERFLOW));
+
+ if (arg_ptr)
+ {
+ *arg_ptr = (uint32_t)((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->obj, 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->obj, 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->obj, 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:
+ {
+ unsigned int *arg_ptr = va_arg(args->obj, unsigned int *);
+ spinel_ssize_t pui_len = spinel_packed_uint_decode(data_ptr, data_len, arg_ptr);
+
+ // Range check
+ require_action(NULL == arg_ptr || (*arg_ptr < SPINEL_MAX_UINT_PACKED), bail, {ret = -1; errno = ERANGE;});
+
+ require(pui_len > 0, bail);
+
+ require(pui_len <= (spinel_ssize_t)data_len, bail);
+
+ ret += pui_len;
+ data_ptr += pui_len;
+ data_len -= (spinel_size_t)pui_len;
+ break;
+ }
+
+ case SPINEL_DATATYPE_UTF8_C:
+ {
+ const char **arg_ptr = va_arg(args->obj, const char **);
+ size_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 += (spinel_size_t)len;
+ data_ptr += len;
+ data_len -= (spinel_size_t)len;
+ break;
+ }
+
+ case SPINEL_DATATYPE_DATA_C:
+ case SPINEL_DATATYPE_DATA_WLEN_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->obj, const uint8_t **);
+ unsigned int *block_len_ptr = va_arg(args->obj, unsigned int *);
+ char nextformat = *spinel_next_packed_datatype(pack_format);
+
+ if ( (pack_format[0] == SPINEL_DATATYPE_DATA_WLEN_C)
+ || ( (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 = (uint16_t)data_len;
+ pui_len = 0;
+ }
+
+ require_action((spinel_ssize_t)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 += (uint16_t)pui_len;
+ ret += block_len;
+ data_ptr += block_len;
+ data_len -= block_len;
+ break;
+ }
+
+
+ case 'T':
+ case SPINEL_DATATYPE_STRUCT_C:
+ {
+ spinel_ssize_t pui_len = 0;
+ uint16_t block_len = 0;
+ spinel_ssize_t actual_len = 0;
+ const uint8_t *block_ptr = data_ptr;
+ char nextformat = *spinel_next_packed_datatype(pack_format);
+
+ if ( (pack_format[0] == SPINEL_DATATYPE_STRUCT_C)
+ || ( (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 = (uint16_t)data_len;
+ pui_len = 0;
+ }
+
+ require_action((spinel_ssize_t)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(actual_len > -1, bail, (ret = -1, errno = EOVERFLOW));
+
+ if (pui_len)
+ {
+ block_len += (uint16_t)pui_len;
+ }
+ else
+ {
+ block_len = (uint16_t)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_unpack(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, ...)
+{
+ spinel_ssize_t ret;
+ va_list_obj args;
+ va_start(args.obj, pack_format);
+
+ ret = spinel_datatype_vunpack_(data_ptr, data_len, pack_format, &args);
+
+ va_end(args.obj);
+ 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;
+ va_list_obj args_obj;
+ va_copy(args_obj.obj, args);
+
+ ret = spinel_datatype_vunpack_(data_ptr, data_len, pack_format, &args_obj);
+
+ va_end(args_obj.obj);
+ return ret;
+}
+
+static spinel_ssize_t
+spinel_datatype_vpack_(uint8_t *data_ptr, spinel_size_t data_len_max, const char *pack_format, va_list_obj *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 = (bool)va_arg(args->obj, 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 = (uint8_t)va_arg(args->obj, 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 = (uint16_t)va_arg(args->obj, int);
+ ret += sizeof(uint16_t);
+
+ if (data_len_max >= sizeof(uint16_t))
+ {
+ data_ptr[1] = (arg >> 8) & 0xff;
+ data_ptr[0] = (arg >> 0) & 0xff;
+ 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 = (uint32_t)va_arg(args->obj, int);
+ ret += sizeof(uint32_t);
+
+ if (data_len_max >= sizeof(uint32_t))
+ {
+ data_ptr[3] = (arg >> 24) & 0xff;
+ data_ptr[2] = (arg >> 16) & 0xff;
+ data_ptr[1] = (arg >> 8) & 0xff;
+ data_ptr[0] = (arg >> 0) & 0xff;
+ data_ptr += sizeof(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->obj, 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->obj, 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->obj, 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->obj, uint32_t);
+ spinel_ssize_t encoded_size;
+
+ // Range Check
+ require_action(arg < SPINEL_MAX_UINT_PACKED, bail, {ret = -1; errno = EINVAL;});
+
+ encoded_size = spinel_packed_uint_encode(data_ptr, data_len_max, arg);
+ ret += encoded_size;
+
+ if ((spinel_ssize_t)data_len_max >= encoded_size)
+ {
+ data_ptr += encoded_size;
+ data_len_max -= (spinel_size_t)encoded_size;
+ }
+ else
+ {
+ data_len_max = 0;
+ }
+
+ break;
+ }
+
+ case SPINEL_DATATYPE_UTF8_C:
+ {
+ const char *string_arg = va_arg(args->obj, 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 += (spinel_size_t)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 -= (spinel_size_t)string_arg_len;
+ }
+ else
+ {
+ data_len_max = 0;
+ }
+
+ break;
+ }
+
+ case SPINEL_DATATYPE_DATA_WLEN_C:
+ case SPINEL_DATATYPE_DATA_C:
+ {
+ const uint8_t *arg = va_arg(args->obj, const uint8_t *);
+ uint32_t data_size_arg = va_arg(args->obj, uint32_t);
+ spinel_ssize_t size_len = 0;
+ char nextformat = *spinel_next_packed_datatype(pack_format);
+
+ if ( (pack_format[0] == SPINEL_DATATYPE_DATA_WLEN_C)
+ || ( (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 += (spinel_size_t)size_len + data_size_arg;
+
+ if (data_len_max >= (spinel_size_t)size_len + data_size_arg)
+ {
+ data_ptr += size_len;
+ data_len_max -= (spinel_size_t)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 'T':
+ 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_obj subargs;
+ va_copy(subargs.obj, args->obj);
+ struct_len = spinel_datatype_vpack_(NULL, 0, pack_format + 2, &subargs);
+ va_end(subargs.obj);
+ }
+
+ if ( (pack_format[0] == SPINEL_DATATYPE_STRUCT_C)
+ || ( (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 <= (spinel_ssize_t)data_len_max)
+ {
+ data_ptr += size_len;
+ data_len_max -= (spinel_size_t)size_len;
+
+ struct_len = spinel_datatype_vpack_(data_ptr, data_len_max, pack_format + 2, args);
+
+ data_ptr += struct_len;
+ data_len_max -= (spinel_size_t)struct_len;
+ }
+ else
+ {
+ data_len_max = 0;
+ }
+
+ break;
+ }
+
+ case '.':
+ // Skip.
+ break;
+
+ default:
+ // Unsupported Type!
+ ret = -1;
+ errno = EINVAL;
+ goto bail;
+
+ }
+ }
+
+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_obj args;
+ va_start(args.obj, pack_format);
+
+ ret = spinel_datatype_vpack_(data_ptr, data_len_max, pack_format, &args);
+
+ va_end(args.obj);
+ 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)
+{
+ int ret;
+ va_list_obj args_obj;
+ va_copy(args_obj.obj, args);
+
+ ret = spinel_datatype_vpack_(data_ptr, data_len_max, pack_format, &args_obj);
+
+ va_end(args_obj.obj);
+ return ret;
+}
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+// **** LCOV_EXCL_START ****
+
+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_INTERFACE_TYPE:
+ ret = "PROP_INTERFACE_TYPE";
+ break;
+
+ case SPINEL_PROP_VENDOR_ID:
+ ret = "PROP_VENDOR_ID";
+ break;
+
+ case SPINEL_PROP_CAPS:
+ ret = "PROP_CAPS";
+ 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_HOST_POWER_STATE:
+ ret = "PROP_HOST_POWER_STATE";
+ 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_UART_BITRATE:
+ ret = "PROP_UART_BITRATE";
+ break;
+
+ case SPINEL_PROP_UART_XON_XOFF:
+ ret = "PROP_UART_XON_XOFF";
+ 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_MAC_EXTENDED_ADDR:
+ ret = "PROP_MAC_EXTENDED_ADDR";
+ break;
+
+ case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
+ ret = "PROP_MAC_RAW_STREAM_ENABLED";
+ break;
+
+ case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
+ ret = "PROP_MAC_PROMISCUOUS_MODE";
+ 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_ENERGY_SCAN_RESULT:
+ ret = "PROP_MAC_SCAN_ENERGY_SCAN_RESULT";
+ break;
+
+ case SPINEL_PROP_MAC_SCAN_PERIOD:
+ ret = "PROP_MAC_SCAN_PERIOD";
+ 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_IF_UP:
+ ret = "PROP_NET_IF_UP";
+ break;
+
+ case SPINEL_PROP_NET_STACK_UP:
+ ret = "PROP_NET_STACK_UP";
+ 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_COUNTER:
+ ret = "PROP_NET_KEY_SEQUENCE_COUNTER";
+ break;
+
+ case SPINEL_PROP_NET_PARTITION_ID:
+ ret = "PROP_NET_PARTITION_ID";
+ break;
+
+ case SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME:
+ ret = "PROP_NET_KEY_SWITCH_GUARDTIME";
+ break;
+
+ case SPINEL_PROP_NET_PSKC:
+ ret = "PROP_NET_PSKC";
+ break;
+
+ case SPINEL_PROP_THREAD_LEADER_ADDR:
+ ret = "PROP_THREAD_LEADER_ADDR";
+ break;
+
+ case SPINEL_PROP_THREAD_LEADER_RID:
+ ret = "PROP_THREAD_LEADER_RID";
+ break;
+
+ case SPINEL_PROP_THREAD_LEADER_WEIGHT:
+ ret = "PROP_THREAD_LEADER_WEIGHT";
+ break;
+
+ case SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT:
+ ret = "PROP_THREAD_LOCAL_LEADER_WEIGHT";
+ break;
+
+ case SPINEL_PROP_THREAD_NETWORK_DATA_VERSION:
+ ret = "PROP_THREAD_NETWORK_DATA_VERSION";
+ break;
+
+ case SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION:
+ ret = "PROP_THREAD_STABLE_NETWORK_DATA_VERSION";
+ break;
+
+ case SPINEL_PROP_THREAD_NETWORK_DATA:
+ ret = "PROP_THREAD_NETWORK_DATA";
+ 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;
+
+ case SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD:
+ ret = "PROP_IPV6_ICMP_PING_OFFLOAD";
+ break;
+
+ case SPINEL_PROP_THREAD_PARENT:
+ ret = "PROP_THREAD_PARENT";
+ break;
+
+ case SPINEL_PROP_THREAD_STABLE_NETWORK_DATA:
+ ret = "PROP_THREAD_STABLE_NETWORK_DATA";
+ break;
+
+ case SPINEL_PROP_THREAD_LEADER_NETWORK_DATA:
+ ret = "PROP_THREAD_LEADER_NETWORK_DATA";
+ break;
+
+ case SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA:
+ ret = "PROP_THREAD_STABLE_LEADER_NETWORK_DATA";
+ break;
+
+ case SPINEL_PROP_THREAD_ON_MESH_NETS:
+ ret = "PROP_THREAD_ON_MESH_NETS";
+ break;
+
+ case SPINEL_PROP_THREAD_OFF_MESH_ROUTES:
+ ret = "PROP_THREAD_OFF_MESH_ROUTES";
+ break;
+
+ case SPINEL_PROP_THREAD_ASSISTING_PORTS:
+ ret = "PROP_THREAD_ASSISTING_PORTS";
+ break;
+
+ case SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE:
+ ret = "PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE";
+ break;
+
+ case SPINEL_PROP_THREAD_JOINERS:
+ ret = "PROP_THREAD_JOINERS";
+ break;
+
+ case SPINEL_PROP_THREAD_COMMISSIONER_ENABLED:
+ ret = "PROP_THREAD_COMMISSIONER_ENABLED";
+ break;
+
+ case SPINEL_PROP_THREAD_BA_PROXY_ENABLED:
+ ret = "PROP_THREAD_BA_PROXY_ENABLED";
+ break;
+
+ case SPINEL_PROP_THREAD_BA_PROXY_STREAM:
+ ret = "PROP_THREAD_BA_PROXY_STREAM";
+ break;
+
+ case SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU:
+ ret = "PROP_THREAD_RLOC16_DEBUG_PASSTHRU";
+ break;
+
+ case SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED:
+ ret = "PROP_THREAD_ROUTER_ROLE_ENABLED";
+ break;
+
+ case SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD:
+ ret = "PROP_THREAD_ROUTER_UPGRADE_THRESHOLD";
+ break;
+
+ case SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY:
+ ret = "PROP_THREAD_CONTEXT_REUSE_DELAY";
+ break;
+
+ case SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG:
+ ret = "PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG";
+ break;
+
+ case SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING:
+ ret = "PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING";
+ break;
+
+ case SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID:
+ ret = "PROP_THREAD_DISCOVERY_SCAN_PANID";
+ break;
+
+ case SPINEL_PROP_THREAD_STEERING_DATA:
+ ret = "PROP_THREAD_STEERING_DATA";
+ break;
+
+ case SPINEL_PROP_MAC_WHITELIST:
+ ret = "PROP_MAC_WHITELIST";
+ break;
+
+ case SPINEL_PROP_MAC_WHITELIST_ENABLED:
+ ret = "PROP_MAC_WHITELIST_ENABLED";
+ break;
+
+ case SPINEL_PROP_THREAD_MODE:
+ ret = "PROP_THREAD_MODE";
+ break;
+
+ case SPINEL_PROP_THREAD_CHILD_TIMEOUT:
+ ret = "PROP_THREAD_CHILD_TIMEOUT";
+ break;
+
+ case SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING:
+ ret = "PROP_NET_REQUIRE_JOIN_EXISTING";
+ break;
+
+ case SPINEL_PROP_NEST_STREAM_MFG:
+ ret = "PROP_NEST_STREAM_MFG";
+ break;
+
+ case SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT:
+ ret = "PROP_THREAD_NETWORK_ID_TIMEOUT";
+ break;
+
+ case SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS:
+ ret = "PROP_THREAD_ACTIVE_ROUTER_IDS";
+ break;
+
+ case SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD:
+ ret = "PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD";
+ break;
+
+ case SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER:
+ ret = "PROP_THREAD_ROUTER_SELECTION_JITTER";
+ break;
+
+ case SPINEL_PROP_THREAD_PREFERRED_ROUTER_ID:
+ ret = "PROP_THREAD_PREFERRED_ROUTER_ID";
+ break;
+
+ case SPINEL_PROP_THREAD_NEIGHBOR_TABLE:
+ ret = "PROP_THREAD_NEIGHBOR_TABLE";
+ break;
+
+ case SPINEL_PROP_JAM_DETECT_ENABLE:
+ ret = "PROP_JAM_DETECT_ENABLE";
+ break;
+
+ case SPINEL_PROP_JAM_DETECTED:
+ ret = "PROP_JAM_DETECTED";
+ break;
+
+ case SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD:
+ ret = "PROP_JAM_DETECT_RSSI_THRESHOLD";
+ break;
+
+ case SPINEL_PROP_JAM_DETECT_WINDOW:
+ ret = "PROP_JAM_DETECT_WINDOW";
+ break;
+
+ case SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP:
+ ret = "PROP_JAM_DETECT_HISTORY_BITMAP";
+ break;
+
+ case SPINEL_PROP_GPIO_CONFIG:
+ ret = "PROP_GPIO_CONFIG";
+ break;
+
+ case SPINEL_PROP_GPIO_STATE:
+ ret = "PROP_GPIO_STATE";
+ break;
+
+ case SPINEL_PROP_GPIO_STATE_SET:
+ ret = "PROP_GPIO_STATE_SET";
+ break;
+
+ case SPINEL_PROP_GPIO_STATE_CLEAR:
+ ret = "PROP_GPIO_STATE_CLEAR";
+ break;
+
+ case SPINEL_PROP_CNTR_RESET:
+ ret = "PROP_CNTR_RESET";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_TOTAL:
+ ret = "PROP_CNTR_TX_PKT_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_ACK_REQ:
+ ret = "PROP_CNTR_TX_PKT_ACK_REQ";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_ACKED:
+ ret = "PROP_CNTR_TX_PKT_ACKED";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ:
+ ret = "PROP_CNTR_TX_PKT_NO_ACK_REQ";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_DATA:
+ ret = "PROP_CNTR_TX_PKT_DATA";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_DATA_POLL:
+ ret = "PROP_CNTR_TX_PKT_DATA_POLL";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_BEACON:
+ ret = "PROP_CNTR_TX_PKT_BEACON";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ:
+ ret = "PROP_CNTR_TX_PKT_BEACON_REQ";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_OTHER:
+ ret = "PROP_CNTR_TX_PKT_OTHER";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_RETRY:
+ ret = "PROP_CNTR_TX_PKT_RETRY";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_ERR_CCA:
+ ret = "PROP_CNTR_TX_ERR_CCA";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_UNICAST:
+ ret = "PROP_CNTR_TX_PKT_UNICAST";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_PKT_BROADCAST:
+ ret = "PROP_CNTR_TX_PKT_BROADCAST";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_ERR_ABORT:
+ ret = "PROP_CNTR_TX_ERR_ABORT";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_TOTAL:
+ ret = "PROP_CNTR_RX_PKT_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_DATA:
+ ret = "PROP_CNTR_RX_PKT_DATA";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_DATA_POLL:
+ ret = "PROP_CNTR_RX_PKT_DATA_POLL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_BEACON:
+ ret = "PROP_CNTR_RX_PKT_BEACON";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ:
+ ret = "PROP_CNTR_RX_PKT_BEACON_REQ";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_OTHER:
+ ret = "PROP_CNTR_RX_PKT_OTHER";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_FILT_WL:
+ ret = "PROP_CNTR_RX_PKT_FILT_WL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_FILT_DA:
+ ret = "PROP_CNTR_RX_PKT_FILT_DA";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_EMPTY:
+ ret = "PROP_CNTR_RX_ERR_EMPTY";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR:
+ ret = "PROP_CNTR_RX_ERR_UKWN_NBR";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR:
+ ret = "PROP_CNTR_RX_ERR_NVLD_SADDR";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_SECURITY:
+ ret = "PROP_CNTR_RX_ERR_SECURITY";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_BAD_FCS:
+ ret = "PROP_CNTR_RX_ERR_BAD_FCS";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_ERR_OTHER:
+ ret = "PROP_CNTR_RX_ERR_OTHER";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_DUP:
+ ret = "PROP_CNTR_RX_PKT_DUP";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_UNICAST:
+ ret = "PROP_CNTR_RX_PKT_UNICAST";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_PKT_BROADCAST:
+ ret = "PROP_CNTR_RX_PKT_BROADCAST";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL:
+ ret = "PROP_CNTR_TX_IP_SEC_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL:
+ ret = "PROP_CNTR_TX_IP_INSEC_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_IP_DROPPED:
+ ret = "PROP_CNTR_TX_IP_DROPPED";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL:
+ ret = "PROP_CNTR_RX_IP_SEC_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL:
+ ret = "PROP_CNTR_RX_IP_INSEC_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_IP_DROPPED:
+ ret = "PROP_CNTR_RX_IP_DROPPED";
+ break;
+
+ case SPINEL_PROP_CNTR_TX_SPINEL_TOTAL:
+ ret = "PROP_CNTR_TX_SPINEL_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_SPINEL_TOTAL:
+ ret = "PROP_CNTR_RX_SPINEL_TOTAL";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_SPINEL_ERR:
+ ret = "PROP_CNTR_RX_SPINEL_ERR";
+ break;
+
+ case SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID:
+ ret = "PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID";
+ break;
+
+ case SPINEL_PROP_CNTR_IP_TX_SUCCESS:
+ ret = "PROP_CNTR_IP_TX_SUCCESS";
+ break;
+
+ case SPINEL_PROP_CNTR_IP_RX_SUCCESS:
+ ret = "PROP_CNTR_IP_RX_SUCCESS";
+ break;
+
+ case SPINEL_PROP_CNTR_IP_TX_FAILURE:
+ ret = "PROP_CNTR_IP_TX_FAILURE";
+ break;
+
+ case SPINEL_PROP_CNTR_IP_RX_FAILURE:
+ ret = "PROP_CNTR_IP_RX_FAILURE";
+ break;
+
+ case SPINEL_PROP_MSG_BUFFER_COUNTERS:
+ ret = "PROP_MSG_BUFFER_COUNTERS";
+ break;
+
+ case SPINEL_PROP_NEST_LEGACY_ULA_PREFIX:
+ ret = "PROP_NEST_LEGACY_ULA_PREFIX";
+ break;
+
+ case SPINEL_PROP_NEST_LEGACY_JOINED_NODE:
+ ret = "PROP_NEST_LEGACY_JOINED_NODE";
+ break;
+
+ case SPINEL_PROP_DEBUG_TEST_ASSERT:
+ ret = "PROP_DEBUG_TEST_ASSERT";
+ break;
+
+ case SPINEL_PROP_DEBUG_NCP_LOG_LEVEL:
+ ret = "PROP_DEBUG_NCP_LOG_LEVEL";
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+const char *spinel_net_role_to_cstr(uint8_t net_role)
+{
+ const char *ret = "NET_ROLE_UNKNONW";
+
+ switch (net_role)
+ {
+ case SPINEL_NET_ROLE_DETACHED:
+ ret = "NET_ROLE_DETACHED";
+ break;
+
+ case SPINEL_NET_ROLE_CHILD:
+ ret = "NET_ROLE_CHILD";
+ break;
+
+ case SPINEL_NET_ROLE_ROUTER:
+ ret = "NET_ROLE_ROUTER";
+ break;
+
+ case SPINEL_NET_ROLE_LEADER:
+ ret = "NET_ROLE_LEADER";
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+const char *spinel_status_to_cstr(spinel_status_t status)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (status)
+ {
+ case SPINEL_STATUS_OK:
+ ret = "STATUS_OK";
+ break;
+
+ case SPINEL_STATUS_FAILURE:
+ ret = "STATUS_FAILURE";
+ break;
+
+ case SPINEL_STATUS_UNIMPLEMENTED:
+ ret = "STATUS_UNIMPLEMENTED";
+ break;
+
+ case SPINEL_STATUS_INVALID_ARGUMENT:
+ ret = "STATUS_INVALID_ARGUMENT";
+ break;
+
+ case SPINEL_STATUS_INVALID_STATE:
+ ret = "STATUS_INVALID_STATE";
+ break;
+
+ case SPINEL_STATUS_INVALID_COMMAND:
+ ret = "STATUS_INVALID_COMMAND";
+ break;
+
+ case SPINEL_STATUS_INVALID_INTERFACE:
+ ret = "STATUS_INVALID_INTERFACE";
+ break;
+
+ case SPINEL_STATUS_INTERNAL_ERROR:
+ ret = "STATUS_INTERNAL_ERROR";
+ break;
+
+ case SPINEL_STATUS_SECURITY_ERROR:
+ ret = "STATUS_SECURITY_ERROR";
+ break;
+
+ case SPINEL_STATUS_PARSE_ERROR:
+ ret = "STATUS_PARSE_ERROR";
+ break;
+
+ case SPINEL_STATUS_IN_PROGRESS:
+ ret = "STATUS_IN_PROGRESS";
+ break;
+
+ case SPINEL_STATUS_NOMEM:
+ ret = "STATUS_NOMEM";
+ break;
+
+ case SPINEL_STATUS_BUSY:
+ ret = "STATUS_BUSY";
+ break;
+
+ case SPINEL_STATUS_PROP_NOT_FOUND:
+ ret = "STATUS_PROP_NOT_FOUND";
+ break;
+
+ case SPINEL_STATUS_DROPPED:
+ ret = "STATUS_DROPPED";
+ break;
+
+ case SPINEL_STATUS_EMPTY:
+ ret = "STATUS_EMPTY";
+ break;
+
+ case SPINEL_STATUS_CMD_TOO_BIG:
+ ret = "STATUS_CMD_TOO_BIG";
+ break;
+
+ case SPINEL_STATUS_NO_ACK:
+ ret = "STATUS_NO_ACK";
+ break;
+
+ case SPINEL_STATUS_CCA_FAILURE:
+ ret = "STATUS_CCA_FAILURE";
+ break;
+
+ case SPINEL_STATUS_ALREADY:
+ ret = "STATUS_ALREADY";
+ break;
+
+ case SPINEL_STATUS_ITEM_NOT_FOUND:
+ ret = "STATUS_ITEM_NOT_FOUND";
+ break;
+
+ case SPINEL_STATUS_INVALID_COMMAND_FOR_PROP:
+ ret = "STATUS_INVALID_COMMAND_FOR_PROP";
+ break;
+
+ case SPINEL_STATUS_JOIN_FAILURE:
+ ret = "STATUS_JOIN_FAILURE";
+ break;
+
+ case SPINEL_STATUS_JOIN_SECURITY:
+ ret = "STATUS_JOIN_SECURITY";
+ break;
+
+ case SPINEL_STATUS_JOIN_NO_PEERS:
+ ret = "STATUS_JOIN_NO_PEERS";
+ break;
+
+ case SPINEL_STATUS_JOIN_INCOMPATIBLE:
+ ret = "STATUS_JOIN_INCOMPATIBLE";
+ break;
+
+ case SPINEL_STATUS_RESET_POWER_ON:
+ ret = "STATUS_RESET_POWER_ON";
+ break;
+
+ case SPINEL_STATUS_RESET_EXTERNAL:
+ ret = "STATUS_RESET_EXTERNAL";
+ break;
+
+ case SPINEL_STATUS_RESET_SOFTWARE:
+ ret = "STATUS_RESET_SOFTWARE";
+ break;
+
+ case SPINEL_STATUS_RESET_FAULT:
+ ret = "STATUS_RESET_FAULT";
+ break;
+
+ case SPINEL_STATUS_RESET_CRASH:
+ ret = "STATUS_RESET_CRASH";
+ break;
+
+ case SPINEL_STATUS_RESET_ASSERT:
+ ret = "STATUS_RESET_ASSERT";
+ break;
+
+ case SPINEL_STATUS_RESET_OTHER:
+ ret = "STATUS_RESET_OTHER";
+ break;
+
+ case SPINEL_STATUS_RESET_UNKNOWN:
+ ret = "STATUS_RESET_UNKNOWN";
+ break;
+
+ case SPINEL_STATUS_RESET_WATCHDOG:
+ ret = "STATUS_RESET_WATCHDOG";
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+const char *spinel_capability_to_cstr(unsigned int capability)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (capability)
+ {
+ case SPINEL_CAP_LOCK:
+ ret = "CAP_LOCK";
+ break;
+
+ case SPINEL_CAP_NET_SAVE:
+ ret = "CAP_NET_SAVE";
+ break;
+
+ case SPINEL_CAP_HBO:
+ ret = "CAP_HBO";
+ break;
+
+ case SPINEL_CAP_POWER_SAVE:
+ ret = "CAP_POWER_SAVE";
+ break;
+
+ case SPINEL_CAP_COUNTERS:
+ ret = "CAP_COUNTERS";
+ break;
+
+ case SPINEL_CAP_JAM_DETECT:
+ ret = "CAP_JAM_DETECT";
+ break;
+
+ case SPINEL_CAP_PEEK_POKE:
+ ret = "CAP_PEEK_POKE";
+ break;
+
+ case SPINEL_CAP_WRITABLE_RAW_STREAM:
+ ret = "CAP_WRITABLE_RAW_STREAM";
+ break;
+
+ case SPINEL_CAP_GPIO:
+ ret = "CAP_GPIO";
+ break;
+
+ case SPINEL_CAP_TRNG:
+ ret = "CAP_TRNG";
+ break;
+
+ case SPINEL_CAP_CMD_MULTI:
+ ret = "CAP_CMD_MULTI";
+ break;
+
+ case SPINEL_CAP_802_15_4_2003:
+ ret = "CAP_802_15_4_2003";
+ break;
+
+ case SPINEL_CAP_802_15_4_2006:
+ ret = "CAP_802_15_4_2006";
+ break;
+
+ case SPINEL_CAP_802_15_4_2011:
+ ret = "CAP_802_15_4_2011";
+ break;
+
+ case SPINEL_CAP_802_15_4_PIB:
+ ret = "CAP_802_15_4_PIB";
+ break;
+
+ case SPINEL_CAP_802_15_4_2450MHZ_OQPSK:
+ ret = "CAP_802_15_4_2450MHZ_OQPSK";
+ break;
+
+ case SPINEL_CAP_802_15_4_915MHZ_OQPSK:
+ ret = "CAP_802_15_4_915MHZ_OQPSK";
+ break;
+
+ case SPINEL_CAP_802_15_4_868MHZ_OQPSK:
+ ret = "CAP_802_15_4_868MHZ_OQPSK";
+ break;
+
+ case SPINEL_CAP_802_15_4_915MHZ_BPSK:
+ ret = "CAP_802_15_4_915MHZ_BPSK";
+ break;
+
+ case SPINEL_CAP_802_15_4_868MHZ_BPSK:
+ ret = "CAP_802_15_4_868MHZ_BPSK";
+ break;
+
+ case SPINEL_CAP_802_15_4_915MHZ_ASK:
+ ret = "CAP_802_15_4_915MHZ_ASK";
+ break;
+
+ case SPINEL_CAP_802_15_4_868MHZ_ASK:
+ ret = "CAP_802_15_4_868MHZ_ASK";
+ break;
+
+ case SPINEL_CAP_ROLE_ROUTER:
+ ret = "CAP_ROLE_ROUTER";
+ break;
+
+ case SPINEL_CAP_ROLE_SLEEPY:
+ ret = "CAP_ROLE_SLEEPY";
+ break;
+
+ case SPINEL_CAP_NET_THREAD_1_0:
+ ret = "CAP_NET_THREAD_1_0";
+ break;
+
+ case SPINEL_CAP_MAC_WHITELIST:
+ ret = "CAP_MAC_WHITELIST";
+ break;
+
+ case SPINEL_CAP_MAC_RAW:
+ ret = "CAP_MAC_RAW";
+ break;
+
+ case SPINEL_CAP_OOB_STEERING_DATA:
+ ret = "CAP_OOB_STEERING_DATA";
+ break;
+
+ case SPINEL_CAP_THREAD_COMMISSIONER:
+ ret = "CAP_THREAD_COMMISSIONER";
+ break;
+
+ case SPINEL_CAP_THREAD_BA_PROXY:
+ ret = "CAP_THREAD_BA_PROXY";
+ break;
+
+ case SPINEL_CAP_NEST_LEGACY_INTERFACE:
+ ret = "CAP_NEST_LEGACY_INTERFACE";
+ break;
+
+ case SPINEL_CAP_NEST_LEGACY_NET_WAKE:
+ ret = "CAP_NEST_LEGACY_NET_WAKE";
+ break;
+
+ case SPINEL_CAP_NEST_TRANSMIT_HOOK:
+ ret = "CAP_NEST_TRANSMIT_HOOK";
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+// **** LCOV_EXCL_STOP ****
+
+/* -------------------------------------------------------------------------- */
+
+#if SPINEL_SELF_TEST
+
+#include <stdlib.h>
+#include <string.h>
+
+
+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", (unsigned int)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", (unsigned int)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/openthread/src/ncp/spinel.h b/openthread/src/ncp/spinel.h
new file mode 100644
index 0000000..05b5577
--- /dev/null
+++ b/openthread/src/ncp/spinel.h
@@ -0,0 +1,1379 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * 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 <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+// ----------------------------------------------------------------------------
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+# if defined(__GNUC__)
+# 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__
+
+# if !defined(__BEGIN_DECLS) || !defined(__END_DECLS)
+# if defined(__cplusplus)
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+# else // if defined(__cplusplus)
+# define __BEGIN_DECLS
+# define __END_DECLS
+# endif // else defined(__cplusplus)
+# endif // if !defined(__BEGIN_DECLS) || !defined(__END_DECLS)
+
+#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 4
+#define SPINEL_PROTOCOL_VERSION_THREAD_MINOR 3
+
+#define SPINEL_FRAME_MAX_SIZE 1300
+
+/// Macro for generating bit masks using bit index from the spec
+#define SPINEL_BIT_MASK(bit_index,field_bit_count) \
+ ( (1 << ((field_bit_count) - 1)) >> (bit_index) )
+
+// ----------------------------------------------------------------------------
+
+__BEGIN_DECLS
+
+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, ///< Given operation has not been implemented.
+ SPINEL_STATUS_INVALID_ARGUMENT = 3, ///< An argument to the operation is invalid.
+ SPINEL_STATUS_INVALID_STATE = 4, ///< This operation is invalid for the current device state.
+ SPINEL_STATUS_INVALID_COMMAND = 5, ///< This command is not recognized.
+ SPINEL_STATUS_INVALID_INTERFACE = 6, ///< This interface is not supported.
+ SPINEL_STATUS_INTERNAL_ERROR = 7, ///< An internal runtime error has occured.
+ SPINEL_STATUS_SECURITY_ERROR = 8, ///< A security/authentication error has occured.
+ SPINEL_STATUS_PARSE_ERROR = 9, ///< A error has occured while parsing the command.
+ SPINEL_STATUS_IN_PROGRESS = 10, ///< This operation is in progress.
+ SPINEL_STATUS_NOMEM = 11, ///< Operation prevented due to memory pressure.
+ SPINEL_STATUS_BUSY = 12, ///< The device is currently performing a mutually exclusive operation
+ SPINEL_STATUS_PROP_NOT_FOUND = 13, ///< The given property is not recognized.
+ SPINEL_STATUS_DROPPED = 14, ///< A/The packet was dropped.
+ SPINEL_STATUS_EMPTY = 15, ///< The result of the operation is empty.
+ SPINEL_STATUS_CMD_TOO_BIG = 16, ///< The command was too large to fit in the internal buffer.
+ SPINEL_STATUS_NO_ACK = 17, ///< The packet was not acknowledged.
+ SPINEL_STATUS_CCA_FAILURE = 18, ///< The packet was not sent due to a CCA failure.
+ SPINEL_STATUS_ALREADY = 19, ///< The operation is already in progress.
+ SPINEL_STATUS_ITEM_NOT_FOUND = 20, ///< The given item could not be found.
+ SPINEL_STATUS_INVALID_COMMAND_FOR_PROP
+ = 21, ///< The given command cannot be performed on this property.
+
+ SPINEL_STATUS_JOIN__BEGIN = 104,
+
+ /// Generic failure to associate with other peers.
+ /**
+ * This status error should not be used by implementors if
+ * enough information is available to determine that one of the
+ * later join failure status codes would be more accurate.
+ *
+ * \sa SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
+ */
+ SPINEL_STATUS_JOIN_FAILURE = SPINEL_STATUS_JOIN__BEGIN + 0,
+
+ /// The node found other peers but was unable to decode their packets.
+ /**
+ * Typically this error code indicates that the network
+ * key has been set incorrectly.
+ *
+ * \sa SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
+ */
+ SPINEL_STATUS_JOIN_SECURITY = SPINEL_STATUS_JOIN__BEGIN + 1,
+
+ /// The node was unable to find any other peers on the network.
+ /**
+ * \sa SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
+ */
+ SPINEL_STATUS_JOIN_NO_PEERS = SPINEL_STATUS_JOIN__BEGIN + 2,
+
+ /// The only potential peer nodes found are incompatible.
+ /**
+ * \sa SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
+ */
+ SPINEL_STATUS_JOIN_INCOMPATIBLE = SPINEL_STATUS_JOIN__BEGIN + 3,
+
+ SPINEL_STATUS_JOIN__END = 112,
+
+ 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_UNKNOWN = SPINEL_STATUS_RESET__BEGIN + 7,
+ SPINEL_STATUS_RESET_WATCHDOG = SPINEL_STATUS_RESET__BEGIN + 8,
+ SPINEL_STATUS_RESET__END = 128,
+
+ SPINEL_STATUS_VENDOR__BEGIN = 15360,
+ SPINEL_STATUS_VENDOR__END = 16384,
+
+ SPINEL_STATUS_STACK_NATIVE__BEGIN = 16384,
+ SPINEL_STATUS_STACK_NATIVE__END = 81920,
+
+ SPINEL_STATUS_EXPERIMENTAL__BEGIN = 2000000,
+ SPINEL_STATUS_EXPERIMENTAL__END = 2097152,
+} spinel_status_t;
+
+typedef enum
+{
+ SPINEL_NET_ROLE_DETACHED = 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_DISCOVER = 3,
+} 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;
+
+typedef enum
+{
+ SPINEL_HOST_POWER_STATE_OFFLINE = 0,
+ SPINEL_HOST_POWER_STATE_DEEP_SLEEP = 1,
+ SPINEL_HOST_POWER_STATE_RESERVED = 2,
+ SPINEL_HOST_POWER_STATE_LOW_POWER = 3,
+ SPINEL_HOST_POWER_STATE_ONLINE = 4,
+} spinel_host_power_state_t;
+
+enum
+{
+ SPINEL_NET_FLAG_ON_MESH = (1 << 0),
+ SPINEL_NET_FLAG_DEFAULT_ROUTE = (1 << 1),
+ SPINEL_NET_FLAG_CONFIGURE = (1 << 2),
+ SPINEL_NET_FLAG_DHCP = (1 << 3),
+ SPINEL_NET_FLAG_SLAAC = (1 << 4),
+ SPINEL_NET_FLAG_PREFERRED = (1 << 5),
+
+ SPINEL_NET_FLAG_PREFERENCE_OFFSET = 6,
+ SPINEL_NET_FLAG_PREFERENCE_MASK = (3 << SPINEL_NET_FLAG_PREFERENCE_OFFSET),
+};
+
+enum {
+ SPINEL_GPIO_FLAG_DIR_INPUT = 0,
+ SPINEL_GPIO_FLAG_DIR_OUTPUT = SPINEL_BIT_MASK(0, 8),
+ SPINEL_GPIO_FLAG_PULL_UP = SPINEL_BIT_MASK(1, 8),
+ SPINEL_GPIO_FLAG_PULL_DOWN = SPINEL_BIT_MASK(2, 8),
+ SPINEL_GPIO_FLAG_OPEN_DRAIN = SPINEL_BIT_MASK(2, 8),
+ SPINEL_GPIO_FLAG_TRIGGER_NONE = 0,
+ SPINEL_GPIO_FLAG_TRIGGER_RISING = SPINEL_BIT_MASK(3, 8),
+ SPINEL_GPIO_FLAG_TRIGGER_FALLING = SPINEL_BIT_MASK(4, 8),
+ SPINEL_GPIO_FLAG_TRIGGER_ANY = SPINEL_GPIO_FLAG_TRIGGER_RISING
+ | SPINEL_GPIO_FLAG_TRIGGER_FALLING,
+};
+
+enum
+{
+ SPINEL_PROTOCOL_TYPE_BOOTLOADER = 0,
+ SPINEL_PROTOCOL_TYPE_ZIGBEE_IP = 2,
+ SPINEL_PROTOCOL_TYPE_THREAD = 3,
+};
+
+enum
+{
+ SPINEL_MAC_PROMISCUOUS_MODE_OFF = 0, ///< Normal MAC filtering is in place.
+ SPINEL_MAC_PROMISCUOUS_MODE_NETWORK = 1, ///< All MAC packets matching network are passed up the stack.
+ SPINEL_MAC_PROMISCUOUS_MODE_FULL = 2, ///< All decoded MAC packets are passed up the stack.
+};
+
+enum
+{
+ SPINEL_NCP_LOG_LEVEL_EMERG = 0,
+ SPINEL_NCP_LOG_LEVEL_ALERT = 1,
+ SPINEL_NCP_LOG_LEVEL_CRIT = 2,
+ SPINEL_NCP_LOG_LEVEL_ERR = 3,
+ SPINEL_NCP_LOG_LEVEL_WARN = 4,
+ SPINEL_NCP_LOG_LEVEL_NOTICE = 5,
+ SPINEL_NCP_LOG_LEVEL_INFO = 6,
+ SPINEL_NCP_LOG_LEVEL_DEBUG = 7,
+};
+
+typedef struct
+{
+ uint8_t bytes[8];
+} spinel_eui64_t;
+
+typedef struct
+{
+ uint8_t bytes[8];
+} spinel_net_xpanid_t;
+
+typedef struct
+{
+ uint8_t bytes[16];
+} spinel_net_pskc_t;
+
+typedef struct
+{
+ uint8_t bytes[6];
+} spinel_eui48_t;
+
+typedef struct
+{
+ uint8_t bytes[16];
+} spinel_ipv6addr_t;
+
+typedef int spinel_ssize_t;
+typedef unsigned int spinel_size_t;
+typedef uint8_t spinel_tid_t;
+typedef unsigned int spinel_cid_t;
+
+enum
+{
+ SPINEL_MD_FLAG_TX = 0x0001, //!< Packet was transmitted, not received.
+ SPINEL_MD_FLAG_BAD_FCS = 0x0004, //!< Packet was received with bad FCS
+ SPINEL_MD_FLAG_DUPE = 0x0008, //!< Packet seems to be a duplicate
+ SPINEL_MD_FLAG_RESERVED = 0xFFF2, //!< Flags reserved for future use.
+};
+
+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_PEEK = 18,
+ SPINEL_CMD_PEEK_RET = 19,
+ SPINEL_CMD_POKE = 20,
+
+ SPINEL_CMD_PROP_VALUE_MULTI_GET = 21,
+ SPINEL_CMD_PROP_VALUE_MULTI_SET = 22,
+ SPINEL_CMD_PROP_VALUES_ARE = 23,
+
+ 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_COUNTERS = 5,
+ SPINEL_CAP_JAM_DETECT = 6,
+
+ SPINEL_CAP_PEEK_POKE = 7,
+
+ SPINEL_CAP_WRITABLE_RAW_STREAM = 8,
+ SPINEL_CAP_GPIO = 9,
+ SPINEL_CAP_TRNG = 10,
+ SPINEL_CAP_CMD_MULTI = 11,
+
+ 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_OPENTHREAD__BEGIN = 512,
+ SPINEL_CAP_MAC_WHITELIST = (SPINEL_CAP_OPENTHREAD__BEGIN + 0),
+ SPINEL_CAP_MAC_RAW = (SPINEL_CAP_OPENTHREAD__BEGIN + 1),
+ SPINEL_CAP_OOB_STEERING_DATA = (SPINEL_CAP_OPENTHREAD__BEGIN + 2),
+ SPINEL_CAP_OPENTHREAD__END = 640,
+
+ SPINEL_CAP_THREAD__BEGIN = 1024,
+ SPINEL_CAP_THREAD_COMMISSIONER = (SPINEL_CAP_THREAD__BEGIN + 0),
+ SPINEL_CAP_THREAD_BA_PROXY = (SPINEL_CAP_THREAD__BEGIN + 1),
+ SPINEL_CAP_THREAD__END = 1152,
+
+ 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, ///< major, minor [i,i]
+ SPINEL_PROP_NCP_VERSION = 2, ///< version string [U]
+ SPINEL_PROP_INTERFACE_TYPE = 3, ///< [i]
+ SPINEL_PROP_VENDOR_ID = 4, ///< [i]
+ SPINEL_PROP_CAPS = 5, ///< capability list [A(i)]
+ SPINEL_PROP_INTERFACE_COUNT = 6, ///< Interface count [C]
+ SPINEL_PROP_POWER_STATE = 7, ///< PowerState [C]
+ SPINEL_PROP_HWADDR = 8, ///< PermEUI64 [E]
+ SPINEL_PROP_LOCK = 9, ///< PropLock [b]
+ SPINEL_PROP_HBO_MEM_MAX = 10, ///< Max offload mem [S]
+ SPINEL_PROP_HBO_BLOCK_MAX = 11, ///< Max offload block [S]
+ SPINEL_PROP_HOST_POWER_STATE = 12, ///< Host MCU power state [C]
+
+ SPINEL_PROP_BASE_EXT__BEGIN = 0x1000,
+
+ /// GPIO Configuration
+ /** Format: `A(CCU)`
+ * Type: Read-Only (Optionally Read-write using `CMD_PROP_VALUE_INSERT`)
+ *
+ * An array of structures which contain the following fields:
+ *
+ * * `C`: GPIO Number
+ * * `C`: GPIO Configuration Flags
+ * * `U`: Human-readable GPIO name
+ *
+ * GPIOs which do not have a corresponding entry are not supported.
+ *
+ * The configuration parameter contains the configuration flags for the
+ * GPIO:
+ *
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * |DIR|PUP|PDN|TRIGGER| RESERVED |
+ * +---+---+---+---+---+---+---+---+
+ * |O/D|
+ * +---+
+ *
+ * * `DIR`: Pin direction. Clear (0) for input, set (1) for output.
+ * * `PUP`: Pull-up enabled flag.
+ * * `PDN`/`O/D`: Flag meaning depends on pin direction:
+ * * Input: Pull-down enabled.
+ * * Output: Output is an open-drain.
+ * * `TRIGGER`: Enumeration describing how pin changes generate
+ * asynchronous notification commands (TBD) from the NCP to the host.
+ * * 0: Feature disabled for this pin
+ * * 1: Trigger on falling edge
+ * * 2: Trigger on rising edge
+ * * 3: Trigger on level change
+ * * `RESERVED`: Bits reserved for future use. Always cleared to zero
+ * and ignored when read.
+ *
+ * As an optional feature, the configuration of individual pins may be
+ * modified using the `CMD_PROP_VALUE_INSERT` command. Only the GPIO
+ * number and flags fields MUST be present, the GPIO name (if present)
+ * would be ignored. This command can only be used to modify the
+ * configuration of GPIOs which are already exposed---it cannot be used
+ * by the host to add additional GPIOs.
+ */
+ SPINEL_PROP_GPIO_CONFIG = SPINEL_PROP_BASE_EXT__BEGIN + 0,
+
+ /// GPIO State Bitmask
+ /** Format: `D`
+ * Type: Read-Write
+ *
+ * Contains a bit field identifying the state of the GPIOs. The length of
+ * the data associated with these properties depends on the number of
+ * GPIOs. If you have 10 GPIOs, you'd have two bytes. GPIOs are numbered
+ * from most significant bit to least significant bit, so 0x80 is GPIO 0,
+ * 0x40 is GPIO 1, etc.
+ *
+ * For GPIOs configured as inputs:
+ *
+ * * `CMD_PROP_VAUE_GET`: The value of the associated bit describes the
+ * logic level read from the pin.
+ * * `CMD_PROP_VALUE_SET`: The value of the associated bit is ignored
+ * for these pins.
+ *
+ * For GPIOs configured as outputs:
+ *
+ * * `CMD_PROP_VAUE_GET`: The value of the associated bit is
+ * implementation specific.
+ * * `CMD_PROP_VALUE_SET`: The value of the associated bit determines
+ * the new logic level of the output. If this pin is configured as an
+ * open-drain, setting the associated bit to 1 will cause the pin to
+ * enter a Hi-Z state.
+ *
+ * For GPIOs which are not specified in `PROP_GPIO_CONFIG`:
+ *
+ * * `CMD_PROP_VAUE_GET`: The value of the associated bit is
+ * implementation specific.
+ * * `CMD_PROP_VALUE_SET`: The value of the associated bit MUST be
+ * ignored by the NCP.
+ *
+ * When writing, unspecified bits are assumed to be zero.
+ */
+ SPINEL_PROP_GPIO_STATE = SPINEL_PROP_BASE_EXT__BEGIN + 2,
+
+ /// GPIO State Set-Only Bitmask
+ /** Format: `D`
+ * Type: Write-Only
+ *
+ * Allows for the state of various output GPIOs to be set without affecting
+ * other GPIO states. Contains a bit field identifying the output GPIOs that
+ * should have their state set to 1.
+ *
+ * When writing, unspecified bits are assumed to be zero. The value of
+ * any bits for GPIOs which are not specified in `PROP_GPIO_CONFIG` MUST
+ * be ignored.
+ */
+ SPINEL_PROP_GPIO_STATE_SET = SPINEL_PROP_BASE_EXT__BEGIN + 3,
+
+ /// GPIO State Clear-Only Bitmask
+ /** Format: `D`
+ * Type: Write-Only
+ *
+ * Allows for the state of various output GPIOs to be cleared without affecting
+ * other GPIO states. Contains a bit field identifying the output GPIOs that
+ * should have their state cleared to 0.
+ *
+ * When writing, unspecified bits are assumed to be zero. The value of
+ * any bits for GPIOs which are not specified in `PROP_GPIO_CONFIG` MUST
+ * be ignored.
+ */
+ SPINEL_PROP_GPIO_STATE_CLEAR = SPINEL_PROP_BASE_EXT__BEGIN + 4,
+
+ /// 32-bit random number from TRNG, ready-to-use.
+ SPINEL_PROP_TRNG_32 = SPINEL_PROP_BASE_EXT__BEGIN + 5,
+
+ /// 16 random bytes from TRNG, ready-to-use.
+ SPINEL_PROP_TRNG_128 = SPINEL_PROP_BASE_EXT__BEGIN + 6,
+
+ /// Raw samples from TRNG entropy source representing 32 bits of entropy.
+ SPINEL_PROP_TRNG_RAW_32 = SPINEL_PROP_BASE_EXT__BEGIN + 7,
+
+ SPINEL_PROP_BASE_EXT__END = 0x1100,
+
+ 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_RX_SENSITIVITY = SPINEL_PROP_PHY__BEGIN + 7, ///< dBm [c]
+ SPINEL_PROP_PHY__END = 0x30,
+
+ SPINEL_PROP_PHY_EXT__BEGIN = 0x1200,
+
+ /// Signal Jamming Detection Enable
+ /** Format: `b`
+ *
+ * Indicates if jamming detection is enabled or disabled. Set to true
+ * to enable jamming detection.
+ */
+ SPINEL_PROP_JAM_DETECT_ENABLE = SPINEL_PROP_PHY_EXT__BEGIN + 0,
+
+ /// Signal Jamming Detected Indicator
+ /** Format: `b` (Read-Only)
+ *
+ * Set to true if radio jamming is detected. Set to false otherwise.
+ *
+ * When jamming detection is enabled, changes to the value of this
+ * property are emitted asynchronously via `CMD_PROP_VALUE_IS`.
+ */
+ SPINEL_PROP_JAM_DETECTED = SPINEL_PROP_PHY_EXT__BEGIN + 1,
+
+ /// Jamming detection RSSI threshold
+ /** Format: `c`
+ * Units: dBm
+ *
+ * This parameter describes the threshold RSSI level (measured in
+ * dBm) above which the jamming detection will consider the
+ * channel blocked.
+ */
+ SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD
+ = SPINEL_PROP_PHY_EXT__BEGIN + 2,
+
+ /// Jamming detection window size
+ /** Format: `C`
+ * Units: Seconds (1-63)
+ *
+ * This parameter describes the window period for signal jamming
+ * detection.
+ */
+ SPINEL_PROP_JAM_DETECT_WINDOW = SPINEL_PROP_PHY_EXT__BEGIN + 3,
+
+ /// Jamming detection busy period
+ /** Format: `C`
+ * Units: Seconds (1-63)
+ *
+ * This parameter describes the number of aggregate seconds within
+ * the detection window where the RSSI must be above
+ * `PROP_JAM_DETECT_RSSI_THRESHOLD` to trigger detection.
+ *
+ * The behavior of the jamming detection feature when `PROP_JAM_DETECT_BUSY`
+ * is larger than `PROP_JAM_DETECT_WINDOW` is undefined.
+ */
+ SPINEL_PROP_JAM_DETECT_BUSY = SPINEL_PROP_PHY_EXT__BEGIN + 4,
+
+ /// Jamming detection history bitmap (for debugging)
+ /** Format: `LL` (read-only)
+ *
+ * This value provides information about current state of jamming detection
+ * module for monitoring/debugging purpose. It returns a 64-bit value where
+ * each bit corresponds to one second interval starting with bit 0 for the
+ * most recent interval and bit 63 for the oldest intervals (63 sec earlier).
+ * The bit is set to 1 if the jamming detection module observed/detected
+ * high signal level during the corresponding one second interval.
+ *
+ * The value is read-only and is encoded as two uint32 values in
+ * little-endian format (first uint32 gives the lower bits corresponding to
+ * more recent history).
+ */
+ SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP
+ = SPINEL_PROP_PHY_EXT__BEGIN + 5,
+
+ SPINEL_PROP_PHY_EXT__END = 0x1300,
+
+ 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,mac_data,net_data [CcdD]
+ 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_RAW_STREAM_ENABLED = SPINEL_PROP_MAC__BEGIN + 7, ///< [C]
+ SPINEL_PROP_MAC_PROMISCUOUS_MODE = SPINEL_PROP_MAC__BEGIN + 8, ///< [C]
+ SPINEL_PROP_MAC_ENERGY_SCAN_RESULT = SPINEL_PROP_MAC__BEGIN + 9, ///< chan,maxRssi [Cc]
+ SPINEL_PROP_MAC__END = 0x40,
+
+ SPINEL_PROP_MAC_EXT__BEGIN = 0x1300,
+ /// MAC Whitelist
+ /** Format: `A(t(Ec))`
+ *
+ * Structure Parameters:
+ *
+ * * `E`: EUI64 address of node
+ * * `c`: Optional fixed RSSI. 127 means not set.
+ */
+ SPINEL_PROP_MAC_WHITELIST = SPINEL_PROP_MAC_EXT__BEGIN + 0,
+
+ /// MAC Whitelist Enabled Flag
+ /** Format: `b`
+ */
+ SPINEL_PROP_MAC_WHITELIST_ENABLED = SPINEL_PROP_MAC_EXT__BEGIN + 1,
+
+ /// MAC Extended Address
+ /** Format: `E`
+ *
+ * Specified by Thread. Randomly-chosen, but non-volatile EUI-64.
+ */
+ SPINEL_PROP_MAC_EXTENDED_ADDR = SPINEL_PROP_MAC_EXT__BEGIN + 2,
+
+ /// MAC Source Match Enabled Flag
+ /** Format: `b`
+ */
+ SPINEL_PROP_MAC_SRC_MATCH_ENABLED = SPINEL_PROP_MAC_EXT__BEGIN + 3,
+
+ /// MAC Source Match Short Address List
+ /** Format: `A(S)`
+ */
+ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES
+ = SPINEL_PROP_MAC_EXT__BEGIN + 4,
+
+ /// MAC Source Match Extended Address List
+ /** Format: `A(E)`
+ */
+ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES
+ = SPINEL_PROP_MAC_EXT__BEGIN + 5,
+ SPINEL_PROP_MAC_EXT__END = 0x1400,
+
+ SPINEL_PROP_NET__BEGIN = 0x40,
+ SPINEL_PROP_NET_SAVED = SPINEL_PROP_NET__BEGIN + 0, ///< [b]
+ SPINEL_PROP_NET_IF_UP = SPINEL_PROP_NET__BEGIN + 1, ///< [b]
+ SPINEL_PROP_NET_STACK_UP = SPINEL_PROP_NET__BEGIN + 2, ///< [b]
+ 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_COUNTER
+ = SPINEL_PROP_NET__BEGIN + 7, ///< [L]
+ SPINEL_PROP_NET_PARTITION_ID = SPINEL_PROP_NET__BEGIN + 8, ///< [L]
+
+ /// Require Join Existing
+ /** Format: `b`
+ * Default Value: `false`
+ *
+ * This flag is typically used for nodes that are associating with an
+ * existing network for the first time. If this is set to `true` before
+ * `PROP_NET_STACK_UP` is set to `true`, the
+ * creation of a new partition at association is prevented. If the node
+ * cannot associate with an existing partition, `PROP_LAST_STATUS` will
+ * emit a status that indicates why the association failed and
+ * `PROP_NET_STACK_UP` will automatically revert to `false`.
+ *
+ * Once associated with an existing partition, this flag automatically
+ * reverts to `false`.
+ *
+ * The behavior of this property being set to `true` when
+ * `PROP_NET_STACK_UP` is already set to `true` is undefined.
+ */
+ SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
+ = SPINEL_PROP_NET__BEGIN + 9,
+
+ SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME
+ = SPINEL_PROP_NET__BEGIN + 10, ///< [L]
+
+ SPINEL_PROP_NET_PSKC = SPINEL_PROP_NET__BEGIN + 11, ///< [D]
+
+ SPINEL_PROP_NET__END = 0x50,
+
+ SPINEL_PROP_THREAD__BEGIN = 0x50,
+ SPINEL_PROP_THREAD_LEADER_ADDR = 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, ///< array(EUI64,rloc16,timeout,age,netDataVer,inLqi,aveRSS,mode) [A(t(ESLLCCcC))]
+ SPINEL_PROP_THREAD_LEADER_RID = SPINEL_PROP_THREAD__BEGIN + 3, ///< [C]
+ SPINEL_PROP_THREAD_LEADER_WEIGHT = SPINEL_PROP_THREAD__BEGIN + 4, ///< [C]
+ SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT
+ = SPINEL_PROP_THREAD__BEGIN + 5, ///< [C]
+ SPINEL_PROP_THREAD_NETWORK_DATA = SPINEL_PROP_THREAD__BEGIN + 6, ///< [D]
+ SPINEL_PROP_THREAD_NETWORK_DATA_VERSION
+ = SPINEL_PROP_THREAD__BEGIN + 7, ///< [S]
+ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA
+ = SPINEL_PROP_THREAD__BEGIN + 8, ///< [D]
+ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION
+ = SPINEL_PROP_THREAD__BEGIN + 9, ///< [S]
+ SPINEL_PROP_THREAD_ON_MESH_NETS = SPINEL_PROP_THREAD__BEGIN + 10, ///< array(ipv6prefix,prefixlen,stable,flags,isLocal) [A(t(6CbCb))]
+ SPINEL_PROP_THREAD_OFF_MESH_ROUTES = SPINEL_PROP_THREAD__BEGIN + 11, ///< array(ipv6prefix,prefixlen,stable,flags,isLocal) [A(t(6CbCb))]
+ SPINEL_PROP_THREAD_ASSISTING_PORTS = SPINEL_PROP_THREAD__BEGIN + 12, ///< array(portn) [A(S)]
+ SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE
+ = SPINEL_PROP_THREAD__BEGIN + 13, ///< [b]
+
+ /// Thread Mode
+ /** Format: `C`
+ *
+ * This property contains the value of the mode
+ * TLV for this node. The meaning of the bits in this
+ * bitfield are defined by section 4.5.2 of the Thread
+ * specification.
+ */
+ SPINEL_PROP_THREAD_MODE = SPINEL_PROP_THREAD__BEGIN + 14,
+ SPINEL_PROP_THREAD__END = 0x60,
+
+ SPINEL_PROP_THREAD_EXT__BEGIN = 0x1500,
+
+ /// Thread Child Timeout
+ /** Format: `L`
+ *
+ * Used when operating in the Child role.
+ */
+ SPINEL_PROP_THREAD_CHILD_TIMEOUT = SPINEL_PROP_THREAD_EXT__BEGIN + 0,
+
+ /// Thread RLOC16
+ /** Format: `S`
+ */
+ SPINEL_PROP_THREAD_RLOC16 = SPINEL_PROP_THREAD_EXT__BEGIN + 1,
+
+ /// Thread Router Upgrade Threshold
+ /** Format: `C`
+ */
+ SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 2,
+
+ /// Thread Context Reuse Delay
+ /** Format: `L`
+ */
+ SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 3,
+
+ /// Thread Network ID Timeout
+ /** Format: `C`
+ */
+ SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 4,
+
+ /// List of active thread router ids
+ /** Format: `A(C)`
+ *
+ * Note that some implementations may not support CMD_GET_VALUE
+ * routerids, but may support CMD_REMOVE_VALUE when the node is
+ * a leader.
+ */
+ SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 5,
+
+ /// Forward IPv6 packets that use RLOC16 addresses to HOST.
+ /** Format: `b`
+ */
+ SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 6,
+
+ /// This property indicates whether or not the `Router Role` is enabled.
+ /** Format `b`
+ */
+ SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 7,
+
+ /// Thread Router Downgrade Threshold
+ /** Format: `C`
+ */
+ SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 8,
+
+ /// Thread Router Selection Jitter
+ /** Format: `C`
+ */
+ SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 9,
+
+ /// Thread Preferred Router Id
+ /** Format: `C` - Write only
+ */
+ SPINEL_PROP_THREAD_PREFERRED_ROUTER_ID
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 10,
+
+ /// Thread Neighbor Table
+ /** Format: `A(t(ESLCcCbLL))`
+ * eui64, rloc16, age, inLqi ,aveRSS, mode, isChild. linkFrameCounter, mleCounter
+ */
+ SPINEL_PROP_THREAD_NEIGHBOR_TABLE = SPINEL_PROP_THREAD_EXT__BEGIN + 11,
+
+ /// Thread Max Child Count
+ /** Format: `C`
+ */
+ SPINEL_PROP_THREAD_CHILD_COUNT_MAX = SPINEL_PROP_THREAD_EXT__BEGIN + 12,
+
+ /// Leader network data
+ /** Format: `D` - Read only
+ */
+ SPINEL_PROP_THREAD_LEADER_NETWORK_DATA
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 13,
+
+ /// Stable leader network data
+ /** Format: `D` - Read only
+ */
+ SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 14,
+
+ /// Thread joiner data
+ /** Format `A(T(ULE))`
+ * PSKd, joiner timeout, eui64 (optional)
+ */
+ SPINEL_PROP_THREAD_JOINERS = SPINEL_PROP_THREAD_EXT__BEGIN + 15,
+
+ /// Thread commissioner enable
+ /** Format `b`
+ *
+ * Default value is `false`.
+ */
+ SPINEL_PROP_THREAD_COMMISSIONER_ENABLED
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 16,
+
+ /// Thread border agent proxy enable
+ /** Format `b`
+ *
+ * Default value is `false`.
+ */
+ SPINEL_PROP_THREAD_BA_PROXY_ENABLED = SPINEL_PROP_THREAD_EXT__BEGIN + 17,
+
+ /// Thread border agent proxy stream
+ /** Format `dSS`
+ */
+ SPINEL_PROP_THREAD_BA_PROXY_STREAM = SPINEL_PROP_THREAD_EXT__BEGIN + 18,
+
+ /// Thread "joiner" flag used during discovery scan operation
+ /** Format `b`
+ *
+ * This property defines the Joiner Flag value in the Discovery Request TLV.
+ *
+ * Default value is `false`.
+ */
+ SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 19,
+
+ /// Enable EUI64 filtering for discovery scan operation.
+ /** Format `b`
+ *
+ * Default value is `false`
+ */
+ SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 20,
+
+ /// PANID used for Discovery scan operation (used for PANID filtering).
+ /** Format: `S`
+ *
+ * Default value is 0xffff (Broadcast PAN) to disable PANID filtering
+ *
+ */
+ SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID
+ = SPINEL_PROP_THREAD_EXT__BEGIN + 21,
+
+ /// Thread (out of band) steering data for MLE Discovery Response.
+ /** Format `E` - Write only
+ *
+ * Required capability: SPINEL_CAP_OOB_STEERING_DATA.
+ *
+ * Writing to this property allows to set/update the the MLE Discovery Response steering data out of band.
+ *
+ * - All zeros to clear the steering data (indicating that there is no steering data).
+ * - All 0xFFs to set steering data/bloom filter to accept/allow all.
+ * - A specific EUI64 which is then added to current steering data/bloom filter.
+ *
+ */
+ SPINEL_PROP_THREAD_STEERING_DATA = SPINEL_PROP_THREAD_EXT__BEGIN + 22,
+
+ SPINEL_PROP_THREAD_EXT__END = 0x1600,
+
+ 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,valid,preferred,flags) [A(t(6CLLC))]
+ SPINEL_PROP_IPV6_ROUTE_TABLE = SPINEL_PROP_IPV6__BEGIN + 4, ///< array(ipv6prefix,prefixlen,iface,flags) [A(t(6CCC))]
+
+ /// IPv6 ICMP Ping Offload
+ /** Format: `b`
+ *
+ * Allow the NCP to directly respond to ICMP ping requests. If this is
+ * turned on, ping request ICMP packets will not be passed to the host.
+ *
+ * Default value is `false`.
+ */
+ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD = SPINEL_PROP_IPV6__BEGIN + 5, ///< [b]
+
+ SPINEL_PROP_IPV6__END = 0x70,
+
+ SPINEL_PROP_STREAM__BEGIN = 0x70,
+ SPINEL_PROP_STREAM_DEBUG = SPINEL_PROP_STREAM__BEGIN + 0, ///< [U]
+ SPINEL_PROP_STREAM_RAW = SPINEL_PROP_STREAM__BEGIN + 1, ///< [dD]
+ SPINEL_PROP_STREAM_NET = SPINEL_PROP_STREAM__BEGIN + 2, ///< [dD]
+ SPINEL_PROP_STREAM_NET_INSECURE = SPINEL_PROP_STREAM__BEGIN + 3, ///< [dD]
+ SPINEL_PROP_STREAM__END = 0x80,
+
+ /// UART Bitrate
+ /** Format: `L`
+ *
+ * If the NCP is using a UART to communicate with the host,
+ * this property allows the host to change the bitrate
+ * of the serial connection. The value encoding is `L`,
+ * which is a little-endian 32-bit unsigned integer.
+ * The host should not assume that all possible numeric values
+ * are supported.
+ *
+ * If implemented by the NCP, this property should be persistent
+ * across software resets and forgotten upon hardware resets.
+ *
+ * This property is only implemented when a UART is being
+ * used for Spinel. This property is optional.
+ *
+ * When changing the bitrate, all frames will be received
+ * at the previous bitrate until the response frame to this command
+ * is received. Once a successful response frame is received by
+ * the host, all further frames will be transmitted at the new
+ * bitrate.
+ */
+ SPINEL_PROP_UART_BITRATE = 0x100,
+
+ /// UART Software Flow Control
+ /** Format: `b`
+ *
+ * If the NCP is using a UART to communicate with the host,
+ * this property allows the host to determine if software flow
+ * control (XON/XOFF style) should be used and (optionally) to
+ * turn it on or off.
+ *
+ * This property is only implemented when a UART is being
+ * used for Spinel. This property is optional.
+ */
+ SPINEL_PROP_UART_XON_XOFF = 0x101,
+
+ 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.
+ //
+ // For brevity, the entire 802.15.4 PIB space is
+ // not defined here, but a few choice attributes
+ // are defined for illustration and convenience.
+ SPINEL_PROP_15_4_PIB_PHY_CHANNELS_SUPPORTED
+ = SPINEL_PROP_15_4_PIB__BEGIN + 0x01, ///< [A(L)]
+ SPINEL_PROP_15_4_PIB_MAC_PROMISCUOUS_MODE
+ = SPINEL_PROP_15_4_PIB__BEGIN + 0x51, ///< [b]
+ SPINEL_PROP_15_4_PIB_MAC_SECURITY_ENABLED
+ = SPINEL_PROP_15_4_PIB__BEGIN + 0x5d, ///< [b]
+ SPINEL_PROP_15_4_PIB__END = 1280,
+
+ SPINEL_PROP_CNTR__BEGIN = 1280,
+
+ /// Counter reset behavior
+ /** Format: `C`
+ * Writing a '1' to this property will reset
+ * all of the counters to zero. */
+ SPINEL_PROP_CNTR_RESET = SPINEL_PROP_CNTR__BEGIN + 0,
+
+ /// The total number of transmissions.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_TOTAL = SPINEL_PROP_CNTR__BEGIN + 1,
+
+ /// The number of transmissions with ack request.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_ACK_REQ = SPINEL_PROP_CNTR__BEGIN + 2,
+
+ /// The number of transmissions that were acked.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_ACKED = SPINEL_PROP_CNTR__BEGIN + 3,
+
+ /// The number of transmissions without ack request.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ = SPINEL_PROP_CNTR__BEGIN + 4,
+
+ /// The number of transmitted data.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_DATA = SPINEL_PROP_CNTR__BEGIN + 5,
+
+ /// The number of transmitted data poll.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_DATA_POLL = SPINEL_PROP_CNTR__BEGIN + 6,
+
+ /// The number of transmitted beacon.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_BEACON = SPINEL_PROP_CNTR__BEGIN + 7,
+
+ /// The number of transmitted beacon request.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ = SPINEL_PROP_CNTR__BEGIN + 8,
+
+ /// The number of transmitted other types of frames.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_OTHER = SPINEL_PROP_CNTR__BEGIN + 9,
+
+ /// The number of retransmission times.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_RETRY = SPINEL_PROP_CNTR__BEGIN + 10,
+
+ /// The number of CCA failure times.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_ERR_CCA = SPINEL_PROP_CNTR__BEGIN + 11,
+
+ /// The number of unicast packets transmitted.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_UNICAST = SPINEL_PROP_CNTR__BEGIN + 12,
+
+ /// The number of broadcast packets transmitted.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_PKT_BROADCAST = SPINEL_PROP_CNTR__BEGIN + 13,
+
+ /// The number of frame transmission failures due to abort error.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_ERR_ABORT = SPINEL_PROP_CNTR__BEGIN + 14,
+
+ /// The total number of received packets.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_TOTAL = SPINEL_PROP_CNTR__BEGIN + 100,
+
+ /// The number of received data.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_DATA = SPINEL_PROP_CNTR__BEGIN + 101,
+
+ /// The number of received data poll.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_DATA_POLL = SPINEL_PROP_CNTR__BEGIN + 102,
+
+ /// The number of received beacon.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_BEACON = SPINEL_PROP_CNTR__BEGIN + 103,
+
+ /// The number of received beacon request.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ = SPINEL_PROP_CNTR__BEGIN + 104,
+
+ /// The number of received other types of frames.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_OTHER = SPINEL_PROP_CNTR__BEGIN + 105,
+
+ /// The number of received packets filtered by whitelist.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_FILT_WL = SPINEL_PROP_CNTR__BEGIN + 106,
+
+ /// The number of received packets filtered by destination check.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_FILT_DA = SPINEL_PROP_CNTR__BEGIN + 107,
+
+ /// The number of received packets that are empty.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_EMPTY = SPINEL_PROP_CNTR__BEGIN + 108,
+
+ /// The number of received packets from an unknown neighbor.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR = SPINEL_PROP_CNTR__BEGIN + 109,
+
+ /// The number of received packets whose source address is invalid.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR = SPINEL_PROP_CNTR__BEGIN + 110,
+
+ /// The number of received packets with a security error.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_SECURITY = SPINEL_PROP_CNTR__BEGIN + 111,
+
+ /// The number of received packets with a checksum error.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_BAD_FCS = SPINEL_PROP_CNTR__BEGIN + 112,
+
+ /// The number of received packets with other errors.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_ERR_OTHER = SPINEL_PROP_CNTR__BEGIN + 113,
+
+ /// The number of received duplicated.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_DUP = SPINEL_PROP_CNTR__BEGIN + 114,
+
+ /// The number of unicast packets received.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_UNICAST = SPINEL_PROP_CNTR__BEGIN + 115,
+
+ /// The number of broadcast packets received.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_PKT_BROADCAST = SPINEL_PROP_CNTR__BEGIN + 116,
+
+ /// The total number of secure transmitted IP messages.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL = SPINEL_PROP_CNTR__BEGIN + 200,
+
+ /// The total number of insecure transmitted IP messages.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL = SPINEL_PROP_CNTR__BEGIN + 201,
+
+ /// The number of dropped (not transmitted) IP messages.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_IP_DROPPED = SPINEL_PROP_CNTR__BEGIN + 202,
+
+ /// The total number of secure received IP message.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL = SPINEL_PROP_CNTR__BEGIN + 203,
+
+ /// The total number of insecure received IP message.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL = SPINEL_PROP_CNTR__BEGIN + 204,
+
+ /// The number of dropped received IP messages.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_IP_DROPPED = SPINEL_PROP_CNTR__BEGIN + 205,
+
+ /// The number of transmitted spinel frames.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_TX_SPINEL_TOTAL = SPINEL_PROP_CNTR__BEGIN + 300,
+
+ /// The number of received spinel frames.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_SPINEL_TOTAL = SPINEL_PROP_CNTR__BEGIN + 301,
+
+ /// The number of received spinel frames with error.
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_SPINEL_ERR = SPINEL_PROP_CNTR__BEGIN + 302,
+
+ /// Number of out of order received spinel frames (tid increase by more than 1).
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID
+ = SPINEL_PROP_CNTR__BEGIN + 303,
+
+ /// The number of successful Tx IP packets
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_IP_TX_SUCCESS = SPINEL_PROP_CNTR__BEGIN + 304,
+
+ /// The number of successful Rx IP packets
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_IP_RX_SUCCESS = SPINEL_PROP_CNTR__BEGIN + 305,
+
+ /// The number of failed Tx IP packets
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_IP_TX_FAILURE = SPINEL_PROP_CNTR__BEGIN + 306,
+
+ /// The number of failed Rx IP packets
+ /** Format: `L` (Read-only) */
+ SPINEL_PROP_CNTR_IP_RX_FAILURE = SPINEL_PROP_CNTR__BEGIN + 307,
+
+ /// The message buffer counter info
+ /** Format: `SSSSSSSSSSSSSSSS` (Read-only)
+ * `S`, (TotalBuffers) The number of buffers in the pool.
+ * `S`, (FreeBuffers) The number of free message buffers.
+ * `S`, (6loSendMessages) The number of messages in the 6lo send queue.
+ * `S`, (6loSendBuffers) The number of buffers in the 6lo send queue.
+ * `S`, (6loReassemblyMessages) The number of messages in the 6LoWPAN reassembly queue.
+ * `S`, (6loReassemblyBuffers) The number of buffers in the 6LoWPAN reassembly queue.
+ * `S`, (Ip6Messages) The number of messages in the IPv6 send queue.
+ * `S`, (Ip6Buffers) The number of buffers in the IPv6 send queue.
+ * `S`, (MplMessages) The number of messages in the MPL send queue.
+ * `S`, (MplBuffers) The number of buffers in the MPL send queue.
+ * `S`, (MleMessages) The number of messages in the MLE send queue.
+ * `S`, (MleBuffers) The number of buffers in the MLE send queue.
+ * `S`, (ArpMessages) The number of messages in the ARP send queue.
+ * `S`, (ArpBuffers) The number of buffers in the ARP send queue.
+ * `S`, (CoapMessages) The number of messages in the CoAP send queue.
+ * `S`, (CoapBuffers) The number of buffers in the CoAP send queue.
+ */
+ SPINEL_PROP_MSG_BUFFER_COUNTERS = SPINEL_PROP_CNTR__BEGIN + 400,
+
+ SPINEL_PROP_CNTR__END = 2048,
+
+ SPINEL_PROP_NEST__BEGIN = 15296,
+ SPINEL_PROP_NEST_STREAM_MFG = SPINEL_PROP_NEST__BEGIN + 0,
+
+ /// The legacy network ULA prefix (8 bytes)
+ /** Format: 'D' */
+ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX = SPINEL_PROP_NEST__BEGIN + 1,
+
+ /// A (newly) joined legacy node (this is signaled from NCP)
+ /** Format: 'E' */
+ SPINEL_PROP_NEST_LEGACY_JOINED_NODE = SPINEL_PROP_NEST__BEGIN + 2,
+
+ SPINEL_PROP_NEST__END = 15360,
+
+ SPINEL_PROP_VENDOR__BEGIN = 15360,
+ SPINEL_PROP_VENDOR__END = 16384,
+
+ SPINEL_PROP_DEBUG__BEGIN = 16384,
+
+ /// Reading this property will cause an assert on the NCP.
+ /// This is intended for testing the assert functionality of
+ /// underlying platform/NCP. Assert should ideally cause the
+ /// NCP to reset, but if this is not supported a `false` boolean
+ /// is returned in response.
+ /** Format: 'b' (read-only) */
+ SPINEL_PROP_DEBUG_TEST_ASSERT = SPINEL_PROP_DEBUG__BEGIN + 0,
+
+ /// The NCP log level.
+ /** Format: `C` */
+ SPINEL_PROP_DEBUG_NCP_LOG_LEVEL = SPINEL_PROP_DEBUG__BEGIN + 1,
+
+ SPINEL_PROP_DEBUG__END = 17408,
+
+ 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_WLEN_C = 'd',
+ 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_WLEN_S "d"
+#define SPINEL_DATATYPE_DATA_S "D"
+#define SPINEL_DATATYPE_UTF8_S "U" //!< Zero-Terminated UTF8-Encoded String
+
+#define SPINEL_DATATYPE_ARRAY_S(x) "A(" x ")"
+#define SPINEL_DATATYPE_STRUCT_S(x) "t(" x ")"
+
+#define SPINEL_DATATYPE_ARRAY_STRUCT_S(x) \
+ SPINEL_DATATYPE_ARRAY_S(SPINEL_DATATYPE_STRUCT_WLEN_S(x))
+
+#define SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_UINT8_S /* header */ \
+ SPINEL_DATATYPE_UINT_PACKED_S /* command */
+
+#define SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_COMMAND_S /* prop command */ \
+ SPINEL_DATATYPE_UINT_PACKED_S /* property id */
+
+#define SPINEL_DATATYPE_MAC_SCAN_RESULT_S(mac_format_str, net_format_str) \
+ SPINEL_DATATYPE_UINT8_S /* Channel */ \
+ SPINEL_DATATYPE_INT8_S /* RSSI */ \
+ SPINEL_DATATYPE_STRUCT_S(mac_format_str) /* mac-layer data */ \
+ SPINEL_DATATYPE_STRUCT_S(net_format_str) /* net-layer data */
+
+#define SPINEL_802_15_4_DATATYPE_MAC_SCAN_RESULT_V1_S \
+ SPINEL_DATATYPE_EUI64_S /* laddr */ \
+ SPINEL_DATATYPE_UINT16_S /* saddr */ \
+ SPINEL_DATATYPE_UINT16_S /* panid */ \
+ SPINEL_DATATYPE_UINT8_S /* lqi */
+
+#define SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V1_S \
+ SPINEL_DATATYPE_UINT_PACKED_S /* type */ \
+ SPINEL_DATATYPE_UINT8_S /* flags */ \
+ SPINEL_DATATYPE_UTF8_S /* network name */ \
+ SPINEL_DATATYPE_DATA_WLEN_S /* xpanid */
+
+#define SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V2_S \
+ SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V1_S \
+ SPINEL_DATATYPE_DATA_WLEN_S /* steering data */
+
+
+#define SPINEL_MAX_UINT_PACKED 2097151
+
+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);
+
+// ----------------------------------------------------------------------------
+
+SPINEL_API_EXTERN const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key);
+
+SPINEL_API_EXTERN const char *spinel_net_role_to_cstr(uint8_t net_role);
+
+SPINEL_API_EXTERN const char *spinel_status_to_cstr(spinel_status_t status);
+
+SPINEL_API_EXTERN const char *spinel_capability_to_cstr(unsigned int capability);
+
+// ----------------------------------------------------------------------------
+
+__END_DECLS
+
+#endif /* defined(SPINEL_HEADER_INCLUDED) */
diff --git a/openthread/tools/spi-hdlc-adapter/README.md b/openthread/tools/spi-hdlc-adapter/README.md
new file mode 100644
index 0000000..104efe8
--- /dev/null
+++ b/openthread/tools/spi-hdlc-adapter/README.md
@@ -0,0 +1,90 @@
+SPI/HDLC Adaptor
+================
+
+`spi-hdlc-adapter` is an adapter tool for using a SPI interface as if
+it were an HDLC-lite encoded bidirectional asynchronous serial stream.
+It uses the SPI protocol outlined in [Appendix A.2][1] of the Spinel
+protocol document.
+
+[1]: https://goo.gl/gt18O4
+
+## Syntax ##
+
+ spi-hdlc-adapter [options] <spi-device-path>
+
+## Options ##
+
+* `--stdio`: Use `stdin` and `stdout` for HDLC input/output. Useful
+ when directly started by the program that will be using it.
+* `--pty`: Create a pseudo terminal for HDLC input/output. The path
+ of the newly-created PTY will be written to `stdout`, followed by
+ a newline.
+* `--raw`: Do not encode/decode packets using HDLC. Instead, write
+ whole, raw frames to the specified input and output FDs. This is
+ useful for emulating a serial port, or when datagram-based sockets
+ are supplied for `stdin` and `stdout` (when used with `--stdio`).
+* `--mtu=[MTU]`: Specify the MTU. Currently only used in raw mode.
+ Default and maximum value is 2043. Must be greater than zero.
+* `--gpio-int[=gpio-path]`: Specify a path to the Linux
+ sysfs-exported GPIO directory for the `I̅N̅T̅` pin. If not
+ specified, `spi-hdlc-adapter` will fall back to polling, which is
+ inefficient.
+* `--gpio-reset[=gpio-path]`: Specify a path to the Linux
+ sysfs-exported GPIO directory for the `R̅E̅S̅` pin.
+* `--spi-mode[=mode]`: Specify the SPI mode to use (0-3). Default
+ value is `0`.
+* `--spi-speed[=hertz]`: Specify the SPI speed in hertz. Default
+ value is `1000000` (1MHz).
+* `--spi-cs-delay[=usec]`: Specify the delay after C̅S̅ assertion,
+ in microseconds. Default is 20µs. Note that this may need to be
+ set to zero for spi-hdlc-adapter to work with some SPI drivers.
+* `--spi-align-allowance[=n]`: Specify the the maximum number of 0xFF
+ bytes to clip from start of MISO frame. This makes this tool usable
+ with SPI slaves which have buggy SPI blocks that prepend up to
+ three 0xFF bytes to the start of MISO frame. Default value is `0`.
+ Maximum value is `3`. *This must be set to at least `2` for chips
+ in the SiLabs EM35x family.*
+* `--verbose`: Increase debug verbosity.
+* `--help`: Print out usage information to `stdout` and exit.
+
+`spi-device-path` is a required argument since it indicates which SPI
+device to use. An example path might be `/dev/spidev1.0`.
+
+The GPIO paths are to the top-level directory for that GPIO. They must
+be already be exported before `spi-hdlc-adapter` can use them.
+
+## Behavior ##
+
+If an MCU reset is detected by the reset bit being set on a SPI frame,
+the special vendor-specific HDLC-lite symbol `0xF8` is emitted. If
+`--gpio-reset` is specified, the HDLC client can trigger an MCU reset
+by sending the symbols `0x7E 0x13 0x11 0x7E` or by sending `SIGUSR1`.
+
+When started, `spi-hdlc-adapter` will configure the following
+properties on the GPIOs:
+
+1. Set `I̅N̅T̅/direction` to `in`.
+2. Set `I̅N̅T̅/edge` to `falling`.
+3. Set `R̅E̅S̅/direction` to `high`.
+
+When resetting the slave device, `spi-hdlc` performs the following
+procedure:
+
+1. Set `R̅E̅S̅/direction` to `low`.
+2. Sleep for 30ms.
+3. Set `R̅E̅S̅/direction` to `high`.
+
+## Statistics ##
+
+Some simple usage statistics are printed out to syslog at exit and
+whenever the `SIGUSR1` signal is received. The easiest way to send
+that signal to `spi-hdlc-adapter` is like this:
+
+ # killall -sigusr1 spi-hdlc-adapter
+
+At which point you will see something like this in the syslogs:
+
+ spi-hdlc-adapter[5215]: INFO: sSpiFrameCount=45660
+ spi-hdlc-adapter[5215]: INFO: sSpiValidFrameCount=45643
+ spi-hdlc-adapter[5215]: INFO: sSpiGarbageFrameCount=17
+ spi-hdlc-adapter[5215]: INFO: sSpiDuplexFrameCount=20931
diff --git a/openthread/tools/spi-hdlc-adapter/spi-hdlc-adapter.c b/openthread/tools/spi-hdlc-adapter/spi-hdlc-adapter.c
new file mode 100644
index 0000000..20b762c
--- /dev/null
+++ b/openthread/tools/spi-hdlc-adapter/spi-hdlc-adapter.c
@@ -0,0 +1,1996 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * 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.
+ */
+
+#define _GNU_SOURCE 1
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/ucontext.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <linux/spi/spidev.h>
+
+#if HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#if HAVE_PTY_H
+#include <pty.h>
+#endif
+
+#if HAVE_UTIL_H
+#include <util.h>
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Macros and Constants */
+
+#define SPI_HDLC_VERSION "0.06"
+
+#define MAX_FRAME_SIZE 2048
+#define HEADER_LEN 5
+#define SPI_HEADER_RESET_FLAG 0x80
+#define SPI_HEADER_CRC_FLAG 0x40
+#define SPI_HEADER_PATTERN_VALUE 0x02
+#define SPI_HEADER_PATTERN_MASK 0x03
+
+#define EXIT_QUIT 65535
+
+#ifndef MSEC_PER_SEC
+#define MSEC_PER_SEC 1000
+#endif
+
+#ifndef USEC_PER_MSEC
+#define USEC_PER_MSEC 1000
+#endif
+
+#ifndef USEC_PER_SEC
+#define USEC_PER_SEC (USEC_PER_MSEC * MSEC_PER_SEC)
+#endif
+
+#define SPI_POLL_PERIOD_MSEC (MSEC_PER_SEC/30)
+
+#define GPIO_INT_ASSERT_STATE 0 // I̅N̅T̅ is asserted low
+#define GPIO_RES_ASSERT_STATE 0 // R̅E̅S̅ is asserted low
+
+#define SPI_RX_ALIGN_ALLOWANCE_MAX 6
+
+#define SOCKET_DEBUG_BYTES_PER_LINE 16
+
+#ifndef AUTO_PRINT_BACKTRACE
+#define AUTO_PRINT_BACKTRACE (HAVE_EXECINFO_H)
+#endif
+
+#define AUTO_PRINT_BACKTRACE_STACK_DEPTH 20
+
+static const uint8_t kHdlcResetSignal[] = { 0x7E, 0x13, 0x11, 0x7E };
+static const uint16_t kHdlcCrcCheckValue = 0xf0b8;
+static const uint16_t kHdlcCrcResetValue = 0xffff;
+
+enum {
+ MODE_STDIO = 0,
+ MODE_PTY = 1,
+};
+
+// Ignores return value from function 's'
+#define IGNORE_RETURN_VALUE(s) do { if (s){} } while (0)
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Global State */
+
+#if HAVE_OPENPTY
+static int sMode = MODE_PTY;
+#else
+static int sMode = MODE_STDIO;
+#endif
+
+static const char* sSpiDevPath = NULL;
+static const char* sIntGpioDevPath = NULL;
+static const char* sResGpioDevPath = NULL;
+
+static int sVerbose = LOG_NOTICE;
+
+static int sSpiDevFd = -1;
+static int sResGpioValueFd = -1;
+static int sIntGpioValueFd = -1;
+
+static int sHdlcInputFd = -1;
+static int sHdlcOutputFd = -1;
+
+static int sSpiSpeed = 1000000; // in Hz (default: 1MHz)
+static uint8_t sSpiMode = 0;
+static int sSpiCsDelay = 20; // in microseconds
+
+static uint16_t sSpiRxPayloadSize;
+static uint8_t sSpiRxFrameBuffer[MAX_FRAME_SIZE + SPI_RX_ALIGN_ALLOWANCE_MAX];
+
+static uint16_t sSpiTxPayloadSize;
+static bool sSpiTxIsReady = false;
+static int sSpiTxRefusedCount = 0;
+static uint8_t sSpiTxFrameBuffer[MAX_FRAME_SIZE + SPI_RX_ALIGN_ALLOWANCE_MAX];
+
+static int sSpiRxAlignAllowance = 0;
+static int sSpiSmallPacketSize = 32; // in bytes
+
+static bool sSlaveDidReset = false;
+
+// If sUseRawFrames is set to true, HDLC encoding/encoding
+// is skipped and the raw frames are read-from/written-to
+// the sHdlcInputFd/sHdlcOutputFd whole. See `--raw`.
+static bool sUseRawFrames = false;
+
+static int sMTU = MAX_FRAME_SIZE - HEADER_LEN;
+
+static int sRet = 0;
+
+static bool sDumpStats = false;
+
+static sig_t sPreviousHandlerForSIGINT;
+static sig_t sPreviousHandlerForSIGTERM;
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Statistics */
+
+static uint64_t sSlaveResetCount = 0;
+static uint64_t sSpiFrameCount = 0;
+static uint64_t sSpiValidFrameCount = 0;
+static uint64_t sSpiGarbageFrameCount = 0;
+static uint64_t sSpiDuplexFrameCount = 0;
+static uint64_t sSpiUnresponsiveFrameCount = 0;
+static uint64_t sHdlcRxFrameByteCount = 0;
+static uint64_t sHdlcTxFrameByteCount = 0;
+static uint64_t sHdlcRxFrameCount = 0;
+static uint64_t sHdlcTxFrameCount = 0;
+static uint64_t sHdlcRxBadCrcCount = 0;
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Signal Handlers */
+
+static void signal_SIGINT(int sig)
+{
+ static const char message[] = "\nCaught SIGINT!\n";
+
+ sRet = EXIT_QUIT;
+
+ // Can't use syslog() because it isn't async signal safe.
+ // So we write to stderr
+ IGNORE_RETURN_VALUE(write(STDERR_FILENO, message, sizeof(message)-1));
+
+ // Restore the previous handler so that if we end up getting
+ // this signal again we perform the system default action.
+ signal(SIGINT, sPreviousHandlerForSIGINT);
+ sPreviousHandlerForSIGINT = NULL;
+
+ // Ignore signal argument.
+ (void)sig;
+}
+
+static void signal_SIGTERM(int sig)
+{
+ static const char message[] = "\nCaught SIGTERM!\n";
+
+ sRet = EXIT_QUIT;
+
+ // Can't use syslog() because it isn't async signal safe.
+ // So we write to stderr
+ IGNORE_RETURN_VALUE(write(STDERR_FILENO, message, sizeof(message)-1));
+
+ // Restore the previous handler so that if we end up getting
+ // this signal again we perform the system default action.
+ signal(SIGTERM, sPreviousHandlerForSIGTERM);
+ sPreviousHandlerForSIGTERM = NULL;
+
+ // Ignore signal argument.
+ (void)sig;
+}
+
+static void signal_SIGHUP(int sig)
+{
+ static const char message[] = "\nCaught SIGHUP!\n";
+
+ sRet = EXIT_FAILURE;
+
+ // Can't use syslog() because it isn't async signal safe.
+ // So we write to stderr
+ IGNORE_RETURN_VALUE(write(STDERR_FILENO, message, sizeof(message)-1));
+
+ // We don't restore the "previous handler"
+ // because we always want to let the main
+ // loop decide what to do for hangups.
+
+ // Ignore signal argument.
+ (void)sig;
+}
+
+static void signal_dumpstats(int sig)
+{
+ sDumpStats = true;
+
+ // Ignore signal argument.
+ (void)sig;
+}
+
+static void signal_clearstats(int sig)
+{
+ sDumpStats = true;
+ sSlaveResetCount = 0;
+ sSpiFrameCount = 0;
+ sSpiValidFrameCount = 0;
+ sSpiGarbageFrameCount = 0;
+ sSpiDuplexFrameCount = 0;
+ sSpiUnresponsiveFrameCount = 0;
+ sHdlcRxFrameByteCount = 0;
+ sHdlcTxFrameByteCount = 0;
+ sHdlcRxFrameCount = 0;
+ sHdlcTxFrameCount = 0;
+ sHdlcRxBadCrcCount = 0;
+
+ // Ignore signal argument.
+ (void)sig;
+}
+
+#if AUTO_PRINT_BACKTRACE
+static void signal_critical(int sig, siginfo_t * info, void * ucontext)
+{
+ // This is the last hurah for this process.
+ // We dump the stack, because that's all we can do.
+
+ void *stack_mem[AUTO_PRINT_BACKTRACE_STACK_DEPTH];
+ void **stack = stack_mem;
+ char **stack_symbols;
+ int stack_depth, i;
+ ucontext_t *uc = (ucontext_t*)ucontext;
+
+ // Shut up compiler warning.
+ (void)uc;
+ (void)info;
+
+ // We call some functions here which aren't async-signal-safe,
+ // but this function isn't really useful without those calls.
+ // Since we are making a gamble (and we deadlock if we loose),
+ // we are going to set up a two-second watchdog to make sure
+ // we end up terminating like we should. The choice of a two
+ // second timeout is entirely arbitrary, and may be changed
+ // if needs warrant.
+ alarm(2);
+ signal(SIGALRM, SIG_DFL);
+
+ fprintf(stderr, " *** FATAL ERROR: Caught signal %d (%s):\n", sig, strsignal(sig));
+
+ stack_depth = backtrace(stack, AUTO_PRINT_BACKTRACE_STACK_DEPTH);
+
+ // Here are are trying to update the pointer in the backtrace
+ // to be the actual location of the fault.
+#if defined(__x86_64__)
+ stack[1] = (void *) uc->uc_mcontext.gregs[REG_RIP];
+#elif defined(__i386__)
+ stack[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
+#elif defined(__arm__)
+ stack[1] = (void *) uc->uc_mcontext.arm_ip;
+#else
+#warning TODO: Add this arch to signal_critical
+#endif
+
+ // Now dump the symbols to stderr, in case syslog barfs.
+ backtrace_symbols_fd(stack, stack_depth, STDERR_FILENO);
+
+ // Load up the symbols individually, so we can output to syslog, too.
+ stack_symbols = backtrace_symbols(stack, stack_depth);
+
+ syslog(LOG_CRIT, " *** FATAL ERROR: Caught signal %d (%s):", sig, strsignal(sig));
+
+ for (i = 0; i != stack_depth; i++)
+ {
+ syslog(LOG_CRIT, "[BT] %2d: %s", i, stack_symbols[i]);
+ }
+
+ free(stack_symbols);
+
+ exit(EXIT_FAILURE);
+}
+#endif // if AUTO_PRINT_BACKTRACE
+
+static void log_debug_buffer(const char* desc, const uint8_t* buffer_ptr, int buffer_len, bool force)
+{
+ int i = 0;
+
+ if (!force && (sVerbose < LOG_DEBUG))
+ {
+ return;
+ }
+
+ while (i < buffer_len)
+ {
+ int j;
+ char dump_string[SOCKET_DEBUG_BYTES_PER_LINE*3+1];
+
+ for (j = 0; i < buffer_len && j < SOCKET_DEBUG_BYTES_PER_LINE; i++, j++)
+ {
+ sprintf(dump_string+j*3, "%02X ", buffer_ptr[i]);
+ }
+
+ syslog(force ? LOG_WARNING : LOG_DEBUG, "%s: %s%s", desc, dump_string, (i < buffer_len)?" ...":"");
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* MARK: SPI Transfer Functions */
+
+static void spi_header_set_flag_byte(uint8_t *header, uint8_t value)
+{
+ header[0] = value;
+}
+
+static void spi_header_set_accept_len(uint8_t *header, uint16_t len)
+{
+ header[1] = ((len >> 0) & 0xFF);
+ header[2] = ((len >> 8) & 0xFF);
+}
+
+static void spi_header_set_data_len(uint8_t *header, uint16_t len)
+{
+ header[3] = ((len >> 0) & 0xFF);
+ header[4] = ((len >> 8) & 0xFF);
+}
+
+static uint8_t spi_header_get_flag_byte(const uint8_t *header)
+{
+ return header[0];
+}
+
+static uint16_t spi_header_get_accept_len(const uint8_t *header)
+{
+ return ( header[1] + (uint16_t)(header[2] << 8) );
+}
+
+static uint16_t spi_header_get_data_len(const uint8_t *header)
+{
+ return ( header[3] + (uint16_t)(header[4] << 8) );
+}
+
+static uint8_t* get_real_rx_frame_start(void)
+{
+ uint8_t* ret = sSpiRxFrameBuffer;
+ int i = 0;
+
+ for (i = 0; i < sSpiRxAlignAllowance; i++)
+ {
+ if (ret[0] != 0xFF)
+ {
+ break;
+ }
+ ret++;
+ }
+
+ return ret;
+}
+
+static int do_spi_xfer(int len)
+ {
+ int ret;
+
+ struct spi_ioc_transfer xfer[2] =
+ {
+ { // This part is the delay between C̅S̅ being
+ // asserted and the SPI clock starting. This
+ // is not supported by all Linux SPI drivers.
+ .tx_buf = 0,
+ .rx_buf = 0,
+ .len = 0,
+ .delay_usecs = (uint16_t)sSpiCsDelay,
+ .speed_hz = (uint32_t)sSpiSpeed,
+ .bits_per_word = 8,
+ .cs_change = false,
+ },
+ { // This part is the actual SPI transfer.
+ .tx_buf = (unsigned long)sSpiTxFrameBuffer,
+ .rx_buf = (unsigned long)sSpiRxFrameBuffer,
+ .len = (uint32_t)(len + HEADER_LEN + sSpiRxAlignAllowance),
+ .delay_usecs = 0,
+ .speed_hz = (uint32_t)sSpiSpeed,
+ .bits_per_word = 8,
+ .cs_change = false,
+ }
+ };
+
+
+
+ if (sSpiCsDelay > 0)
+ {
+ // A C̅S̅ delay has been specified. Start transactions
+ // with both parts.
+ ret = ioctl(sSpiDevFd, SPI_IOC_MESSAGE(2), &xfer[0]);
+ }
+ else
+ {
+ // No C̅S̅ delay has been specified, so we skip the first
+ // part because it causes some SPI drivers to croak.
+ ret = ioctl(sSpiDevFd, SPI_IOC_MESSAGE(1), &xfer[1]);
+ }
+
+ if (ret != -1)
+ {
+ log_debug_buffer("SPI-TX", sSpiTxFrameBuffer, (int)xfer[1].len, false);
+ log_debug_buffer("SPI-RX", sSpiRxFrameBuffer, (int)xfer[1].len, false);
+
+ sSpiFrameCount++;
+ }
+
+ return ret;
+}
+
+static void debug_spi_header(const char* hint, bool force)
+{
+ if (force || (sVerbose >= LOG_DEBUG))
+ {
+ const uint8_t* spiRxFrameBuffer = get_real_rx_frame_start();
+
+ syslog(force ? LOG_WARNING : LOG_DEBUG, "%s-TX: H:%02X ACCEPT:%d DATA:%0d\n",
+ hint,
+ spi_header_get_flag_byte(sSpiTxFrameBuffer),
+ spi_header_get_accept_len(sSpiTxFrameBuffer),
+ spi_header_get_data_len(sSpiTxFrameBuffer)
+ );
+
+ syslog(force ? LOG_WARNING : LOG_DEBUG, "%s-RX: H:%02X ACCEPT:%d DATA:%0d\n",
+ hint,
+ spi_header_get_flag_byte(spiRxFrameBuffer),
+ spi_header_get_accept_len(spiRxFrameBuffer),
+ spi_header_get_data_len(spiRxFrameBuffer)
+ );
+ }
+}
+
+static int push_pull_spi(void)
+{
+ int ret;
+ uint16_t spi_xfer_bytes = 0;
+ const uint8_t* spiRxFrameBuffer = NULL;
+ uint8_t slave_header;
+ uint16_t slave_max_rx;
+ int successful_exchanges = 0;
+
+ static uint16_t slave_data_len;
+
+ // For now, sSpiRxPayloadSize must be zero
+ // when entering this function. This may change
+ // at some point, for now this makes things
+ // much easier.
+ assert(sSpiRxPayloadSize == 0);
+
+ if (sSpiValidFrameCount == 0)
+ {
+ // Set the reset flag to indicate to our slave that we
+ // are coming up from scratch.
+ spi_header_set_flag_byte(sSpiTxFrameBuffer, SPI_HEADER_RESET_FLAG|SPI_HEADER_PATTERN_VALUE);
+ }
+ else
+ {
+ spi_header_set_flag_byte(sSpiTxFrameBuffer, SPI_HEADER_PATTERN_VALUE);
+ }
+
+ // Zero out our rx_accept and our data_len for now.
+ spi_header_set_accept_len(sSpiTxFrameBuffer, 0);
+ spi_header_set_data_len(sSpiTxFrameBuffer, 0);
+
+ // Sanity check.
+ if (slave_data_len > MAX_FRAME_SIZE)
+ {
+ slave_data_len = 0;
+ }
+
+ if (sSpiTxIsReady)
+ {
+ // Go ahead and try to immediately send a frame if we have it queued up.
+ spi_header_set_data_len(sSpiTxFrameBuffer, sSpiTxPayloadSize);
+
+ if (sSpiTxPayloadSize > spi_xfer_bytes)
+ {
+ spi_xfer_bytes = sSpiTxPayloadSize;
+ }
+ }
+
+ if (sSpiRxPayloadSize == 0)
+ {
+ if (slave_data_len != 0)
+ {
+ // In a previous transaction the slave indicated
+ // it had something to send us. Make sure our
+ // transaction is large enough to handle it.
+ if (slave_data_len > spi_xfer_bytes)
+ {
+ spi_xfer_bytes = slave_data_len;
+ }
+ }
+ else
+ {
+ // Set up a minimum transfer size to allow small
+ // frames the slave wants to send us to be handled
+ // in a single transaction.
+ if (sSpiSmallPacketSize > spi_xfer_bytes)
+ {
+ spi_xfer_bytes = (uint16_t)sSpiSmallPacketSize;
+ }
+ }
+
+ spi_header_set_accept_len(sSpiTxFrameBuffer, spi_xfer_bytes);
+ }
+
+ // Perform the SPI transaction.
+ ret = do_spi_xfer(spi_xfer_bytes);
+
+ if (ret < 0)
+ {
+ perror("do_spi_xfer");
+
+ // Print out a helpful error message for
+ // a common error.
+ if ( (sSpiCsDelay != 0)
+ && (errno == EINVAL)
+ ) {
+ syslog(LOG_ERR, "SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
+ }
+ goto bail;
+ }
+
+ // Account for misalignment (0xFF bytes at the start)
+ spiRxFrameBuffer = get_real_rx_frame_start();
+
+ debug_spi_header("push_pull", false);
+
+ slave_header = spi_header_get_flag_byte(spiRxFrameBuffer);
+
+ if ((slave_header == 0xFF) || (slave_header == 0x00))
+ {
+ if ( (slave_header == spiRxFrameBuffer[1])
+ && (slave_header == spiRxFrameBuffer[2])
+ && (slave_header == spiRxFrameBuffer[3])
+ && (slave_header == spiRxFrameBuffer[4])
+ ) {
+ // Device is off or in a bad state.
+ // In some cases may be induced by flow control.
+ syslog(slave_data_len == 0 ? LOG_DEBUG : LOG_WARNING, "Slave did not respond to frame. (Header was all 0x%02X)", slave_header);
+ sSpiUnresponsiveFrameCount++;
+ }
+ else
+ {
+ // Header is full of garbage
+ syslog(
+ LOG_WARNING,
+ "Garbage in header : %02X %02X %02X %02X %02X",
+ spiRxFrameBuffer[0],
+ spiRxFrameBuffer[1],
+ spiRxFrameBuffer[2],
+ spiRxFrameBuffer[3],
+ spiRxFrameBuffer[4]
+ );
+ sSpiGarbageFrameCount++;
+ if (sVerbose < LOG_DEBUG)
+ {
+ log_debug_buffer("SPI-TX", sSpiTxFrameBuffer, (int)spi_xfer_bytes + HEADER_LEN + sSpiRxAlignAllowance, true);
+ log_debug_buffer("SPI-RX", sSpiRxFrameBuffer, (int)spi_xfer_bytes + HEADER_LEN + sSpiRxAlignAllowance, true);
+ }
+ }
+ sSpiTxRefusedCount++;
+ goto bail;
+ }
+
+ slave_max_rx = spi_header_get_accept_len(spiRxFrameBuffer);
+ slave_data_len = spi_header_get_data_len(spiRxFrameBuffer);
+
+ if ( ((slave_header & SPI_HEADER_PATTERN_MASK) != SPI_HEADER_PATTERN_VALUE)
+ || (slave_max_rx > MAX_FRAME_SIZE)
+ || (slave_data_len > MAX_FRAME_SIZE)
+ )
+ {
+ sSpiGarbageFrameCount++;
+ sSpiTxRefusedCount++;
+ slave_data_len = 0;
+ syslog(
+ LOG_WARNING,
+ "Garbage in header : %02X %02X %02X %02X %02X",
+ spiRxFrameBuffer[0],
+ spiRxFrameBuffer[1],
+ spiRxFrameBuffer[2],
+ spiRxFrameBuffer[3],
+ spiRxFrameBuffer[4]
+ );
+ if (sVerbose < LOG_DEBUG)
+ {
+ log_debug_buffer("SPI-TX", sSpiTxFrameBuffer, (int)spi_xfer_bytes + HEADER_LEN + sSpiRxAlignAllowance, true);
+ log_debug_buffer("SPI-RX", sSpiRxFrameBuffer, (int)spi_xfer_bytes + HEADER_LEN + sSpiRxAlignAllowance, true);
+ }
+ goto bail;
+ }
+
+ sSpiValidFrameCount++;
+
+ if ( (slave_header & SPI_HEADER_RESET_FLAG) == SPI_HEADER_RESET_FLAG)
+ {
+ sSlaveResetCount++;
+ syslog(LOG_NOTICE, "Slave did reset (%llu resets so far)", (unsigned long long)sSlaveResetCount);
+ sSlaveDidReset = true;
+ sDumpStats = true;
+ }
+
+ // Handle received packet, if any.
+ if ( (sSpiRxPayloadSize == 0)
+ && (slave_data_len != 0)
+ && (slave_data_len <= spi_header_get_accept_len(sSpiTxFrameBuffer))
+ ) {
+ // We have received a packet. Set sSpiRxPayloadSize so that
+ // the packet will eventually get queued up by push_hdlc().
+ sSpiRxPayloadSize = slave_data_len;
+
+ slave_data_len = 0;
+
+ successful_exchanges++;
+ }
+
+ // Handle transmitted packet, if any.
+ if ( sSpiTxIsReady
+ && (sSpiTxPayloadSize == spi_header_get_data_len(sSpiTxFrameBuffer))
+ ) {
+ if (spi_header_get_data_len(sSpiTxFrameBuffer) <= slave_max_rx)
+ {
+ // Our outbound packet has been successfully transmitted. Clear
+ // sSpiTxPayloadSize and sSpiTxIsReady so that pull_hdlc() can
+ // pull another packet for us to send.
+ sSpiTxIsReady = false;
+ sSpiTxPayloadSize = 0;
+ sSpiTxRefusedCount = 0;
+ successful_exchanges++;
+ }
+ else
+ {
+ // The slave Wasn't ready for what we had to
+ // send them. Incrementing this counter will
+ // turn on rate limiting so that we
+ // don't waste a ton of CPU bombarding them
+ // with useless SPI transfers.
+ sSpiTxRefusedCount++;
+ }
+ }
+
+ if (!sSpiTxIsReady)
+ {
+ sSpiTxRefusedCount = 0;
+ }
+
+ if (successful_exchanges == 2)
+ {
+ sSpiDuplexFrameCount++;
+ }
+bail:
+ return ret;
+}
+
+static bool check_and_clear_interrupt(void)
+{
+ if (sIntGpioValueFd >= 0)
+ {
+ char value[5] = "";
+ ssize_t len;
+
+ lseek(sIntGpioValueFd, 0, SEEK_SET);
+
+ len = read(sIntGpioValueFd, value, sizeof(value)-1);
+
+ if (len < 0)
+ {
+ perror("check_and_clear_interrupt");
+ sRet = EXIT_FAILURE;
+ }
+
+ // The interrupt pin is active low.
+ return GPIO_INT_ASSERT_STATE == atoi(value);
+ }
+
+ return true;
+}
+
+/* ------------------------------------------------------------------------- */
+/* MARK: HDLC Transfer Functions */
+
+#define HDLC_BYTE_FLAG 0x7E
+#define HDLC_BYTE_ESC 0x7D
+#define HDLC_BYTE_XON 0x11
+#define HDLC_BYTE_XOFF 0x13
+#define HDLC_BYTE_SPECIAL 0xF8
+#define HDLC_ESCAPE_XFORM 0x20
+
+static uint16_t hdlc_crc16(uint16_t aFcs, uint8_t aByte)
+{
+#if 1
+ // CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT
+ // width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 name="KERMIT"
+ // http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
+ 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];
+#else
+ // CRC-16/CCITT-FALSE, same CRC as 802.15.4
+ // width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 name="CRC-16/CCITT-FALSE"
+ // http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.crc-16-ccitt-false
+ aFcs = (uint16_t)((aFcs >> 8) | (aFcs << 8));
+ aFcs ^= aByte;
+ aFcs ^= ((aFcs & 0xff) >> 4);
+ aFcs ^= (aFcs << 12);
+ aFcs ^= ((aFcs & 0xff) << 5);
+ return aFcs;
+#endif
+}
+
+static bool hdlc_byte_needs_escape(uint8_t byte)
+{
+ switch(byte)
+ {
+ case HDLC_BYTE_SPECIAL:
+ case HDLC_BYTE_ESC:
+ case HDLC_BYTE_FLAG:
+ case HDLC_BYTE_XOFF:
+ case HDLC_BYTE_XON:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static int push_hdlc(void)
+{
+ int ret = 0;
+ const uint8_t* spiRxFrameBuffer = get_real_rx_frame_start();
+ static uint8_t escaped_frame_buffer[MAX_FRAME_SIZE*2];
+ static uint16_t unescaped_frame_len;
+ static uint16_t escaped_frame_len;
+ static uint16_t escaped_frame_sent;
+
+ if (escaped_frame_len == 0)
+ {
+ if (sSlaveDidReset)
+ {
+ // Indicate an MCU reset.
+ memcpy(escaped_frame_buffer, kHdlcResetSignal, sizeof(kHdlcResetSignal));
+ escaped_frame_len = sizeof(kHdlcResetSignal);
+ sSlaveDidReset = false;
+
+ // Set this to zero, since this isn't a real frame.
+ unescaped_frame_len = 0;
+ }
+ else if (sSpiRxPayloadSize != 0)
+ {
+ // Escape the frame.
+ uint8_t c;
+ uint16_t fcs = kHdlcCrcResetValue;
+ uint16_t i;
+
+ unescaped_frame_len = sSpiRxPayloadSize;
+
+ for (i = 0; i < sSpiRxPayloadSize; i++)
+ {
+ c = spiRxFrameBuffer[i + HEADER_LEN];
+ fcs = hdlc_crc16(fcs, c);
+ if (hdlc_byte_needs_escape(c))
+ {
+ escaped_frame_buffer[escaped_frame_len++] = HDLC_BYTE_ESC;
+ escaped_frame_buffer[escaped_frame_len++] = c ^ HDLC_ESCAPE_XFORM;
+ }
+ else
+ {
+ escaped_frame_buffer[escaped_frame_len++] = c;
+ }
+ }
+
+ fcs ^= 0xFFFF;
+
+ c = fcs & 0xFF;
+ if (hdlc_byte_needs_escape(c))
+ {
+ escaped_frame_buffer[escaped_frame_len++] = HDLC_BYTE_ESC;
+ escaped_frame_buffer[escaped_frame_len++] = c ^ HDLC_ESCAPE_XFORM;
+ }
+ else
+ {
+ escaped_frame_buffer[escaped_frame_len++] = c;
+ }
+
+ c = (fcs >> 8) & 0xFF;
+ if (hdlc_byte_needs_escape(c))
+ {
+ escaped_frame_buffer[escaped_frame_len++] = HDLC_BYTE_ESC;
+ escaped_frame_buffer[escaped_frame_len++] = c ^ HDLC_ESCAPE_XFORM;
+ }
+ else
+ {
+ escaped_frame_buffer[escaped_frame_len++] = c;
+ }
+
+ escaped_frame_buffer[escaped_frame_len++] = HDLC_BYTE_FLAG;
+ escaped_frame_sent = 0;
+ sSpiRxPayloadSize = 0;
+
+ }
+ else
+ {
+ // Nothing to do.
+ goto bail;
+ }
+ }
+
+ ret = (int)write(
+ sHdlcOutputFd,
+ escaped_frame_buffer + escaped_frame_sent,
+ escaped_frame_len - escaped_frame_sent
+ );
+
+ if (ret < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ ret = 0;
+ }
+ else
+ {
+ perror("push_hdlc:write");
+ syslog(LOG_ERR, "push_hdlc:write: errno=%d (%s)", errno, strerror(errno));
+ }
+ goto bail;
+ }
+
+ escaped_frame_sent += ret;
+
+ // Reset state once we have sent the entire frame.
+ if (escaped_frame_len == escaped_frame_sent)
+ {
+ escaped_frame_len = escaped_frame_sent = 0;
+
+ // Increment counter for statistics
+ sHdlcTxFrameCount++;
+ sHdlcTxFrameByteCount += unescaped_frame_len;
+ }
+
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int pull_hdlc(void)
+{
+ int ret = 0;
+ static uint16_t fcs;
+ static bool unescape_next_byte = false;
+
+ if (!sSpiTxIsReady)
+ {
+ uint8_t byte;
+ while ((ret = (int)read(sHdlcInputFd, &byte, 1)) == 1)
+ {
+ if (sSpiTxPayloadSize >= (MAX_FRAME_SIZE - HEADER_LEN))
+ {
+ syslog(LOG_WARNING, "HDLC frame was too big");
+ unescape_next_byte = false;
+ sSpiTxPayloadSize = 0;
+ fcs = kHdlcCrcResetValue;
+
+ }
+ else if (byte == HDLC_BYTE_FLAG)
+ {
+ if (sSpiTxPayloadSize <= 2)
+ {
+ unescape_next_byte = false;
+ sSpiTxPayloadSize = 0;
+ fcs = kHdlcCrcResetValue;
+ continue;
+
+ }
+ else if (fcs != kHdlcCrcCheckValue)
+ {
+ syslog(LOG_WARNING, "HDLC frame with bad CRC (LEN:%d, FCS:0x%04X)", sSpiTxPayloadSize, fcs);
+ sHdlcRxBadCrcCount++;
+ unescape_next_byte = false;
+ sSpiTxPayloadSize = 0;
+ fcs = kHdlcCrcResetValue;
+ continue;
+ }
+
+ // Clip off the CRC
+ sSpiTxPayloadSize -= 2;
+
+ // Indicate that a frame is ready to go out
+ sSpiTxIsReady = true;
+
+ // Increment counters for statistics
+ sHdlcRxFrameCount++;
+ sHdlcRxFrameByteCount += sSpiTxPayloadSize;
+
+ // Clean up for the next frame
+ unescape_next_byte = false;
+ fcs = kHdlcCrcResetValue;
+ break;
+
+ }
+ else if (byte == HDLC_BYTE_ESC)
+ {
+ unescape_next_byte = true;
+ continue;
+
+ }
+ else if (hdlc_byte_needs_escape(byte))
+ {
+ // Skip all other control codes.
+ continue;
+
+ }
+ else if (unescape_next_byte)
+ {
+ byte = byte ^ HDLC_ESCAPE_XFORM;
+ unescape_next_byte = false;
+ }
+
+ fcs = hdlc_crc16(fcs, byte);
+ sSpiTxFrameBuffer[HEADER_LEN + sSpiTxPayloadSize++] = byte;
+ }
+ }
+
+ if (ret < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ ret = 0;
+ }
+ else
+ {
+ perror("pull_hdlc:read");
+ syslog(LOG_ERR, "pull_hdlc:read: errno=%d (%s)", errno, strerror(errno));
+ }
+ }
+
+ return ret < 0
+ ? ret
+ : 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Raw Transfer Functions */
+
+static int push_raw(void)
+{
+ int ret = 0;
+ const uint8_t* spiRxFrameBuffer = get_real_rx_frame_start();
+ static uint8_t raw_frame_buffer[MAX_FRAME_SIZE];
+ static uint16_t raw_frame_len;
+ static uint16_t raw_frame_sent;
+
+ if (raw_frame_len == 0)
+ {
+ if (sSlaveDidReset)
+ {
+ // Indicates an MCU reset.
+ // We don't have anything to do here because
+ // raw mode doesn't have any way to signal
+ // resets out-of-band.
+ sSlaveDidReset = false;
+ }
+ else if (sSpiRxPayloadSize > 0)
+ {
+ // Read the frame into raw_frame_buffer
+ assert(sSpiRxPayloadSize <= sizeof(raw_frame_buffer));
+ memcpy(raw_frame_buffer, &spiRxFrameBuffer[HEADER_LEN], sSpiRxPayloadSize);
+ raw_frame_len = sSpiRxPayloadSize;
+ raw_frame_sent = 0;
+ sSpiRxPayloadSize = 0;
+ }
+ else
+ {
+ // Nothing to do.
+ goto bail;
+ }
+ }
+
+ ret = (int)write(
+ sHdlcOutputFd,
+ raw_frame_buffer + raw_frame_sent,
+ raw_frame_len - raw_frame_sent
+ );
+
+ if (ret < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ ret = 0;
+ }
+ else
+ {
+ perror("push_raw:write");
+ syslog(LOG_ERR, "push_raw:write: errno=%d (%s)", errno, strerror(errno));
+ }
+ goto bail;
+ }
+
+ raw_frame_sent += ret;
+
+ // Reset state once we have sent the entire frame.
+ if (raw_frame_len == raw_frame_sent)
+ {
+ // Increment counter for statistics
+ sHdlcTxFrameCount++;
+ sHdlcTxFrameByteCount += raw_frame_len;
+
+ raw_frame_len = raw_frame_sent = 0;
+ }
+
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int pull_raw(void)
+{
+ int ret = 0;
+
+ if (!sSpiTxIsReady)
+ {
+ ret = (int)read(sHdlcInputFd, &sSpiTxFrameBuffer[HEADER_LEN], (size_t)sMTU);
+
+ if (ret < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ ret = 0;
+ }
+ else
+ {
+ perror("pull_raw:read");
+ syslog(LOG_ERR, "pull_raw:read: errno=%d (%s)", errno, strerror(errno));
+ }
+
+ }
+ else if (ret > 0)
+ {
+ sSpiTxPayloadSize = (uint16_t)ret;
+ sSpiTxIsReady = true;
+
+ // Increment counters for statistics
+ sHdlcRxFrameCount++;
+ sHdlcRxFrameByteCount += sSpiTxPayloadSize;
+ }
+ }
+
+ return ret < 0
+ ? ret
+ : 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Setup Functions */
+
+static bool update_spi_mode(int x)
+{
+ sSpiMode = (uint8_t)x;
+
+ if ( (sSpiDevFd >= 0)
+ && (ioctl(sSpiDevFd, SPI_IOC_WR_MODE, &sSpiMode) < 0)
+ )
+ {
+ perror("ioctl(SPI_IOC_WR_MODE)");
+ return false;
+ }
+
+ return true;
+}
+
+static bool update_spi_speed(int x)
+{
+ sSpiSpeed = x;
+
+ if ( (sSpiDevFd >= 0)
+ && (ioctl(sSpiDevFd, SPI_IOC_WR_MAX_SPEED_HZ, &sSpiSpeed) < 0)
+ )
+ {
+ perror("ioctl(SPI_IOC_WR_MAX_SPEED_HZ)");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool setup_spi_dev(const char* path)
+{
+ int fd = -1;
+ const uint8_t spi_word_bits = 8;
+ int ret;
+ sSpiDevPath = path;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ {
+ perror("open");
+ goto bail;
+ }
+
+ // Set the SPI mode.
+ ret = ioctl(fd, SPI_IOC_WR_MODE, &sSpiMode);
+ if (ret < 0)
+ {
+ perror("ioctl(SPI_IOC_WR_MODE)");
+ goto bail;
+ }
+
+ // Set the SPI clock speed.
+ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &sSpiSpeed);
+ if (ret < 0)
+ {
+ perror("ioctl(SPI_IOC_WR_MAX_SPEED_HZ)");
+ goto bail;
+ }
+
+ // Set the SPI word size.
+ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &spi_word_bits);
+ if (ret < 0)
+ {
+ perror("ioctl(SPI_IOC_WR_BITS_PER_WORD)");
+ goto bail;
+ }
+
+ // Lock the file descriptor
+ if (flock(fd, LOCK_EX | LOCK_NB) < 0)
+ {
+ perror("flock");
+ goto bail;
+ }
+
+ sSpiDevFd = fd;
+ fd = -1;
+
+bail:
+ if (fd >= 0)
+ {
+ close(fd);
+ }
+ return sSpiDevFd >= 0;
+}
+
+static bool setup_res_gpio(const char* path)
+{
+ int setup_fd = -1;
+ char* dir_path = NULL;
+ char* value_path = NULL;
+ int len;
+
+ sResGpioDevPath = path;
+
+ len = asprintf(&dir_path, "%s/direction", path);
+
+ if (len < 0)
+ {
+ perror("asprintf");
+ goto bail;
+ }
+
+ len = asprintf(&value_path, "%s/value", path);
+
+ if (len < 0)
+ {
+ perror("asprintf");
+ goto bail;
+ }
+
+ setup_fd = open(dir_path, O_WRONLY);
+
+ if (setup_fd >= 0)
+ {
+ if (-1 == write(setup_fd, "high\n", 5))
+ {
+ perror("set_res_direction");
+ goto bail;
+ }
+ }
+
+ sResGpioValueFd = open(value_path, O_WRONLY);
+
+bail:
+
+ if (setup_fd >= 0)
+ {
+ close(setup_fd);
+ }
+
+ if (dir_path)
+ {
+ free(dir_path);
+ }
+
+ if (value_path)
+ {
+ free(value_path);
+ }
+
+ return sResGpioValueFd >= 0;
+}
+
+static void trigger_reset(void)
+{
+ if (sResGpioValueFd >= 0)
+ {
+ char str[] = { '0' + GPIO_RES_ASSERT_STATE, '\n' };
+
+ lseek(sResGpioValueFd, 0, SEEK_SET);
+ if (write(sResGpioValueFd, str, sizeof(str)) == -1)
+ {
+ syslog(LOG_ERR, "trigger_reset(): error on write: %d (%s)", errno, strerror(errno));
+ }
+
+ usleep(10 * USEC_PER_MSEC);
+
+ // Set the string to switch to the not-asserted state.
+ str[0] = '0' + !GPIO_RES_ASSERT_STATE;
+
+ lseek(sResGpioValueFd, 0, SEEK_SET);
+ if (write(sResGpioValueFd, str, sizeof(str)) == -1)
+ {
+ syslog(LOG_ERR, "trigger_reset(): error on write: %d (%s)", errno, strerror(errno));
+ }
+
+ syslog(LOG_NOTICE, "Triggered hardware reset");
+ }
+}
+
+static bool setup_int_gpio(const char* path)
+{
+ char* edge_path = NULL;
+ char* dir_path = NULL;
+ char* value_path = NULL;
+ ssize_t len;
+ int setup_fd = -1;
+
+ sIntGpioValueFd = -1;
+
+ sIntGpioDevPath = path;
+
+ len = asprintf(&dir_path, "%s/direction", path);
+
+ if (len < 0)
+ {
+ perror("asprintf");
+ goto bail;
+ }
+
+ len = asprintf(&edge_path, "%s/edge", path);
+
+ if (len < 0)
+ {
+ perror("asprintf");
+ goto bail;
+ }
+
+ len = asprintf(&value_path, "%s/value", path);
+
+ if (len < 0)
+ {
+ perror("asprintf");
+ goto bail;
+ }
+
+ setup_fd = open(dir_path, O_WRONLY);
+
+ if (setup_fd >= 0)
+ {
+ len = write(setup_fd, "in", 2);
+ if (len < 0)
+ {
+ perror("write");
+ goto bail;
+ }
+
+ close(setup_fd);
+ }
+
+ setup_fd = open(edge_path, O_WRONLY);
+
+ if (setup_fd >= 0)
+ {
+ len = write(setup_fd, "falling", 7);
+
+ if (len < 0)
+ {
+ perror("write");
+ goto bail;
+ }
+
+ close(setup_fd);
+
+ setup_fd = -1;
+ }
+
+ sIntGpioValueFd = open(value_path, O_RDONLY);
+
+bail:
+
+ if (setup_fd >= 0)
+ {
+ close(setup_fd);
+ }
+
+ if (edge_path)
+ {
+ free(edge_path);
+ }
+
+ if (dir_path)
+ {
+ free(dir_path);
+ }
+
+ if (value_path)
+ {
+ free(value_path);
+ }
+
+ return sIntGpioValueFd >= 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Help */
+
+static void print_version(void)
+{
+ printf("spi-hdlc-adapter " SPI_HDLC_VERSION " (" __TIME__ " " __DATE__ ")\n");
+ printf("Copyright (c) 2017 The OpenThread Authors, All Rights Reserved\n");
+}
+
+static void print_help(void)
+{
+ print_version();
+ const char* help =
+ "\n"
+ "Syntax:\n"
+ "\n"
+ " spi-hdlc [options] <spi-device-path>\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " --stdio ...................... Use `stdin` and `stdout` for HDLC input and\n"
+ " output. Useful when directly started by the\n"
+ " program that will be using it.\n"
+#if HAVE_OPENPTY
+ " --pty ........................ Create a pseudoterminal for HDLC input and\n"
+ " output. The path of the newly-created PTY\n"
+ " will be written to `stdout`, followed by a\n"
+ " newline.\n"
+#endif // HAVE_OPENPTY
+ " --raw ........................ Do not encode/decode packets using HDLC.\n"
+ " Instead, write whole, raw frames to the\n"
+ " specified input and output FDs. This is useful\n"
+ " for emulating a serial port, or when datagram-\n"
+ " based sockets are supplied for stdin and\n"
+ " stdout` (when used with --stdio).\n"
+ " --mtu=[MTU] .................. Specify the MTU. Currently only used in raw mode.\n"
+ " Default and maximum value is 2043.\n"
+ " -i/--gpio-int[=gpio-path] .... Specify a path to the Linux sysfs-exported\n"
+ " GPIO directory for the `I̅N̅T̅` pin. If not\n"
+ " specified, `spi-hdlc` will fall back to\n"
+ " polling, which is inefficient.\n"
+ " -r/--gpio-reset[=gpio-path] .. Specify a path to the Linux sysfs-exported\n"
+ " GPIO directory for the `R̅E̅S̅` pin.\n"
+ " --spi-mode[=mode] ............ Specify the SPI mode to use (0-3).\n"
+ " --spi-speed[=hertz] .......... Specify the SPI speed in hertz.\n"
+ " --spi-cs-delay[=usec] ........ Specify the delay after C̅S̅ assertion, in µsec\n"
+ " --spi-align-allowance[=n] .... Specify the the maximum number of FF bytes to\n"
+ " clip from start of MISO frame. Max value is 3.\n"
+ " --spi-small-packet=[n] ....... Specify the smallest packet we can receive\n"
+ " in a single transaction(larger packets will\n"
+ " require two transactions). Default value is 32.\n"
+ " -v/--verbose ................. Increase debug verbosity. (Repeatable)\n"
+ " -h/-?/--help ................. Print out usage information and exit.\n"
+ "\n";
+
+ printf("%s", help);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Main Loop */
+
+int main(int argc, char *argv[])
+{
+ int i = 0;
+ char prog[32];
+ static fd_set read_set;
+ static fd_set write_set;
+ static fd_set error_set;
+ struct timeval timeout;
+ int max_fd = -1;
+ bool did_print_rate_limit_log = false;
+
+#if AUTO_PRINT_BACKTRACE
+ struct sigaction sigact;
+#endif // if AUTO_PRINT_BACKTRACE
+
+ enum {
+ ARG_SPI_MODE = 1001,
+ ARG_SPI_SPEED = 1002,
+ ARG_VERBOSE = 1003,
+ ARG_SPI_CS_DELAY = 1004,
+ ARG_SPI_ALIGN_ALLOWANCE = 1005,
+ ARG_RAW = 1006,
+ ARG_MTU = 1007,
+ ARG_SPI_SMALL_PACKET = 1008,
+ };
+
+ static struct option options[] = {
+ { "stdio", no_argument, &sMode, MODE_STDIO },
+ { "pty", no_argument, &sMode, MODE_PTY },
+ { "gpio-int", required_argument, NULL, 'i' },
+ { "gpio-res", required_argument, NULL, 'r' },
+ { "verbose", optional_argument, NULL, ARG_VERBOSE },
+ { "version", no_argument, NULL, 'V' },
+ { "raw", no_argument, NULL, ARG_RAW },
+ { "mtu", required_argument, NULL, ARG_MTU },
+ { "help", no_argument, NULL, 'h' },
+ { "spi-mode", required_argument, NULL, ARG_SPI_MODE },
+ { "spi-speed", required_argument, NULL, ARG_SPI_SPEED },
+ { "spi-cs-delay",required_argument,NULL, ARG_SPI_CS_DELAY },
+ { "spi-align-allowance", required_argument, NULL, ARG_SPI_ALIGN_ALLOWANCE },
+ { "spi-small-packet", required_argument, NULL, ARG_SPI_SMALL_PACKET },
+ { NULL, 0, NULL, 0 },
+ };
+
+ strncpy(prog, argv[0], sizeof(prog) - 1);
+ prog[sizeof(prog) - 1] = 0;
+
+ if (argc < 2)
+ {
+ print_help();
+ exit(EXIT_FAILURE);
+ }
+
+
+ // ========================================================================
+ // INITIALIZATION
+
+ sPreviousHandlerForSIGINT = signal(SIGINT, &signal_SIGINT);
+ sPreviousHandlerForSIGTERM = signal(SIGTERM, &signal_SIGTERM);
+ signal(SIGHUP, &signal_SIGHUP);
+ signal(SIGUSR1, &signal_dumpstats);
+ signal(SIGUSR2, &signal_clearstats);
+
+#if AUTO_PRINT_BACKTRACE
+ sigact.sa_sigaction = &signal_critical;
+ sigact.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT;
+
+ sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);
+ sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);
+ sigaction(SIGILL, &sigact, (struct sigaction *)NULL);
+ sigaction(SIGABRT, &sigact, (struct sigaction *)NULL);
+#endif // if AUTO_PRINT_BACKTRACE
+
+ // ========================================================================
+ // ARGUMENT PARSING
+
+ openlog(basename(prog), LOG_PERROR | LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ setlogmask(LOG_UPTO(sVerbose));
+
+ while (1)
+ {
+ int c = getopt_long(argc, argv, "i:r:vVh?", options, NULL);
+ if (c == -1)
+ {
+ break;
+ }
+ else
+ {
+ switch (c)
+ {
+ case 'i':
+ if (!setup_int_gpio(optarg))
+ {
+ syslog(LOG_ERR, "Unable to setup INT GPIO \"%s\", %s", optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case ARG_SPI_ALIGN_ALLOWANCE:
+ errno = 0;
+ sSpiRxAlignAllowance = atoi(optarg);
+
+ if (errno != 0 || (sSpiRxAlignAllowance < 0))
+ {
+ syslog(LOG_ERR, "Invalid SPI RX Align Allowance \"%s\"", optarg);
+ exit(EXIT_FAILURE);
+ }
+
+ if (sSpiRxAlignAllowance > SPI_RX_ALIGN_ALLOWANCE_MAX)
+ {
+ syslog(LOG_WARNING, "Reducing SPI RX Align Allowance from %s to %d", optarg, SPI_RX_ALIGN_ALLOWANCE_MAX);
+ sSpiRxAlignAllowance = SPI_RX_ALIGN_ALLOWANCE_MAX;
+ }
+
+ break;
+
+ case ARG_SPI_MODE:
+ if (!update_spi_mode(atoi(optarg)))
+ {
+ syslog(LOG_ERR, "Unable to set SPI mode to \"%s\", %s", optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case ARG_SPI_SPEED:
+ if (!update_spi_speed(atoi(optarg)))
+ {
+ syslog(LOG_ERR, "Unable to set SPI speed to \"%s\", %s", optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case ARG_SPI_SMALL_PACKET:
+ sSpiSmallPacketSize = atoi(optarg);
+ if (sSpiSmallPacketSize > MAX_FRAME_SIZE - HEADER_LEN)
+ {
+ syslog(LOG_WARNING, "Reducing SPI small-packet size from %s to %d", optarg, MAX_FRAME_SIZE - HEADER_LEN);
+ sSpiSmallPacketSize = MAX_FRAME_SIZE - HEADER_LEN;
+ }
+ if (sSpiSmallPacketSize < 0)
+ {
+ syslog(LOG_ERR, "The argument to --spi-small-packet cannot be negative. (Given: \"%s\")", optarg);
+ exit(EXIT_FAILURE);
+ }
+ syslog(LOG_NOTICE, "SPI small-packet size set to %d bytes.", sSpiSmallPacketSize);
+ break;
+
+ case ARG_SPI_CS_DELAY:
+ sSpiCsDelay = atoi(optarg);
+ if (sSpiCsDelay < 0)
+ {
+ syslog(LOG_ERR, "Negative values (%d) for --spi-cs-delay are invalid.", sSpiCsDelay);
+ exit(EXIT_FAILURE);
+ }
+ syslog(LOG_NOTICE, "SPI CS Delay set to %d usec", sSpiCsDelay);
+ break;
+
+ case ARG_RAW:
+ sUseRawFrames = true;
+ syslog(LOG_NOTICE, "HDLC encoding/decoding disabled. Will use raw frames for input/output.");
+ break;
+
+ case ARG_MTU:
+ sMTU = atoi(optarg);
+ if (sMTU > MAX_FRAME_SIZE - HEADER_LEN)
+ {
+ syslog(LOG_ERR, "Specified MTU of %d is too large, maximum is %d bytes.", sMTU, MAX_FRAME_SIZE - HEADER_LEN);
+ exit(EXIT_FAILURE);
+ }
+ if (sMTU < 1)
+ {
+ syslog(LOG_ERR, "Specified MTU of %d is too small, minimum is 1 byte.", sMTU);
+ exit(EXIT_FAILURE);
+ }
+ syslog(LOG_NOTICE, "MTU set to %d bytes", sMTU);
+ break;
+
+ case 'r':
+ if (!setup_res_gpio(optarg))
+ {
+ syslog(LOG_ERR, "Unable to setup RES GPIO \"%s\", %s", optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'v':
+ case ARG_VERBOSE:
+ if (sVerbose < LOG_DEBUG)
+ {
+ if (optarg)
+ {
+ sVerbose += atoi(optarg);
+ }
+ else
+ {
+ sVerbose++;
+ }
+ setlogmask(setlogmask(0) | LOG_UPTO(sVerbose));
+ syslog(sVerbose, "Verbosity set to level %d", sVerbose);
+ }
+ break;
+
+ case 'V':
+ print_version();
+ exit(EXIT_SUCCESS);
+ break;
+
+ case 'h':
+ case '?':
+ print_help();
+ exit(EXIT_SUCCESS);
+ break;
+ }
+ }
+ }
+
+ syslog(LOG_NOTICE,"spi-hdlc-adapter " SPI_HDLC_VERSION " (" __TIME__ " " __DATE__ ")\n");
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc >= 1)
+ {
+ if (!setup_spi_dev(argv[0]))
+ {
+ char spi_path[64];
+
+ strncpy(spi_path, argv[0], sizeof(spi_path) - 1);
+ spi_path[sizeof(spi_path) - 1] = 0;
+ syslog(LOG_ERR, "%s: Unable to open SPI device \"%s\", %s", prog, spi_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ argc--;
+ argv++;
+ }
+
+ if (argc >= 1)
+ {
+ fprintf(stderr, "%s: Unexpected argument \"%s\"\n", prog, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (sSpiDevPath == NULL)
+ {
+ fprintf(stderr, "%s: Missing SPI device path\n", prog);
+ exit(EXIT_FAILURE);
+ }
+
+ if (sMode == MODE_STDIO)
+ {
+ sHdlcInputFd = dup(STDIN_FILENO);
+ sHdlcOutputFd = dup(STDOUT_FILENO);
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+
+ }
+ else if (sMode == MODE_PTY)
+ {
+#if HAVE_OPENPTY
+
+ static int pty_slave_fd = -1;
+ char pty_name[1024];
+ sRet = openpty(&sHdlcInputFd, &pty_slave_fd, pty_name, NULL, NULL);
+
+ if (sRet != 0)
+ {
+ perror("openpty");
+ goto bail;
+ }
+
+ sHdlcOutputFd = dup(sHdlcInputFd);
+
+ printf("%s\n", pty_name);
+
+ close(STDOUT_FILENO);
+
+#else // if HAVE_OPENPTY
+
+ syslog(LOG_ERR, "Not built with support for `--pty`.");
+ sRet = EXIT_FAILURE;
+ goto bail;
+
+#endif // else HAVE_OPENPTY
+
+ }
+ else
+ {
+ sRet = EXIT_FAILURE;
+ goto bail;
+ }
+
+ if ((sHdlcInputFd < 0) || (sHdlcOutputFd < 0))
+ {
+ sRet = EXIT_FAILURE;
+ goto bail;
+ }
+
+ // Set up sHdlcInputFd for non-blocking I/O
+ if (-1 == (i = fcntl(sHdlcInputFd, F_GETFL, 0)))
+ {
+ i = 0;
+ }
+ IGNORE_RETURN_VALUE(fcntl(sHdlcInputFd, F_SETFL, i | O_NONBLOCK));
+
+ // Since there are so few file descriptors in
+ // this program, we calculate `max_fd` once
+ // instead of trying to optimize its value
+ // at every iteration.
+ max_fd = sHdlcInputFd;
+
+ if (max_fd < sHdlcOutputFd)
+ {
+ max_fd = sHdlcOutputFd;
+ }
+
+ if (max_fd < sIntGpioValueFd)
+ {
+ max_fd = sIntGpioValueFd;
+ }
+
+ if (sIntGpioValueFd < 0)
+ {
+ syslog(LOG_WARNING, "Interrupt pin was not set, must poll SPI. Performance will suffer.");
+ }
+
+ trigger_reset();
+
+ // ========================================================================
+ // MAIN LOOP
+
+ while (sRet == 0)
+ {
+ int timeout_ms = MSEC_PER_SEC * 60 * 60 * 24; // 24 hours
+
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&error_set);
+
+ if (!sSpiTxIsReady)
+ {
+ FD_SET(sHdlcInputFd, &read_set);
+
+ }
+ else
+ {
+ // We have data to send to the slave.
+ timeout_ms = 0;
+ }
+
+ if (sSpiRxPayloadSize != 0)
+ {
+ // We have data that we are waiting to send out
+ // of the HDLC descriptor, so we need to wait
+ // for that to clear out before we can do anything
+ // else.
+ FD_SET(sHdlcOutputFd, &write_set);
+
+ }
+ else if (sIntGpioValueFd >= 0)
+ {
+ if (check_and_clear_interrupt())
+ {
+ // Interrupt pin is asserted,
+ // set the timeout to be 0.
+ timeout_ms = 0;
+
+ syslog(LOG_DEBUG, "Interrupt.");
+ }
+ else
+ {
+ // The interrupt pin was not asserted,
+ // so we wait for the interrupt pin to
+ // be asserted by adding it to the error
+ // set.
+ FD_SET(sIntGpioValueFd, &error_set);
+ }
+
+ }
+ else if (timeout_ms > SPI_POLL_PERIOD_MSEC)
+ {
+ // In this case we don't have an interrupt, so
+ // we revert to SPI polling.
+ timeout_ms = SPI_POLL_PERIOD_MSEC;
+ }
+
+ if (sDumpStats)
+ {
+ timeout_ms = 0;
+ }
+
+ if (sSpiTxRefusedCount)
+ {
+ // We are being rate-limited by the slave. This is
+ // fairly normal behavior. We poll because we
+ // won't get an interrupt unless the slave happens
+ // to be trying to send us something.
+ if (timeout_ms < SPI_POLL_PERIOD_MSEC)
+ {
+ timeout_ms = SPI_POLL_PERIOD_MSEC;
+ }
+
+ if ( sSpiTxIsReady
+ && !did_print_rate_limit_log
+ && (sSpiTxRefusedCount > 1)
+ ) {
+ // To avoid printing out this message over and over,
+ // we only print it out once the refused count is at
+ // two or higher when we actually have something to
+ // send the slave. And then, we only print it once.
+ syslog(LOG_INFO, "Slave is rate limiting transactions");
+
+ did_print_rate_limit_log = true;
+ }
+
+ if (sSpiTxRefusedCount == 30)
+ {
+ // Ua-oh. The slave hasn't given us a chance to send
+ // it anything for over thirty frames. If this ever
+ // happens, print out a warning to the logs.
+ syslog(LOG_WARNING, "Slave seems stuck.");
+ }
+
+ if (sSpiTxRefusedCount == 100)
+ {
+ // Double ua-oh. The slave hasn't given us a chance
+ // to send it anything for over a hundred frames.
+ // This almost certainly means that the slave has
+ // locked up or gotten into an unrecoverable state.
+ // It is not spi-hdlc-adapter's job to identify and
+ // reset misbehaving devices (that is handled at a
+ // higher level), but we go ahead and log the condition
+ // for debugging purposes.
+ syslog(LOG_ERR, "Slave seems REALLY stuck.");
+ }
+ }
+ else
+ {
+ did_print_rate_limit_log = false;
+ }
+
+ // Calculate the timeout value.
+ timeout.tv_sec = timeout_ms / MSEC_PER_SEC;
+ timeout.tv_usec = (timeout_ms % MSEC_PER_SEC) * USEC_PER_MSEC;
+
+ // Wait for something to happen.
+ IGNORE_RETURN_VALUE(select(max_fd + 1, &read_set, &write_set, &error_set, &timeout));
+
+ if (sDumpStats || sRet != 0)
+ {
+ sDumpStats = false;
+ syslog(LOG_NOTICE, "INFO: sSlaveResetCount=%llu", (unsigned long long)sSlaveResetCount);
+ syslog(LOG_NOTICE, "INFO: sSpiFrameCount=%llu", (unsigned long long)sSpiFrameCount);
+ syslog(LOG_NOTICE, "INFO: sSpiValidFrameCount=%llu", (unsigned long long)sSpiValidFrameCount);
+ syslog(LOG_NOTICE, "INFO: sSpiDuplexFrameCount=%llu", (unsigned long long)sSpiDuplexFrameCount);
+ syslog(LOG_NOTICE, "INFO: sSpiUnresponsiveFrameCount=%llu", (unsigned long long)sSpiUnresponsiveFrameCount);
+ syslog(LOG_NOTICE, "INFO: sSpiGarbageFrameCount=%llu", (unsigned long long)sSpiGarbageFrameCount);
+ syslog(LOG_NOTICE, "INFO: sHdlcTxFrameCount=%llu", (unsigned long long)sHdlcTxFrameCount);
+ syslog(LOG_NOTICE, "INFO: sHdlcTxFrameByteCount=%llu", (unsigned long long)sHdlcTxFrameByteCount);
+ syslog(LOG_NOTICE, "INFO: sHdlcRxFrameCount=%llu", (unsigned long long)sHdlcRxFrameCount);
+ syslog(LOG_NOTICE, "INFO: sHdlcRxFrameByteCount=%llu", (unsigned long long)sHdlcRxFrameByteCount);
+ syslog(LOG_NOTICE, "INFO: sHdlcRxBadCrcCount=%llu", (unsigned long long)sHdlcRxBadCrcCount);
+ }
+
+ // Handle serial input.
+ if (FD_ISSET(sHdlcInputFd, &read_set))
+ {
+ // Read in the data.
+ if ((sUseRawFrames ? pull_raw() : pull_hdlc()) < 0)
+ {
+ sRet = EXIT_FAILURE;
+ break;
+ }
+ }
+
+ // Handle serial output.
+ if (FD_ISSET(sHdlcOutputFd, &write_set))
+ {
+ // Write out the data.
+ if ((sUseRawFrames ? push_raw() : push_hdlc()) < 0)
+ {
+ sRet = EXIT_FAILURE;
+ break;
+ }
+
+ continue;
+ }
+
+ // Service the SPI port if we can receive
+ // a packet or we have a packet to be sent.
+ if ( (sSpiRxPayloadSize == 0)
+ && (sSpiTxIsReady || check_and_clear_interrupt())
+ ) {
+ // We guard this with the above check because we don't
+ // want to overwrite any previously received (but not
+ // yet pushed out) frames.
+ if (push_pull_spi() < 0)
+ {
+ sRet = EXIT_FAILURE;
+ }
+ }
+ }
+
+
+ // ========================================================================
+ // SHUTDOWN
+
+bail:
+ syslog(LOG_NOTICE, "Shutdown. (sRet = %d)", sRet);
+
+ if (sRet == EXIT_QUIT)
+ {
+ sRet = EXIT_SUCCESS;
+ }
+ else if (sRet == -1)
+ {
+ sRet = EXIT_FAILURE;
+ }
+
+ return sRet;
+}