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