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;
+}